pass sub_relations into canonical queries

This commit is contained in:
lcnr
2025-08-26 15:23:57 +02:00
parent 2cb04b960f
commit 28a0e77d13
14 changed files with 342 additions and 318 deletions

View File

@@ -67,6 +67,7 @@ pub struct Canonicalizer<'a, D: SolverDelegate<Interner = I>, I: Interner> {
variables: &'a mut Vec<I::GenericArg>,
var_kinds: Vec<CanonicalVarKind<I>>,
variable_lookup_table: HashMap<I::GenericArg, usize>,
sub_root_lookup_table: HashMap<ty::TyVid, usize>,
binder_index: ty::DebruijnIndex,
/// We only use the debruijn index during lookup. We don't need to
@@ -88,6 +89,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
variables,
variable_lookup_table: Default::default(),
sub_root_lookup_table: Default::default(),
var_kinds: Vec::new(),
binder_index: ty::INNERMOST,
@@ -132,6 +134,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
variables: &mut variables,
variable_lookup_table: Default::default(),
sub_root_lookup_table: Default::default(),
var_kinds: Vec::new(),
binder_index: ty::INNERMOST,
@@ -139,6 +142,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
};
let param_env = param_env.fold_with(&mut env_canonicalizer);
debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST);
debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty());
CanonicalParamEnvCacheEntry {
param_env,
variable_lookup_table: env_canonicalizer.variable_lookup_table,
@@ -164,6 +168,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
variables,
variable_lookup_table: Default::default(),
sub_root_lookup_table: Default::default(),
var_kinds: Vec::new(),
binder_index: ty::INNERMOST,
@@ -171,6 +176,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
};
let param_env = param_env.fold_with(&mut env_canonicalizer);
debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST);
debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty());
(param_env, env_canonicalizer.variable_lookup_table, env_canonicalizer.var_kinds)
}
}
@@ -199,6 +205,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
variables,
variable_lookup_table,
sub_root_lookup_table: Default::default(),
var_kinds,
binder_index: ty::INNERMOST,
@@ -265,6 +272,13 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
ty::BoundVar::from(idx)
}
fn get_or_insert_sub_root(&mut self, vid: ty::TyVid) -> ty::BoundVar {
let root_vid = self.delegate.sub_root_ty_var(vid);
let idx =
*self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len());
ty::BoundVar::from(idx)
}
fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVarKinds) {
let mut var_kinds = self.var_kinds;
// See the rustc-dev-guide section about how we deal with universes
@@ -312,16 +326,15 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
"ty vid should have been resolved fully before canonicalization"
);
match self.canonicalize_mode {
CanonicalizeMode::Input { .. } => {
CanonicalVarKind::Ty(ty::UniverseIndex::ROOT)
}
CanonicalizeMode::Response { .. } => {
CanonicalVarKind::Ty(self.delegate.universe_of_ty(vid).unwrap_or_else(
|| panic!("ty var should have been resolved: {t:?}"),
))
}
}
let sub_root = self.get_or_insert_sub_root(vid);
let ui = match self.canonicalize_mode {
CanonicalizeMode::Input { .. } => ty::UniverseIndex::ROOT,
CanonicalizeMode::Response { .. } => self
.delegate
.universe_of_ty(vid)
.unwrap_or_else(|| panic!("ty var should have been resolved: {t:?}")),
};
CanonicalVarKind::Ty { ui, sub_root }
}
ty::IntVar(vid) => {
debug_assert_eq!(

View File

@@ -57,12 +57,14 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
where
V: TypeFoldable<Self::Interner>;
fn instantiate_canonical_var_with_infer(
fn instantiate_canonical_var(
&self,
kind: ty::CanonicalVarKind<Self::Interner>,
span: <Self::Interner as Interner>::Span,
var_values: &[<Self::Interner as Interner>::GenericArg],
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
) -> <Self::Interner as Interner>::GenericArg;
fn add_item_bounds_for_hidden_type(
&self,
def_id: <Self::Interner as Interner>::DefId,

View File

@@ -16,7 +16,8 @@ use rustc_type_ir::data_structures::HashSet;
use rustc_type_ir::inherent::*;
use rustc_type_ir::relate::solver_relating::RelateExt;
use rustc_type_ir::{
self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable,
self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner,
TypeFoldable,
};
use tracing::{debug, instrument, trace};
@@ -336,7 +337,16 @@ where
{
match result_value.kind() {
ty::GenericArgKind::Type(t) => {
if let ty::Bound(debruijn, b) = t.kind() {
// We disable the instantiation guess for inference variables
// and only use it for placeholders. We need to handle the
// `sub_root` of type inference variables which would make this
// more involved. They are also a lot rarer than region variables.
if let ty::Bound(debruijn, b) = t.kind()
&& !matches!(
response.variables.get(b.var().as_usize()).unwrap(),
CanonicalVarKind::Ty { .. }
)
{
assert_eq!(debruijn, ty::INNERMOST);
opt_values[b.var()] = Some(*original_value);
}
@@ -356,38 +366,37 @@ where
}
}
let var_values = delegate.cx().mk_args_from_iter(
response.variables.iter().enumerate().map(|(index, var_kind)| {
if var_kind.universe() != ty::UniverseIndex::ROOT {
// A variable from inside a binder of the query. While ideally these shouldn't
// exist at all (see the FIXME at the start of this method), we have to deal with
// them for now.
delegate.instantiate_canonical_var_with_infer(var_kind, span, |idx| {
prev_universe + idx.index()
})
} else if var_kind.is_existential() {
// As an optimization we sometimes avoid creating a new inference variable here.
//
// All new inference variables we create start out in the current universe of the caller.
// This is conceptually wrong as these inference variables would be able to name
// more placeholders then they should be able to. However the inference variables have
// to "come from somewhere", so by equating them with the original values of the caller
// later on, we pull them down into their correct universe again.
if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] {
v
} else {
delegate
.instantiate_canonical_var_with_infer(var_kind, span, |_| prev_universe)
}
let mut var_values = Vec::with_capacity(response.variables.len());
for (index, kind) in response.variables.iter().enumerate() {
let value = if kind.universe() != ty::UniverseIndex::ROOT {
// A variable from inside a binder of the query. While ideally these shouldn't
// exist at all (see the FIXME at the start of this method), we have to deal with
// them for now.
delegate.instantiate_canonical_var(kind, span, &var_values, |idx| {
prev_universe + idx.index()
})
} else if kind.is_existential() {
// As an optimization we sometimes avoid creating a new inference variable here.
//
// All new inference variables we create start out in the current universe of the caller.
// This is conceptually wrong as these inference variables would be able to name
// more placeholders then they should be able to. However the inference variables have
// to "come from somewhere", so by equating them with the original values of the caller
// later on, we pull them down into their correct universe again.
if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] {
v
} else {
// For placeholders which were already part of the input, we simply map this
// universal bound variable back the placeholder of the input.
original_values[var_kind.expect_placeholder_index()]
delegate.instantiate_canonical_var(kind, span, &var_values, |_| prev_universe)
}
}),
);
} else {
// For placeholders which were already part of the input, we simply map this
// universal bound variable back the placeholder of the input.
original_values[kind.expect_placeholder_index()]
};
var_values.push(value)
}
CanonicalVarValues { var_values }
CanonicalVarValues { var_values: delegate.cx().mk_args(&var_values) }
}
/// Unify the `original_values` with the `var_values` returned by the canonical query..

View File

@@ -418,6 +418,11 @@ pub struct GoalEvaluation<I: Interner> {
pub has_changed: HasChanged,
/// If the [`Certainty`] was `Maybe`, then keep track of whether the goal has changed
/// before rerunning it.
///
/// We knowingly ignore the `sub_root` of our inference variables here. This means we
/// may not reevaluate a goal even though a change to the `sub_root` could cause a goal
/// to make progress. Tracking them adds additional complexity for an incredibly minor
/// type inference improvement. We could look into properly handling this in the future.
pub stalled_on: Option<GoalStalledOn<I>>,
}