Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 53 additions & 16 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,9 @@ pub(crate) struct DiagMetadata<'ast> {
/// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.
current_where_predicate: Option<&'ast WherePredicate>,

/// Whether we are visiting an associated type equality binding like `Trait<Assoc = &T>`.
in_assoc_ty_binding: bool,

current_type_path: Option<&'ast Ty>,

/// The current impl items (used to suggest).
Expand Down Expand Up @@ -1327,7 +1330,11 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc
}
match constraint.kind {
AssocItemConstraintKind::Equality { ref term } => match term {
Term::Ty(ty) => self.visit_ty(ty),
Term::Ty(ty) => {
let prev = replace(&mut self.diag_metadata.in_assoc_ty_binding, true);
self.visit_ty(ty);
self.diag_metadata.in_assoc_ty_binding = prev;
}
Term::Const(c) => {
self.resolve_anon_const(c, AnonConstKind::ConstArg(IsRepeatExpr::No))
}
Expand Down Expand Up @@ -1927,21 +1934,35 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
}
LifetimeRibKind::AnonymousReportError => {
let guar = if elided {
let suggestion = self.lifetime_ribs[i..].iter().rev().find_map(|rib| {
if let LifetimeRibKind::Generics {
span,
kind: LifetimeBinderKind::PolyTrait | LifetimeBinderKind::WhereBound,
..
} = rib.kind
{
Some(crate::diagnostics::ElidedAnonymousLifetimeReportErrorSuggestion {
lo: span.shrink_to_lo(),
hi: lifetime.ident.span.shrink_to_hi(),
})
} else {
None
}
});
let suggestion = if self.diag_metadata.in_assoc_ty_binding {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment explaining the rationale here? Why does this if make sense? What is the output we are trying to produce? Why are we looking for a LifetimeRibKind::Generics below?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// In an associated type binding like `I: IntoIterator<Item = &T>`,
// introducing the lifetime on the trait ref would produce
// `I: for<'a> IntoIterator<Item = &'a T>`. Prefer a named lifetime
// from an enclosing item instead, so the assoc-ty-binding-specific path
// below builds that suggestion.
None
} else {
self.lifetime_ribs[i..].iter().rev().find_map(|rib| {
// Look for a `Generics` rib that represents a trait or where-bound
// binder (`T: Trait<&U>` or `where T: Trait<&U>`), since that is
// where the generic E0637 diagnostic can insert `for<'a>`.
if let LifetimeRibKind::Generics {
span,
kind:
LifetimeBinderKind::PolyTrait
| LifetimeBinderKind::WhereBound,
..
} = rib.kind
{
Some(crate::diagnostics::ElidedAnonymousLifetimeReportErrorSuggestion {
lo: span.shrink_to_lo(),
hi: lifetime.ident.span.shrink_to_hi(),
})
} else {
None
}
})
};
// are we trying to use an anonymous lifetime
// on a non GAT associated trait type?
if !self.in_func_body
Expand Down Expand Up @@ -1993,6 +2014,22 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
self.point_at_impl_lifetimes(&mut err, i, lifetime.ident.span);
err.emit()
}
} else if self.diag_metadata.in_assoc_ty_binding {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, a comment -- I think it'd be great to have a code example and show what this is doing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

// For associated type bindings, e.g.
// `fn f<I: IntoIterator<Item = &T>>()`, introduce a named lifetime
// on an enclosing generics binder instead:
// `fn f<'a, I: IntoIterator<Item = &'a T>>()`.
let mut err = self.r.dcx().create_err(
crate::diagnostics::ElidedAnonymousLifetimeReportError {
span: lifetime.ident.span,
suggestion,
},
);
self.suggest_introducing_lifetime_for_assoc_ty_binding(
&mut err,
lifetime.ident.span,
);
err.emit()
} else {
self.r.dcx().emit_err(
crate::diagnostics::ElidedAnonymousLifetimeReportError {
Expand Down
46 changes: 45 additions & 1 deletion compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3762,6 +3762,50 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
String,
Vec<(Span, String)>,
) -> bool,
) {
self.suggest_introducing_lifetime_filtered(err, name, |_| true, suggest);
}

pub(crate) fn suggest_introducing_lifetime_for_assoc_ty_binding(
&self,
err: &mut Diag<'_>,
lifetime: Span,
) {
self.suggest_introducing_lifetime_filtered(
err,
None,
|kind| {
!matches!(
kind,
LifetimeBinderKind::FnPtrType
| LifetimeBinderKind::PolyTrait
| LifetimeBinderKind::WhereBound
)
},
|err, _higher_ranked, span, message, intro_sugg, _| {
err.multipart_suggestion(
message,
vec![(span, intro_sugg), (lifetime.shrink_to_hi(), "'a ".to_string())],
Applicability::MaybeIncorrect,
);
false
},
);
}

fn suggest_introducing_lifetime_filtered(
&self,
err: &mut Diag<'_>,
name: Option<Ident>,
mut consider: impl FnMut(LifetimeBinderKind) -> bool,
suggest: impl Fn(
&mut Diag<'_>,
bool,
Span,
Cow<'static, str>,
String,
Vec<(Span, String)>,
) -> bool,
) {
let mut suggest_note = true;
for rib in self.lifetime_ribs.iter().rev() {
Expand All @@ -3775,7 +3819,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
{
continue;
}
if let LifetimeBinderKind::ImplAssocType = kind {
if matches!(kind, LifetimeBinderKind::ImplAssocType) || !consider(kind) {
continue;
}

Expand Down
12 changes: 12 additions & 0 deletions tests/ui/error-codes/E0637.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,16 @@ where
{
}

// Regression test for https://github.com/rust-lang/rust/issues/156456.
fn associated_type_binding<I: IntoIterator<Item = &String>>() {}
//~^ ERROR: `&` without an explicit lifetime name cannot be used here [E0637]

trait T {
type Assoc;
}

// Regression test for https://github.com/rust-lang/rust/issues/122025.
fn foo<F>(t: F) where F: T<Assoc=&str> {}
//~^ ERROR: `&` without an explicit lifetime name cannot be used here [E0637]

fn main() {}
24 changes: 23 additions & 1 deletion tests/ui/error-codes/E0637.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,29 @@ help: consider introducing a higher-ranked lifetime here
LL | T: for<'a> Into<&'a u32>,
| +++++++ ++

error: aborting due to 3 previous errors
error[E0637]: `&` without an explicit lifetime name cannot be used here
--> $DIR/E0637.rs:18:51
|
LL | fn associated_type_binding<I: IntoIterator<Item = &String>>() {}
| ^ explicit lifetime name needed here
|
help: consider introducing a named lifetime parameter
|
LL | fn associated_type_binding<'a, I: IntoIterator<Item = &'a String>>() {}
| +++ ++

error[E0637]: `&` without an explicit lifetime name cannot be used here
--> $DIR/E0637.rs:26:34
|
LL | fn foo<F>(t: F) where F: T<Assoc=&str> {}
| ^ explicit lifetime name needed here
|
help: consider introducing a named lifetime parameter
|
LL | fn foo<'a, F>(t: F) where F: T<Assoc=&'a str> {}
| +++ ++

error: aborting due to 5 previous errors

Some errors have detailed explanations: E0106, E0637.
For more information about an error, try `rustc --explain E0106`.
Loading