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
46 changes: 38 additions & 8 deletions compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,14 +410,22 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
}

fn fold_region(&mut self, r: I::Region) -> I::Region {
// We canonicalize free regions from the input into placeholder regions so that
// region constraints created in nested contexts can be propagated back to the
// caller, instead of unifying them.
// See the following Zulip discussion for details:
// https://rust-lang.zulipchat.com/#narrow/channel/364551-t-types.2Ftrait-system-refactor/topic/A.20question.20on.20.23251/near/579240238
let kind = match r.kind() {
ty::ReBound(..) => return r,

// We don't canonicalize `ReStatic` in the `param_env` as we use it
// 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 +439,41 @@ 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 from the root universe, which is used for
// canonicalization of any free region from the input.
if placeholder.universe() != ty::UniverseIndex::ROOT
&& max_input_universe.can_name(placeholder.universe())
{
panic!("new placeholder in universe {max_input_universe:?}: {r:?}");
}
CanonicalVarKind::PlaceholderRegion(placeholder)
Expand All @@ -462,7 +487,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
10 changes: 10 additions & 0 deletions compiler/rustc_next_trait_solver/src/canonical/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,16 @@ where
} else {
// For placeholders which were already part of the input, we simply map this
// universal bound variable back the placeholder of the input.
//
// For `CanonicalVarKind::PlaceholderRegion`, this differs slightly: we
// canonicalize all free regions from the input into placeholders. This is
// unlike types or consts, where only input placeholders remain placeholders
// in the canonical form. However, we can still map these back to the original
// input regions, as we set their placeholder indices during canonicalization
// with there indices in the original values. We deliberately do so because
// we don't want to unify any free region into other region in nest contexts
// but want to propagate region constraints on them back to the caller.
// Therefore, we need to map them back to their original values.
Comment on lines +218 to +223
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.

Suggested change
// in the canonical form. However, we can still map these back to the original
// input regions, as we set their placeholder indices during canonicalization
// with there indices in the original values. We deliberately do so because
// we don't want to unify any free region into other region in nest contexts
// but want to propagate region constraints on them back to the caller.
// Therefore, we need to map them back to their original values.
// in the canonical form.
//
// We can still map these back to the original input regions, as we
// just instantiate the canonical variable with its corresponding
// `original_value`.
//
// For more information on why we canonicalize all input regions as
// placeholders, see the comment in `Canonicalizer::fold_region`.

the comment is already quite good, I mostly just started to rewrite it as I was trying to fix a typo. Alternatively just fix that typo and leave the comment otherwise as is.

"we don't want to unify any free region into other region in nest[ed] contexts"

original_values[kind.expect_placeholder_index()]
}
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: lifetime may not live long enough
--> $DIR/normalization-preserve-equality.rs:24:1
--> $DIR/normalization-preserve-equality.rs:27:1
|
LL | fn test_borrowck<'a, 'b>(_: (<Equal<'a, 'b> as Trait>::Ty, Equal<'a, 'b>)) {
| ^^^^^^^^^^^^^^^^^--^^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -11,7 +11,7 @@ LL | fn test_borrowck<'a, 'b>(_: (<Equal<'a, 'b> as Trait>::Ty, Equal<'a, 'b>))
= help: consider adding the following bound: `'a: 'b`

error: lifetime may not live long enough
--> $DIR/normalization-preserve-equality.rs:24:1
--> $DIR/normalization-preserve-equality.rs:27:1
|
LL | fn test_borrowck<'a, 'b>(_: (<Equal<'a, 'b> as Trait>::Ty, Equal<'a, 'b>)) {
| ^^^^^^^^^^^^^^^^^--^^--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
13 changes: 8 additions & 5 deletions tests/ui/implied-bounds/normalization-preserve-equality.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// Both revisions should pass. `borrowck` revision is a bug!
// All the revisions should pass. `borrowck_current` revision is a bug!
//
//@ revisions: wfcheck borrowck
//@ ignore-compare-mode-next-solver (explicit revisions)
//@ revisions: wfcheck borrowck_current borrowck_next
//@ [wfcheck] check-pass
//@ [borrowck] check-fail
//@ [borrowck] known-bug: #106569
//@ [borrowck_current] check-fail
//@ [borrowck_current] known-bug: #106569
//@ [borrowck_next] compile-flags: -Znext-solver
//@ [borrowck_next] check-pass

struct Equal<'a, 'b>(&'a &'b (), &'b &'a ()); // implies 'a == 'b

Expand All @@ -20,7 +23,7 @@ trait WfCheckTrait {}
#[cfg(wfcheck)]
impl<'a, 'b> WfCheckTrait for (<Equal<'a, 'b> as Trait>::Ty, Equal<'a, 'b>) {}

#[cfg(borrowck)]
#[cfg(any(borrowck_current, borrowck_next))]
fn test_borrowck<'a, 'b>(_: (<Equal<'a, 'b> as Trait>::Ty, Equal<'a, 'b>)) {
let _ = None::<Equal<'a, 'b>>;
}
Expand Down
Loading