Skip to content

Commit

Permalink
Rollup merge of #97720 - cjgillot:all-fresh, r=petrochenkov
Browse files Browse the repository at this point in the history
Always create elided lifetime parameters for functions

Anonymous and elided lifetimes in functions are sometimes (async fns) --and sometimes not (regular fns)-- desugared to implicit generic parameters.

This difference of treatment makes it some downstream analyses more complicated to handle.  This step is a pre-requisite to perform lifetime elision resolution on AST.

There is currently an inconsistency in the treatment of argument-position impl-trait for functions and async fns:
```rust
trait Foo<'a> {}
fn foo(t: impl Foo<'_>) {} //~ ERROR missing lifetime specifier
async fn async_foo(t: impl Foo<'_>) {} //~ OK
fn bar(t: impl Iterator<Item = &'_ u8>) {} //~ ERROR missing lifetime specifier
async fn async_bar(t: impl Iterator<Item = &'_ u8>) {} //~ OK
```

The current implementation reports "missing lifetime specifier" on `foo`, but **accepts it** in `async_foo`.
This PR **proposes to accept** the anonymous lifetime in both cases as an extra generic lifetime parameter.
This change would be insta-stable, so let's ping t-lang.
Anonymous lifetimes in GAT bindings keep being forbidden:
```rust
fn foo(t: impl Foo<Assoc<'_> = Bar<'_>>) {}
                         ^^        ^^
                       forbidden   ok
```
I started a discussion here: https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Anonymous.20lifetimes.20in.20universal.20impl-trait/near/284968606

r? ``@petrochenkov``
  • Loading branch information
Dylan-DPC authored Jul 14, 2022
2 parents 431c6f8 + 5a20834 commit f5e9cb5
Show file tree
Hide file tree
Showing 20 changed files with 163 additions and 56 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ declare_features! (
/// below (it has to be checked before expansion possibly makes
/// macros disappear).
(active, allow_internal_unstable, "1.0.0", None, None),
/// Allows using anonymous lifetimes in argument-position impl-trait.
(active, anonymous_lifetime_in_impl_trait, "1.63.0", None, None),
/// Allows identifying the `compiler_builtins` crate.
(active, compiler_builtins, "1.13.0", None, None),
/// Outputs useful `assert!` messages
Expand Down
20 changes: 9 additions & 11 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,10 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
// We don't need to deal with patterns in parameters, because
// they are not possible for foreign or bodiless functions.
self.with_lifetime_rib(
LifetimeRibKind::AnonymousPassThrough(fn_id, false),
LifetimeRibKind::AnonymousCreateParameter {
binder: fn_id,
report_in_path: false,
},
|this| walk_list!(this, visit_param, &sig.decl.inputs),
);
self.with_lifetime_rib(
Expand Down Expand Up @@ -792,18 +795,13 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
// generic parameters. This is especially useful for `async fn`, where
// these fresh generic parameters can be applied to the opaque `impl Trait`
// return type.
let rib = if async_node_id.is_some() {
// Only emit a hard error for `async fn`, since this kind of
// elision has always been allowed in regular `fn`s.
this.with_lifetime_rib(
LifetimeRibKind::AnonymousCreateParameter {
binder: fn_id,
report_in_path: true,
}
} else {
LifetimeRibKind::AnonymousPassThrough(fn_id, false)
};
this.with_lifetime_rib(
rib,
// Only emit a hard error for `async fn`, since this kind of
// elision has always been allowed in regular `fn`s.
report_in_path: async_node_id.is_some(),
},
// Add each argument to the rib.
|this| this.resolve_params(&declaration.inputs),
);
Expand Down
17 changes: 16 additions & 1 deletion compiler/rustc_resolve/src/late/lifetimes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1677,14 +1677,29 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
break None;
}

Scope::Binder { ref lifetimes, scope_type, s, .. } => {
Scope::Binder { ref lifetimes, scope_type, s, where_bound_origin, .. } => {
if let Some(&def) = lifetimes.get(&region_def_id) {
break Some(def.shifted(late_depth));
}
match scope_type {
BinderScopeType::Normal => late_depth += 1,
BinderScopeType::Concatenating => {}
}
// Fresh lifetimes in APIT used to be allowed in async fns and forbidden in
// regular fns.
if let Some(hir::PredicateOrigin::ImplTrait) = where_bound_origin
&& let hir::LifetimeName::Param(_, hir::ParamName::Fresh) = lifetime_ref.name
&& let hir::IsAsync::NotAsync = self.tcx.asyncness(lifetime_ref.hir_id.owner)
&& !self.tcx.features().anonymous_lifetime_in_impl_trait
{
rustc_session::parse::feature_err(
&self.tcx.sess.parse_sess,
sym::anonymous_lifetime_in_impl_trait,
lifetime_ref.span,
"anonymous lifetimes in `impl Trait` are unstable",
).emit();
return;
}
scope = s;
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ symbols! {
always,
and,
and_then,
anonymous_lifetime_in_impl_trait,
any,
append_const_msg,
arbitrary_enum_discriminant,
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui-fulldeps/internal-lints/rustc_pass_by_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ impl CustomStruct {

fn test_alias(
value: CustomAlias,
reference: &CustomAlias, //~ ERROR passing `CustomAlias<>` by reference
reference: &CustomAlias, //~ ERROR passing `CustomAlias<'_>` by reference
) {
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,11 @@ error: passing `CustomStruct` by reference
LL | reference: &CustomStruct,
| ^^^^^^^^^^^^^ help: try passing by value: `CustomStruct`

error: passing `CustomAlias<>` by reference
error: passing `CustomAlias<'_>` by reference
--> $DIR/rustc_pass_by_value.rs:96:20
|
LL | reference: &CustomAlias,
| ^^^^^^^^^^^^ help: try passing by value: `CustomAlias<>`
| ^^^^^^^^^^^^ help: try passing by value: `CustomAlias<'_>`

error: passing `WithParameters<T, 1>` by reference
--> $DIR/rustc_pass_by_value.rs:110:20
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/generic-associated-types/bugs/issue-87748.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ error[E0478]: lifetime bound not satisfied
LL | fn do_sth(_: u32) {}
| ^^^^^^^^^^^^^^^^^
|
note: lifetime parameter instantiated with the anonymous lifetime #2 defined here
note: lifetime parameter instantiated with the anonymous lifetime as defined here
--> $DIR/issue-87748.rs:18:5
|
LL | fn do_sth(_: u32) {}
| ^^^^^^^^^^^^^^^^^
note: but lifetime parameter must outlive the anonymous lifetime #1 defined here
note: but lifetime parameter must outlive the anonymous lifetime as defined here
--> $DIR/issue-87748.rs:18:5
|
LL | fn do_sth(_: u32) {}
Expand Down
7 changes: 5 additions & 2 deletions src/test/ui/generic-associated-types/issue-95305.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
// at some point in the future.

#![feature(generic_associated_types)]

#![feature(anonymous_lifetime_in_impl_trait)]
trait Foo {
type Item<'a>;
}

fn foo(x: &impl Foo<Item<'_> = u32>) { }
//~^ ERROR `'_` cannot be used here [E0637]

// Ok: the anonymous lifetime is bound to the function.
fn bar(x: &impl for<'a> Foo<Item<'a> = &'_ u32>) { }
//~^ ERROR missing lifetime specifier

// Ok: the anonymous lifetime is bound to the function.
fn baz(x: &impl for<'a> Foo<Item<'a> = &u32>) { }

fn main() {}
16 changes: 2 additions & 14 deletions src/test/ui/generic-associated-types/issue-95305.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,6 @@ error[E0637]: `'_` cannot be used here
LL | fn foo(x: &impl Foo<Item<'_> = u32>) { }
| ^^ `'_` is a reserved lifetime name

error[E0106]: missing lifetime specifier
--> $DIR/issue-95305.rs:14:41
|
LL | fn bar(x: &impl for<'a> Foo<Item<'a> = &'_ u32>) { }
| ^^ expected named lifetime parameter
|
help: consider using the `'a` lifetime
|
LL | fn bar(x: &impl for<'a> Foo<Item<'a> = &'a u32>) { }
| ~~

error: aborting due to 2 previous errors
error: aborting due to previous error

Some errors have detailed explanations: E0106, E0637.
For more information about an error, try `rustc --explain E0106`.
For more information about this error, try `rustc --explain E0637`.
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-37884.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LL | fn next(&'a mut self) -> Option<Self::Item>
|
= note: expected fn pointer `fn(&mut RepeatMut<'a, T>) -> Option<_>`
found fn pointer `fn(&'a mut RepeatMut<'a, T>) -> Option<_>`
note: the anonymous lifetime #1 defined here...
note: the anonymous lifetime as defined here...
--> $DIR/issue-37884.rs:6:5
|
LL | fn next(&'a mut self) -> Option<Self::Item>
Expand Down
8 changes: 4 additions & 4 deletions src/test/ui/nll/ty-outlives/impl-trait-captures.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appea
--> $DIR/impl-trait-captures.rs:11:5
|
LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> {
| -- hidden type `&ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrAnon(0)) T` captures the anonymous lifetime defined here
| -- hidden type `&ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[1afc]::foo::'_), '_)) T` captures the anonymous lifetime defined here
LL | x
| ^
|
help: to declare that the `impl Trait` captures `ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrAnon(0))`, you can add an explicit `ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrAnon(0))` lifetime bound
help: to declare that the `impl Trait` captures `ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[1afc]::foo::'_), '_))`, you can add an explicit `ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[1afc]::foo::'_), '_))` lifetime bound
|
LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> + ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrAnon(0)) {
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
LL | fn foo<'a, T>(x: &T) -> impl Foo<'a> + ReFree(DefId(0:8 ~ impl_trait_captures[1afc]::foo), BrNamed(DefId(0:13 ~ impl_trait_captures[1afc]::foo::'_), '_)) {
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

error: aborting due to previous error

Expand Down
21 changes: 21 additions & 0 deletions src/test/ui/suggestions/impl-trait-missing-lifetime-gated.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// edition:2021
// gate-test-anonymous_lifetime_in_impl_trait
// Verify the behaviour of `feature(anonymous_lifetime_in_impl_trait)`.

fn f(_: impl Iterator<Item = &'_ ()>) {}
//~^ ERROR anonymous lifetimes in `impl Trait` are unstable

fn g(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() }
//~^ ERROR anonymous lifetimes in `impl Trait` are unstable
//~| ERROR missing lifetime specifier

// Anonymous lifetimes in async fn are already allowed.
// This is understood as `fn foo<'_1>(_: impl Iterator<Item = &'_1 ()>) {}`.
async fn h(_: impl Iterator<Item = &'_ ()>) {}

// Anonymous lifetimes in async fn are already allowed.
// But that lifetime does not participate in resolution.
async fn i(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() }
//~^ ERROR missing lifetime specifier

fn main() {}
44 changes: 44 additions & 0 deletions src/test/ui/suggestions/impl-trait-missing-lifetime-gated.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
error[E0658]: anonymous lifetimes in `impl Trait` are unstable
--> $DIR/impl-trait-missing-lifetime-gated.rs:5:31
|
LL | fn f(_: impl Iterator<Item = &'_ ()>) {}
| ^^
|
= help: add `#![feature(anonymous_lifetime_in_impl_trait)]` to the crate attributes to enable

error[E0106]: missing lifetime specifier
--> $DIR/impl-trait-missing-lifetime-gated.rs:8:50
|
LL | fn g(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() }
| ^^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
help: consider using the `'static` lifetime
|
LL | fn g(x: impl Iterator<Item = &'_ ()>) -> Option<&'static ()> { x.next() }
| ~~~~~~~

error[E0658]: anonymous lifetimes in `impl Trait` are unstable
--> $DIR/impl-trait-missing-lifetime-gated.rs:8:31
|
LL | fn g(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() }
| ^^
|
= help: add `#![feature(anonymous_lifetime_in_impl_trait)]` to the crate attributes to enable

error[E0106]: missing lifetime specifier
--> $DIR/impl-trait-missing-lifetime-gated.rs:18:56
|
LL | async fn i(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() }
| ^^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
help: consider using the `'static` lifetime
|
LL | async fn i(x: impl Iterator<Item = &'_ ()>) -> Option<&'static ()> { x.next() }
| ~~~~~~~

error: aborting due to 4 previous errors

Some errors have detailed explanations: E0106, E0658.
For more information about an error, try `rustc --explain E0106`.
19 changes: 18 additions & 1 deletion src/test/ui/suggestions/impl-trait-missing-lifetime.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,19 @@
fn f(_: impl Iterator<Item = &'_ ()>) {} //~ ERROR missing lifetime specifier
// edition:2021

#![feature(anonymous_lifetime_in_impl_trait)]

// This is understood as `fn foo<'_1>(_: impl Iterator<Item = &'_1 ()>) {}`.
fn f(_: impl Iterator<Item = &'_ ()>) {}

// But that lifetime does not participate in resolution.
fn g(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() }
//~^ ERROR missing lifetime specifier

// This is understood as `fn foo<'_1>(_: impl Iterator<Item = &'_1 ()>) {}`.
async fn h(_: impl Iterator<Item = &'_ ()>) {}

// But that lifetime does not participate in resolution.
async fn i(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() }
//~^ ERROR missing lifetime specifier

fn main() {}
27 changes: 20 additions & 7 deletions src/test/ui/suggestions/impl-trait-missing-lifetime.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
error[E0106]: missing lifetime specifier
--> $DIR/impl-trait-missing-lifetime.rs:1:31
--> $DIR/impl-trait-missing-lifetime.rs:9:50
|
LL | fn f(_: impl Iterator<Item = &'_ ()>) {}
| ^^ expected named lifetime parameter
LL | fn g(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() }
| ^^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
help: consider using the `'static` lifetime
|
LL | fn f<'a>(_: impl Iterator<Item = &'a ()>) {}
| ++++ ~~
LL | fn g(x: impl Iterator<Item = &'_ ()>) -> Option<&'static ()> { x.next() }
| ~~~~~~~

error: aborting due to previous error
error[E0106]: missing lifetime specifier
--> $DIR/impl-trait-missing-lifetime.rs:16:56
|
LL | async fn i(x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()> { x.next() }
| ^^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments
help: consider using the `'static` lifetime
|
LL | async fn i(x: impl Iterator<Item = &'_ ()>) -> Option<&'static ()> { x.next() }
| ~~~~~~~

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0106`.
6 changes: 3 additions & 3 deletions src/test/ui/wf/wf-in-foreign-fn-decls-issue-80468.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ note: because this has an unmet lifetime requirement
|
LL | pub struct Wrapper<T: Trait>(T);
| ^^^^^ introduces a `'static` lifetime requirement
note: the anonymous lifetime #1 defined here...
--> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:5
note: the anonymous lifetime as defined here...
--> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:16:29
|
LL | pub fn repro(_: Wrapper<Ref>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^
note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl`
--> $DIR/wf-in-foreign-fn-decls-issue-80468.rs:13:1
|
Expand Down
4 changes: 2 additions & 2 deletions src/tools/clippy/clippy_lints/src/inherent_to_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::{get_trait_def_id, paths, return_ty, trait_ref_of_method};
use if_chain::if_chain;
use rustc_hir::{ImplItem, ImplItemKind};
use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
Expand Down Expand Up @@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString {
let decl = &signature.decl;
if decl.implicit_self.has_implicit_self();
if decl.inputs.len() == 1;
if impl_item.generics.params.is_empty();
if impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }));

// Check if return type is String
if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::String);
Expand Down
10 changes: 7 additions & 3 deletions src/tools/clippy/clippy_lints/src/lifetimes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use rustc_hir::intravisit::{
use rustc_hir::FnRetTy::Return;
use rustc_hir::{
BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem,
ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin,
TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, LifetimeParamKind, ParamName, PolyTraitRef,
PredicateOrigin, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter as middle_nested_filter;
Expand Down Expand Up @@ -338,7 +338,10 @@ fn could_use_elision<'tcx>(
fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
let mut allowed_lts = FxHashSet::default();
for par in named_generics.iter() {
if let GenericParamKind::Lifetime { .. } = par.kind {
if let GenericParamKind::Lifetime {
kind: LifetimeParamKind::Explicit,
} = par.kind
{
allowed_lts.insert(RefLt::Named(par.name.ident().name));
}
}
Expand Down Expand Up @@ -379,6 +382,7 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
self.lts.push(RefLt::Static);
} else if let LifetimeName::Param(_, ParamName::Fresh) = lt.name {
// Fresh lifetimes generated should be ignored.
self.lts.push(RefLt::Unnamed);
} else if lt.is_elided() {
self.lts.push(RefLt::Unnamed);
} else {
Expand Down
3 changes: 2 additions & 1 deletion src/tools/clippy/clippy_lints/src/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,12 +495,13 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio
if let FnRetTy::Return(ty) = sig.decl.output
&& let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty)
{
let out_region = cx.tcx.named_region(out.hir_id);
let args: Option<Vec<_>> = sig
.decl
.inputs
.iter()
.filter_map(get_rptr_lm)
.filter(|&(lt, _, _)| lt.name == out.name)
.filter(|&(lt, _, _)| cx.tcx.named_region(lt.hir_id) == out_region)
.map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span))
.collect();
if let Some(args) = args
Expand Down
Loading

0 comments on commit f5e9cb5

Please sign in to comment.