Rollup merge of #145041 - lcnr:borrowck-limitations-error, r=BoxyUwU

rework GAT borrowck limitation error

The old one depends on the `ConstraintCategory` of the constraint which meant we did not emit this note if we had to prove the higher ranked trait bound due to e.g. normalization.

This made it annoying brittle and caused MIR borrowck errors to be order dependent, fixes the issue in https://github.com/rust-lang/rust/pull/140737#discussion_r2259592651.

r? types cc ```@amandasystems```
This commit is contained in:
Stuart Cook 2025-08-19 14:18:22 +10:00 committed by GitHub
commit f3f1847e40
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 133 additions and 58 deletions

View file

@ -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

View file

@ -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;
@ -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()
&& 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;
}
}
}
}
}

View file

@ -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'
@ -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
@ -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;

View file

@ -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) {}
| ^^^^
error: aborting due to 2 previous errors

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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> = ()>,

View file

@ -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

View file

@ -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>

View file

@ -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>,

View file

@ -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
@ -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

View file

@ -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

View file

@ -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) {}

View file

@ -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) {}

View file

@ -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,
@ -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,

View file

@ -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) {}

View file

@ -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
@ -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

View file

@ -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

View file

@ -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

View file

@ -12,11 +12,11 @@ LL | fn test2<'a>() {
LL | outlives_forall::<Value<'a>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
|
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
--> $DIR/type-test-universe.rs:6:16
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/type-test-universe.rs:6:5
|
LL | for<'u> T: 'u,
| ^^
| ^^^^^^^^^^^^^
error: aborting due to 2 previous errors

View file

@ -67,11 +67,11 @@ LL | unsafe { extend_hrtb(src) }
| `src` escapes the function body here
| argument requires that `'a` must outlive `'static`
|
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
--> $DIR/reject_lifetime_extension.rs:85:25
note: due to a current limitation of the type system, this implies a `'static` lifetime
--> $DIR/reject_lifetime_extension.rs:85:9
|
LL | for<'b> &'b u8: TransmuteFrom<&'a u8>,
| ^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 8 previous errors