Skip to content

Canonicalize input ty/ct infer/placeholder in the root universe #142732

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
148 changes: 29 additions & 119 deletions compiler/rustc_next_trait_solver/src/canonicalizer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::cmp::Ordering;

use rustc_type_ir::data_structures::{HashMap, ensure_sufficient_stack};
use rustc_type_ir::inherent::*;
use rustc_type_ir::solve::{Goal, QueryInput};
Expand Down Expand Up @@ -266,11 +264,11 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
// See the rustc-dev-guide section about how we deal with universes
// during canonicalization in the new solver.
match self.canonicalize_mode {
// We try to deduplicate as many query calls as possible and hide
// all information which should not matter for the solver.
//
// For this we compress universes as much as possible.
CanonicalizeMode::Input { .. } => {}
// All placeholders and vars are canonicalized in the root universe.
CanonicalizeMode::Input { .. } => {
let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
(ty::UniverseIndex::ROOT, var_kinds)
}
// When canonicalizing a response we map a universes already entered
// by the caller to the root universe and only return useful universe
// information for placeholders and inference variables created inside
Expand All @@ -288,113 +286,10 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
.map(|kind| kind.universe())
.max()
.unwrap_or(ty::UniverseIndex::ROOT);

let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
return (max_universe, var_kinds);
}
}

// Given a `var_kinds` with existentials `En` and universals `Un` in
// universes `n`, this algorithm compresses them in place so that:
//
// - the new universe indices are as small as possible
// - we create a new universe if we would otherwise
// 1. put existentials from a different universe into the same one
// 2. put a placeholder in the same universe as an existential which cannot name it
//
// Let's walk through an example:
// - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 0
// - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 0, next_orig_uv: 1
// - var_kinds: [E0, U1, E5, U2, E2, E6, U6], curr_compressed_uv: 1, next_orig_uv: 2
// - var_kinds: [E0, U1, E5, U1, E1, E6, U6], curr_compressed_uv: 1, next_orig_uv: 5
// - var_kinds: [E0, U1, E2, U1, E1, E6, U6], curr_compressed_uv: 2, next_orig_uv: 6
// - var_kinds: [E0, U1, E1, U1, E1, E3, U3], curr_compressed_uv: 2, next_orig_uv: -
//
// This algorithm runs in `O(mn)` where `n` is the number of different universes and
// `m` the number of variables. This should be fine as both are expected to be small.
let mut curr_compressed_uv = ty::UniverseIndex::ROOT;
let mut existential_in_new_uv = None;
let mut next_orig_uv = Some(ty::UniverseIndex::ROOT);
while let Some(orig_uv) = next_orig_uv.take() {
let mut update_uv = |var: &mut CanonicalVarKind<I>, orig_uv, is_existential| {
let uv = var.universe();
match uv.cmp(&orig_uv) {
Ordering::Less => (), // Already updated
Ordering::Equal => {
if is_existential {
if existential_in_new_uv.is_some_and(|uv| uv < orig_uv) {
// Condition 1.
//
// We already put an existential from a outer universe
// into the current compressed universe, so we need to
// create a new one.
curr_compressed_uv = curr_compressed_uv.next_universe();
}

// `curr_compressed_uv` will now contain an existential from
// `orig_uv`. Trying to canonicalizing an existential from
// a higher universe has to therefore use a new compressed
// universe.
existential_in_new_uv = Some(orig_uv);
} else if existential_in_new_uv.is_some() {
// Condition 2.
//
// `var` is a placeholder from a universe which is not nameable
// by an existential which we already put into the compressed
// universe `curr_compressed_uv`. We therefore have to create a
// new universe for `var`.
curr_compressed_uv = curr_compressed_uv.next_universe();
existential_in_new_uv = None;
}

*var = var.with_updated_universe(curr_compressed_uv);
}
Ordering::Greater => {
// We can ignore this variable in this iteration. We only look at
// universes which actually occur in the input for performance.
//
// For this we set `next_orig_uv` to the next smallest, not yet compressed,
// universe of the input.
if next_orig_uv.is_none_or(|curr_next_uv| uv.cannot_name(curr_next_uv)) {
next_orig_uv = Some(uv);
}
}
}
};

// For each universe which occurs in the input, we first iterate over all
// placeholders and then over all inference variables.
//
// Whenever we compress the universe of a placeholder, no existential with
// an already compressed universe can name that placeholder.
for is_existential in [false, true] {
for var in var_kinds.iter_mut() {
// We simply put all regions from the input into the highest
// compressed universe, so we only deal with them at the end.
if !var.is_region() {
if is_existential == var.is_existential() {
update_uv(var, orig_uv, is_existential)
}
}
}
(max_universe, var_kinds)
}
}

// We put all regions into a separate universe.
let mut first_region = true;
for var in var_kinds.iter_mut() {
if var.is_region() {
if first_region {
first_region = false;
curr_compressed_uv = curr_compressed_uv.next_universe();
}
debug_assert!(var.is_existential());
*var = var.with_updated_universe(curr_compressed_uv);
}
}

let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds);
(curr_compressed_uv, var_kinds)
}

fn cached_fold_ty(&mut self, t: I::Ty) -> I::Ty {
Expand All @@ -407,11 +302,18 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
"ty vid should have been resolved fully before canonicalization"
);

CanonicalVarKind::Ty(CanonicalTyVarKind::General(
self.delegate
.universe_of_ty(vid)
.unwrap_or_else(|| panic!("ty var should have been resolved: {t:?}")),
))
match self.canonicalize_mode {
CanonicalizeMode::Input { .. } => CanonicalVarKind::Ty(
CanonicalTyVarKind::General(ty::UniverseIndex::ROOT),
),
CanonicalizeMode::Response { .. } => {
CanonicalVarKind::Ty(CanonicalTyVarKind::General(
self.delegate.universe_of_ty(vid).unwrap_or_else(|| {
panic!("ty var should have been resolved: {t:?}")
}),
))
}
}
}
ty::IntVar(vid) => {
debug_assert_eq!(
Expand All @@ -435,7 +337,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
},
ty::Placeholder(placeholder) => match self.canonicalize_mode {
CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
PlaceholderLike::new_anon(placeholder.universe(), self.variables.len().into()),
PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
),
CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder),
},
Expand Down Expand Up @@ -588,13 +490,21 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
c,
"const vid should have been resolved fully before canonicalization"
);
CanonicalVarKind::Const(self.delegate.universe_of_ct(vid).unwrap())

match self.canonicalize_mode {
CanonicalizeMode::Input { .. } => {
CanonicalVarKind::Const(ty::UniverseIndex::ROOT)
}
CanonicalizeMode::Response { .. } => {
CanonicalVarKind::Const(self.delegate.universe_of_ct(vid).unwrap())
}
}
}
ty::InferConst::Fresh(_) => todo!(),
},
ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode {
CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
PlaceholderLike::new_anon(placeholder.universe(), self.variables.len().into()),
PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
),
CanonicalizeMode::Response { .. } => {
CanonicalVarKind::PlaceholderConst(placeholder)
Expand Down
Loading