Skip to content
Draft
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
30 changes: 17 additions & 13 deletions compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,8 @@ impl CanonicalizeMode for CanonicalizeUserTypeAnnotation {
| ty::ReErased
| ty::ReStatic
| ty::ReError(_) => r,
ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r),
ty::ReVar(_) => canonicalizer
.canonical_var_for_region(CanonicalVarKind::Region(ty::UniverseIndex::ROOT), r),
ty::RePlaceholder(..) | ty::ReBound(..) => {
// We only expect region names that the user can type.
bug!("unexpected region in query response: `{:?}`", r)
Expand Down Expand Up @@ -707,23 +708,26 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
}

/// Shorthand helper that creates a canonical region variable for
/// `r` (always in the root universe). The reason that we always
/// put these variables into the root universe is because this
/// method is used during **query construction:** in that case, we
/// are taking all the regions and just putting them into the most
/// generic context we can. This may generate solutions that don't
/// fit (e.g., that equate some region variable with a placeholder
/// it can't name) on the caller side, but that's ok, the caller
/// can figure that out. In the meantime, it maximizes our
/// `r` (always as a placeholder in the root universe). The reason
/// that we always put these variables into the root universe as a
/// placeholder is because this method is used during
/// **query construction:** in that case, we are taking all the
/// free regions and just putting them into the most generic context
/// we can. This makes any region constraints between them, including
/// region equalities, to be preserved and propagated to the caller
/// instead of unifying them. In the meantime, it maximizes our
/// caching.
///
/// (This works because unification never fails -- and hence trait
/// selection is never affected -- due to a universe mismatch.)
fn canonical_var_for_region_in_root_universe(
&mut self,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
self.canonical_var_for_region(CanonicalVarKind::Region(ty::UniverseIndex::ROOT), r)
self.canonical_var_for_region(
CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new_anon(
ty::UniverseIndex::ROOT,
self.var_kinds.len().into(),
)),
r,
)
}

/// Creates a canonical variable (with the given `info`)
Expand Down
42 changes: 34 additions & 8 deletions compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,10 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
// when checking whether a `ParamEnv` candidate is global.
ty::ReStatic => match self.canonicalize_mode {
CanonicalizeMode::Input(CanonicalizeInputKind::Predicate) => {
CanonicalVarKind::Region(ty::UniverseIndex::ROOT)
CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new_anon(
ty::UniverseIndex::ROOT,
self.variables.len().into(),
))
}
CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv)
| CanonicalizeMode::Response { .. } => return r,
Expand All @@ -431,24 +434,42 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
// `ReErased`. We may be able to short-circuit registering region
// obligations if we encounter a `ReErased` on one side, for example.
ty::ReErased | ty::ReError(_) => match self.canonicalize_mode {
CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
CanonicalizeMode::Input(_) => {
CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new_anon(
ty::UniverseIndex::ROOT,
self.variables.len().into(),
))
}
CanonicalizeMode::Response { .. } => return r,
},

ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode {
CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
CanonicalizeMode::Input(_) => {
CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new_anon(
ty::UniverseIndex::ROOT,
self.variables.len().into(),
))
}
CanonicalizeMode::Response { .. } => {
panic!("unexpected region in response: {r:?}")
}
},

ty::RePlaceholder(placeholder) => match self.canonicalize_mode {
// We canonicalize placeholder regions as existentials in query inputs.
CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
CanonicalizeMode::Input(_) => {
CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new_anon(
ty::UniverseIndex::ROOT,
self.variables.len().into(),
))
}
CanonicalizeMode::Response { max_input_universe } => {
// If we have a placeholder region inside of a query, it must be from
// a new universe.
if max_input_universe.can_name(placeholder.universe()) {
// a new universe, unless it's anon from the root universe, which is
// used for canonicalization of any free region from the input.
if !(placeholder.universe() == ty::UniverseIndex::ROOT
&& placeholder.bound.kind == ty::BoundRegionKind::Anon)
&& max_input_universe.can_name(placeholder.universe())
{
panic!("new placeholder in universe {max_input_universe:?}: {r:?}");
}
CanonicalVarKind::PlaceholderRegion(placeholder)
Expand All @@ -462,7 +483,12 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
"region vid should have been resolved fully before canonicalization"
);
match self.canonicalize_mode {
CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
CanonicalizeMode::Input(_) => {
CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new_anon(
ty::UniverseIndex::ROOT,
self.variables.len().into(),
))
}
CanonicalizeMode::Response { .. } => {
CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap())
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
error: implementation of `Foo` is not general enough
error[E0308]: mismatched types
--> $DIR/higher-ranked-auto-trait-10.rs:32:5
|
LL | Box::new(async move { get_foo(x).await })
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`...
= note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2`
= note: expected enum `Result<&_, _>`
found enum `Result<&_, _>`

error: implementation of `Foo` is not general enough
error: implementation of `Send` is not general enough
--> $DIR/higher-ranked-auto-trait-10.rs:32:5
|
LL | Box::new(async move { get_foo(x).await })
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Send` is not general enough
|
= note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`...
= note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
= note: `Send` would have to be implemented for the type `&str`
= note: ...but `Send` is actually implemented for the type `&'0 str`, for some specific lifetime `'0`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`.
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
error: implementation of `Foo` is not general enough
error[E0308]: mismatched types
--> $DIR/higher-ranked-auto-trait-10.rs:32:5
|
LL | Box::new(async move { get_foo(x).await })
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`...
= note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2`
= note: expected enum `Result<&_, _>`
found enum `Result<&_, _>`

error: implementation of `Foo` is not general enough
error: implementation of `Send` is not general enough
--> $DIR/higher-ranked-auto-trait-10.rs:32:5
|
LL | Box::new(async move { get_foo(x).await })
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Send` is not general enough
|
= note: `Foo<'1>` would have to be implemented for the type `&'0 str`, for any two lifetimes `'0` and `'1`...
= note: ...but `Foo<'2>` is actually implemented for the type `&'2 str`, for some specific lifetime `'2`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
= note: `Send` would have to be implemented for the type `&str`
= note: ...but `Send` is actually implemented for the type `&'0 str`, for some specific lifetime `'0`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`.
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ error: implementation of `Send` is not general enough
LL | Box::pin(async move { <T as Foo<'a>>::foo().await })
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Send` is not general enough
|
= note: `Send` would have to be implemented for the type `<T as Foo<'0>>::Future`, for any lifetime `'0`...
= note: ...but `Send` is actually implemented for the type `<T as Foo<'1>>::Future`, for some specific lifetime `'1`
= note: `<T as Foo<'0>>::Future` must implement `Send`, for any lifetime `'0`...
= note: ...but `Send` is actually implemented for the type `<T as Foo<'_>>::Future`

error: aborting due to 2 previous errors

Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ error: implementation of `Send` is not general enough
LL | Box::pin(async move { <T as Foo<'a>>::foo().await })
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Send` is not general enough
|
= note: `Send` would have to be implemented for the type `<T as Foo<'0>>::Future`, for any lifetime `'0`...
= note: ...but `Send` is actually implemented for the type `<T as Foo<'1>>::Future`, for some specific lifetime `'1`
= note: `<T as Foo<'0>>::Future` must implement `Send`, for any lifetime `'0`...
= note: ...but `Send` is actually implemented for the type `<T as Foo<'_>>::Future`

error: aborting due to 2 previous errors

Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ LL | assert_send(my_send_async_method(struct_with_lifetime, data));
= note: ...but `Getter<'2>` is actually implemented for the type `GetterImpl<'2, ConstructableImpl<'_>>`, for some specific lifetime `'2`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: implementation of `Callable` is not general enough
error: implementation of `Send` is not general enough
--> $DIR/higher-ranked-auto-trait-13.rs:65:5
|
LL | assert_send(my_send_async_method(struct_with_lifetime, data));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Callable` is not general enough
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Send` is not general enough
|
= note: `Callable<'_>` would have to be implemented for the type `ConstructableImpl<'0>`, for any lifetime `'0`...
= note: ...but `Callable<'1>` is actually implemented for the type `ConstructableImpl<'1>`, for some specific lifetime `'1`
= note: `Send` would have to be implemented for the type `&Vec<u8>`
= note: ...but `Send` is actually implemented for the type `&'0 Vec<u8>`, for some specific lifetime `'0`

error: implementation of `Getter` is not general enough
--> $DIR/higher-ranked-auto-trait-13.rs:65:5
Expand Down Expand Up @@ -54,7 +54,6 @@ LL | assert_send(my_send_async_method(struct_with_lifetime, data));
|
= note: `Callable<'_>` would have to be implemented for the type `ConstructableImpl<'0>`, for any lifetime `'0`...
= note: ...but `Callable<'1>` is actually implemented for the type `ConstructableImpl<'1>`, for some specific lifetime `'1`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: aborting due to 6 previous errors

Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ LL | | call_me.call().await;
LL | | });
| |______^ implementation of `Send` is not general enough
|
= note: `Send` would have to be implemented for the type `&'0 str`, for any lifetime `'0`...
= note: ...but `Send` is actually implemented for the type `&'1 str`, for some specific lifetime `'1`
= note: `Send` would have to be implemented for the type `&Wrap<CallMeImpl<&str>>`
= note: ...but `Send` is actually implemented for the type `&'0 Wrap<CallMeImpl<&str>>`, for some specific lifetime `'0`

error: aborting due to 1 previous error

6 changes: 6 additions & 0 deletions tests/ui/borrowck/closure-upvar-named-lifetime.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ LL | f(value);
| -------- argument requires that `map` is borrowed for `'a`
LL | }
| - `map` dropped here while still borrowed
|
note: requirement that the value outlives `'a` introduced here
--> $SRC_DIR/core/src/ops/function.rs:LL:COL

error[E0716]: temporary value dropped while borrowed
--> $DIR/closure-upvar-named-lifetime.rs:34:21
Expand All @@ -87,6 +90,9 @@ LL | let value = map.borrow_mut().entry("foo".to_string());
...
LL | f(value);
| -------- argument requires that borrow lasts for `'a`
|
note: requirement that the value outlives `'a` introduced here
--> $SRC_DIR/core/src/ops/function.rs:LL:COL

error[E0700]: hidden type for `impl Fn(RefCell<HashMap<String, String>>)` captures lifetime that does not appear in bounds
--> $DIR/closure-upvar-named-lifetime.rs:32:5
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/coroutine/resume-arg-late-bound.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ error: implementation of `Coroutine` is not general enough
LL | test(gen);
| ^^^^^^^^^ implementation of `Coroutine` is not general enough
|
= note: `{coroutine@$DIR/resume-arg-late-bound.rs:11:28: 11:44}` must implement `Coroutine<&'1 mut bool>`, for any lifetime `'1`...
= note: ...but it actually implements `Coroutine<&'2 mut bool>`, for some specific lifetime `'2`
= note: `{coroutine@$DIR/resume-arg-late-bound.rs:11:28: 11:44}` must implement `Coroutine<&'1 mut bool>`, for any two lifetimes `'0` and `'1`...
= note: ...but it actually implements `Coroutine<&mut bool>`

error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error: higher-ranked lifetime error
error: lifetime bound not satisfied
--> $DIR/must-prove-where-clauses-on-norm.rs:23:61
|
LL | let func: for<'a, 'b> fn((), &'b str) -> &'static str = foo::<()>;
| ^^^^^^^^^
|
= note: could not normalize `for<'b> fn(<() as Trait>::Assoc<'_, 'b>, &'b str) -> &str`
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)

error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn foo<'a, 'b, T: Trait>(_: <T as Trait>::Assoc<'a, 'b>, x: &'b str) -> &'a str

fn main() {
let func: for<'a, 'b> fn((), &'b str) -> &'static str = foo::<()>;
//[current]~^ ERROR higher-ranked lifetime error
//[current]~^ ERROR lifetime bound not satisfied
//[next]~^^ ERROR mismatched types
let x: &'static str = func((), &String::from("temporary"));
println!("{x}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ LL | foo_hrtb_bar_not(&mut t);
| ^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Bar` is not general enough
|
= note: `T` must implement `Bar<&'0 isize>`, for any lifetime `'0`...
= note: ...but it actually implements `Bar<&'1 isize>`, for some specific lifetime `'1`
= note: ...but it actually implements `Bar<&isize>`

warning: function cannot return without recursing
--> $DIR/hrtb-perfect-forwarding.rs:48:1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ error: implementation of `FnOnce` is not general enough
LL | foo(bar, "string", |s| s.len() == 5);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
= note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2`
= note: closure with signature `for<'a> fn(&'a &'0 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any two lifetimes `'0` and `'1`...
= note: ...but it actually implements `FnOnce<(&&str,)>`
Copy link
Copy Markdown
Member Author

@ShoyuVanilla ShoyuVanilla Apr 19, 2026

Choose a reason for hiding this comment

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

We have lots of those ...but it actually implements X for some specific lifetime '?x -> ...but it actually implements X for some specific lifetime diagnostic drifts due to the canonicalized region being placeholders rather than region vars.
I think this is somewhat regressive. Would it make sense to fix them somehow?

View changes since the review


error: implementation of `FnOnce` is not general enough
--> $DIR/issue-71955.rs:45:5
|
LL | foo(bar, "string", |s| s.len() == 5);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
= note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2`
= note: closure with signature `for<'a> fn(&'a &'0 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any two lifetimes `'0` and `'1`...
= note: ...but it actually implements `FnOnce<(&&str,)>`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: implementation of `FnOnce` is not general enough
Expand All @@ -23,17 +23,17 @@ error: implementation of `FnOnce` is not general enough
LL | foo(baz, "string", |s| s.0.len() == 5);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
= note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2`
= note: closure with signature `for<'a> fn(&'a Wrapper<'0>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any two lifetimes `'0` and `'1`...
= note: ...but it actually implements `FnOnce<(&Wrapper<'_>,)>`

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-71955.rs:48:5
|
LL | foo(baz, "string", |s| s.0.len() == 5);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
= note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2`
= note: closure with signature `for<'a> fn(&'a Wrapper<'0>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any two lifetimes `'0` and `'1`...
= note: ...but it actually implements `FnOnce<(&Wrapper<'_>,)>`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: aborting due to 4 previous errors
Expand Down
Loading
Loading