Skip to content

rework GAT borrowck limitation error #145041

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 19, 2025
Merged
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
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ borrowck_lifetime_constraints_error =
lifetime may not live long enough

borrowck_limitations_implies_static =
due to current limitations in the borrow checker, this implies a `'static` lifetime
due to a current limitation of the type system, this implies a `'static` lifetime

borrowck_move_closure_suggestion =
consider adding 'move' keyword before the nested closure
Expand Down
61 changes: 52 additions & 9 deletions compiler/rustc_borrowck/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use rustc_abi::{FieldIdx, VariantIdx};
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify};
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::{self as hir, CoroutineKind, LangItem};
use rustc_hir::{
self as hir, CoroutineKind, GenericBound, LangItem, WhereBoundPredicate, WherePredicateKind,
};
use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::{BoundRegionConversionTime, NllRegionVariableOrigin};
use rustc_infer::traits::SelectionError;
Expand Down Expand Up @@ -658,25 +660,66 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {

/// Add a note to region errors and borrow explanations when higher-ranked regions in predicates
/// implicitly introduce an "outlives `'static`" constraint.
///
/// This is very similar to `fn suggest_static_lifetime_for_gat_from_hrtb` which handles this
/// note for failed type tests instead of outlives errors.
fn add_placeholder_from_predicate_note<G: EmissionGuarantee>(
&self,
err: &mut Diag<'_, G>,
diag: &mut Diag<'_, G>,
path: &[OutlivesConstraint<'tcx>],
) {
let predicate_span = path.iter().find_map(|constraint| {
let tcx = self.infcx.tcx;
let Some((gat_hir_id, generics)) = path.iter().find_map(|constraint| {
let outlived = constraint.sub;
if let Some(origin) = self.regioncx.definitions.get(outlived)
&& let NllRegionVariableOrigin::Placeholder(_) = origin.origin
&& let ConstraintCategory::Predicate(span) = constraint.category
&& let NllRegionVariableOrigin::Placeholder(placeholder) = origin.origin
&& let Some(id) = placeholder.bound.kind.get_id()
&& let Some(placeholder_id) = id.as_local()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This lint should ideally also work for placeholders from foreign crates by looking at the bound vars and span from the parent predicates_of.

Changing that is annoying and I did not do it in this PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

This logic will probably help a lot with various order-dependent things I introduce in my PRs, since selection of the correct placeholder is a lot less sensitive now! Thanks!

&& let gat_hir_id = tcx.local_def_id_to_hir_id(placeholder_id)
&& let Some(generics_impl) =
tcx.parent_hir_node(tcx.parent_hir_id(gat_hir_id)).generics()
{
Some(span)
Some((gat_hir_id, generics_impl))
} else {
None
}
});
}) else {
return;
};

if let Some(span) = predicate_span {
err.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
// Look for the where-bound which introduces the placeholder.
// As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>`
// and `T: for<'a> Trait`<'a>.
for pred in generics.predicates {
let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
bound_generic_params,
bounds,
..
}) = pred.kind
else {
continue;
};
if bound_generic_params
.iter()
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
.is_some()
{
diag.span_note(pred.span, fluent::borrowck_limitations_implies_static);
return;
}
for bound in bounds.iter() {
if let GenericBound::Trait(bound) = bound {
if bound
.bound_generic_params
.iter()
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
.is_some()
{
diag.span_note(bound.span, fluent::borrowck_limitations_implies_static);
return;
}
}
}
}
}

Expand Down
24 changes: 20 additions & 4 deletions compiler/rustc_borrowck/src/diagnostics/region_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
diag: &mut Diag<'_>,
lower_bound: RegionVid,
) {
let mut suggestions = vec![];
let tcx = self.infcx.tcx;

// find generic associated types in the given region 'lower_bound'
Expand All @@ -237,9 +236,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
.collect::<Vec<_>>();
debug!(?gat_id_and_generics);

// find higher-ranked trait bounds bounded to the generic associated types
// Look for the where-bound which introduces the placeholder.
// As we're using the HIR, we need to handle both `for<'a> T: Trait<'a>`
// and `T: for<'a> Trait`<'a>.
let mut hrtb_bounds = vec![];
gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| {
gat_id_and_generics.iter().flatten().for_each(|&(gat_hir_id, generics)| {
for pred in generics.predicates {
let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) =
pred.kind
Expand All @@ -248,17 +249,32 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
};
if bound_generic_params
.iter()
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id)
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
.is_some()
{
for bound in *bounds {
hrtb_bounds.push(bound);
}
} else {
for bound in *bounds {
if let Trait(trait_bound) = bound {
if trait_bound
.bound_generic_params
.iter()
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
.is_some()
{
hrtb_bounds.push(bound);
return;
}
}
}
}
}
});
debug!(?hrtb_bounds);

let mut suggestions = vec![];
hrtb_bounds.iter().for_each(|bound| {
let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }) = bound else {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,6 @@ LL | force_send(async_load(&not_static));
...
LL | }
| - `not_static` dropped here while still borrowed
|
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
--> $DIR/implementation-not-general-enough-ice-133252.rs:16:18
|
LL | fn force_send<T: Send>(_: T) {}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The fact that this note worked and was also applicable here feels somewhat incidental to me. It's caused by incorrectly handling the existential regions in opaque types. This is an instantiation of #110338 for opaque types instead of coroutines

| ^^^^

error: aborting due to 2 previous errors

Expand Down
6 changes: 3 additions & 3 deletions tests/ui/generic-associated-types/bugs/hrtb-implied-1.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ LL | print_items::<WindowsMut<'_>>(windows);
LL | }
| - temporary value is freed at the end of this statement
|
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
--> $DIR/hrtb-implied-1.rs:26:26
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/hrtb-implied-1.rs:26:5
|
LL | for<'a> I::Item<'a>: Debug,
| ^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error

Expand Down
6 changes: 5 additions & 1 deletion tests/ui/generic-associated-types/bugs/hrtb-implied-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ LL | let _next = iter2.next();
= note: requirement occurs because of a mutable reference to `Eat<&mut I, F>`
= note: mutable references are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
= note: due to current limitations in the borrow checker, this implies a `'static` lifetime
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/hrtb-implied-2.rs:31:8
|
LL | F: FnMut(I::Item<'_>),
| ^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error

Expand Down
6 changes: 3 additions & 3 deletions tests/ui/generic-associated-types/bugs/hrtb-implied-3.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ LL | trivial_bound(iter);
| `iter` escapes the function body here
| argument requires that `'1` must outlive `'static`
|
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
--> $DIR/hrtb-implied-3.rs:14:26
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/hrtb-implied-3.rs:14:5
|
LL | for<'a> I::Item<'a>: Sized,
| ^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ LL | | let _x = x;
LL | | };
| |_____^
|
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/collectivity-regression.rs:11:16
|
LL | for<'a> T: Get<Value<'a> = ()>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ error: `Self` does not live long enough
|
LL | <B as FromLendingIterator<A>>::from_iter(self)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/lending_iterator.rs:4:21
|
LL | fn from_iter<T: for<'x> LendingIterator<Item<'x> = A>>(iter: T) -> Self;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ LL | fn give_some<'a>() {
LL | want_hrtb::<&'a u32>()
| ^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
|
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/hrtb-just-for-static.rs:9:15
|
LL | where T : for<'a> Foo<&'a isize>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ LL | fn foo_hrtb_bar_not<'b, T>(mut t: T)
LL | foo_hrtb_bar_not(&mut t);
| ^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'static`
|
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/hrtb-perfect-forwarding.rs:37:8
|
LL | T: for<'a> Foo<&'a isize> + Bar<&'b isize>,
Expand Down
12 changes: 12 additions & 0 deletions tests/ui/implied-bounds/normalization-placeholder-leak.fail.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ LL | fn test_lifetime<'lt, T: Trait>(_: Foo<&'lt u8>) {}
| | |
| | lifetime `'lt` defined here
| requires that `'lt` must outlive `'static`
|
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/normalization-placeholder-leak.rs:19:5
|
LL | for<'x> T::Ty<'x>: Sized;
| ^^^^^^^^^^^^^^^^^^^^^^^^

error: lifetime may not live long enough
--> $DIR/normalization-placeholder-leak.rs:38:5
Expand All @@ -39,6 +45,12 @@ LL | fn test_alias<'lt, T: AnotherTrait>(_: Foo<T::Ty2::<'lt>>) {}
| | |
| | lifetime `'lt` defined here
| requires that `'lt` must outlive `'static`
|
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/normalization-placeholder-leak.rs:19:5
|
LL | for<'x> T::Ty<'x>: Sized;
| ^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 6 previous errors

Expand Down
6 changes: 3 additions & 3 deletions tests/ui/issues/issue-26217.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ LL | fn bar<'a>() {
LL | foo::<&'a i32>();
| ^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
|
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
--> $DIR/issue-26217.rs:1:30
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/issue-26217.rs:1:19
|
LL | fn foo<T>() where for<'a> T: 'a {}
| ^^
| ^^^^^^^^^^^^^

error: aborting due to 1 previous error

4 changes: 2 additions & 2 deletions tests/ui/lifetimes/issue-105507.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ impl<T> ProjectedMyTrait for T
where
T: Project,
for<'a> T::Projected<'a>: MyTrait,
//~^ NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
//~| NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
//~^ NOTE due to a current limitation of the type system, this implies a `'static` lifetime
//~| NOTE due to a current limitation of the type system, this implies a `'static` lifetime
{}

fn require_trait<T: MyTrait>(_: T) {}
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/lifetimes/issue-105507.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ impl<T> ProjectedMyTrait for T
where
T: Project,
for<'a> T::Projected<'a>: MyTrait,
//~^ NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
//~| NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
//~^ NOTE due to a current limitation of the type system, this implies a `'static` lifetime
//~| NOTE due to a current limitation of the type system, this implies a `'static` lifetime
{}

fn require_trait<T: MyTrait>(_: T) {}
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/lifetimes/issue-105507.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ error: `T` does not live long enough
LL | require_trait(wrap);
| ^^^^^^^^^^^^^^^^^^^
|
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/issue-105507.rs:27:35
|
LL | for<'a> T::Projected<'a>: MyTrait,
Expand All @@ -20,7 +20,7 @@ error: `U` does not live long enough
LL | require_trait(wrap1);
| ^^^^^^^^^^^^^^^^^^^^
|
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/issue-105507.rs:27:35
|
LL | for<'a> T::Projected<'a>: MyTrait,
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/mismatched_types/closure-arg-type-mismatch.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ LL | baz(f);
= note: requirement occurs because of a mutable pointer to `&u32`
= note: mutable pointers are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/closure-arg-type-mismatch.rs:8:11
|
LL | fn baz<F: Fn(*mut &u32)>(_: F) {}
Expand Down
12 changes: 6 additions & 6 deletions tests/ui/nll/local-outlives-static-via-hrtb.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ LL | assert_static_via_hrtb_with_assoc_type(&&local);
LL | }
| - `local` dropped here while still borrowed
|
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
--> $DIR/local-outlives-static-via-hrtb.rs:15:53
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/local-outlives-static-via-hrtb.rs:15:42
|
LL | fn assert_static_via_hrtb<G>(_: G) where for<'a> G: Outlives<'a> {}
| ^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^

error[E0597]: `local` does not live long enough
--> $DIR/local-outlives-static-via-hrtb.rs:25:45
Expand All @@ -32,11 +32,11 @@ LL | assert_static_via_hrtb_with_assoc_type(&&local);
LL | }
| - `local` dropped here while still borrowed
|
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
--> $DIR/local-outlives-static-via-hrtb.rs:19:20
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/local-outlives-static-via-hrtb.rs:19:5
|
LL | for<'a> &'a T: Reference<AssociatedType = &'a ()>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ LL | let b = |_| &a;
LL | }
| - `a` dropped here while still borrowed
|
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
--> $DIR/location-insensitive-scopes-issue-117146.rs:20:22
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/location-insensitive-scopes-issue-117146.rs:20:11
|
LL | fn bad<F: Fn(&()) -> &()>(_: F) {}
| ^^^
| ^^^^^^^^^^^^^^

error: implementation of `Fn` is not general enough
--> $DIR/location-insensitive-scopes-issue-117146.rs:13:5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ LL | let b = |_| &a;
LL | }
| - `a` dropped here while still borrowed
|
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
--> $DIR/location-insensitive-scopes-issue-117146.rs:20:22
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/location-insensitive-scopes-issue-117146.rs:20:11
|
LL | fn bad<F: Fn(&()) -> &()>(_: F) {}
| ^^^
| ^^^^^^^^^^^^^^

error: implementation of `Fn` is not general enough
--> $DIR/location-insensitive-scopes-issue-117146.rs:13:5
Expand Down
Loading
Loading