From 5a956727170262d8d2a618d72e54a21f9aa891f1 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 20 Sep 2024 13:55:26 +0000 Subject: [PATCH 1/4] very cool, 10/10 --- .../src/canonicalizer.rs | 244 ++++++++++-------- .../src/solve/eval_ctxt/mod.rs | 6 + 2 files changed, 141 insertions(+), 109 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 196ddeb244302..31e67b3d3a042 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -1,5 +1,6 @@ use std::cmp::Ordering; +use rustc_type_ir::data_structures::HashMap; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::visit::TypeVisitableExt; @@ -44,8 +45,12 @@ pub struct Canonicalizer<'a, D: SolverDelegate, I: Interner> { canonicalize_mode: CanonicalizeMode, variables: &'a mut Vec, + variable_lookup_table: HashMap, + primitive_var_infos: Vec>, binder_index: ty::DebruijnIndex, + + cache: HashMap<(ty::DebruijnIndex, I::Ty), I::Ty>, } impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { @@ -60,12 +65,14 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { canonicalize_mode, variables, + variable_lookup_table: Default::default(), primitive_var_infos: Vec::new(), binder_index: ty::INNERMOST, + + cache: Default::default(), }; let value = value.fold_with(&mut canonicalizer); - // FIXME: Restore these assertions. Should we uplift type flags? assert!(!value.has_infer(), "unexpected infer in {value:?}"); assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}"); @@ -75,6 +82,37 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { Canonical { defining_opaque_types, max_universe, variables, value } } + fn get_or_insert_bound_var( + &mut self, + arg: impl Into, + canonical_var_info: CanonicalVarInfo, + ) -> ty::BoundVar { + // FIXME: 16 is made up and arbitrary. We should look at some + // perf data here. + let arg = arg.into(); + let idx = if self.variables.len() > 16 { + if self.variable_lookup_table.is_empty() { + self.variable_lookup_table.extend(self.variables.iter().copied().zip(0..)); + } + + *self.variable_lookup_table.entry(arg).or_insert_with(|| { + let var = self.variables.len(); + self.variables.push(arg); + self.primitive_var_infos.push(canonical_var_info); + var + }) + } else { + self.variables.iter().position(|&v| v == arg).unwrap_or_else(|| { + let var = self.variables.len(); + self.variables.push(arg); + self.primitive_var_infos.push(canonical_var_info); + var + }) + }; + + ty::BoundVar::from(idx) + } + fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVars) { let mut var_infos = self.primitive_var_infos; // See the rustc-dev-guide section about how we deal with universes @@ -124,8 +162,8 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { // - var_infos: [E0, U1, E2, U1, E1, E6, U6], curr_compressed_uv: 2, next_orig_uv: 6 // - var_infos: [E0, U1, E1, U1, E1, E3, U3], curr_compressed_uv: 2, next_orig_uv: - // - // This algorithm runs in `O(n²)` where `n` is the number of different universe - // indices in the input. This should be fine as `n` is expected to be small. + // 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); @@ -185,14 +223,16 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { for var in var_infos.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() && is_existential == var.is_existential() { - update_uv(var, orig_uv, is_existential) + if !var.is_region() { + if is_existential == var.is_existential() { + update_uv(var, orig_uv, is_existential) + } } } } } - // We uniquify regions and always put them into their own universe + // We put all regions into a separate universe. let mut first_region = true; for var in var_infos.iter_mut() { if var.is_region() { @@ -208,93 +248,8 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { let var_infos = self.delegate.cx().mk_canonical_var_infos(&var_infos); (curr_compressed_uv, var_infos) } -} - -impl, I: Interner> TypeFolder for Canonicalizer<'_, D, I> { - fn cx(&self) -> I { - self.delegate.cx() - } - - fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder - where - T: TypeFoldable, - { - self.binder_index.shift_in(1); - let t = t.super_fold_with(self); - self.binder_index.shift_out(1); - t - } - - fn fold_region(&mut self, r: I::Region) -> I::Region { - let kind = match r.kind() { - ty::ReBound(..) => return r, - - // We may encounter `ReStatic` in item signatures or the hidden type - // of an opaque. `ReErased` should only be encountered in the hidden - // type of an opaque for regions that are ignored for the purposes of - // captures. - // - // FIXME: We should investigate the perf implications of not uniquifying - // `ReErased`. We may be able to short-circuit registering region - // obligations if we encounter a `ReErased` on one side, for example. - ty::ReStatic | ty::ReErased | ty::ReError(_) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), - CanonicalizeMode::Response { .. } => return r, - }, - - ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), - 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::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()) { - panic!("new placeholder in universe {max_input_universe:?}: {r:?}"); - } - CanonicalVarKind::PlaceholderRegion(placeholder) - } - }, - - ty::ReVar(vid) => { - assert_eq!( - self.delegate.opportunistic_resolve_lt_var(vid), - r, - "region vid should have been resolved fully before canonicalization" - ); - match self.canonicalize_mode { - CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), - CanonicalizeMode::Response { .. } => { - CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap()) - } - } - } - }; - - let existing_bound_var = match self.canonicalize_mode { - CanonicalizeMode::Input => None, - CanonicalizeMode::Response { .. } => { - self.variables.iter().position(|&v| v == r.into()).map(ty::BoundVar::from) - } - }; - - let var = existing_bound_var.unwrap_or_else(|| { - let var = ty::BoundVar::from(self.variables.len()); - self.variables.push(r.into()); - self.primitive_var_infos.push(CanonicalVarInfo { kind }); - var - }); - Region::new_anon_bound(self.cx(), self.binder_index, var) - } - - fn fold_ty(&mut self, t: I::Ty) -> I::Ty { + fn cached_fold_ty(&mut self, t: I::Ty) -> I::Ty { let kind = match t.kind() { ty::Infer(i) => match i { ty::TyVar(vid) => { @@ -368,20 +323,98 @@ impl, I: Interner> TypeFolder for Canonicaliz | ty::Tuple(_) | ty::Alias(_, _) | ty::Bound(_, _) - | ty::Error(_) => return t.super_fold_with(self), + | ty::Error(_) => { + return t.super_fold_with(self); + } }; - let var = ty::BoundVar::from( - self.variables.iter().position(|&v| v == t.into()).unwrap_or_else(|| { - let var = self.variables.len(); - self.variables.push(t.into()); - self.primitive_var_infos.push(CanonicalVarInfo { kind }); - var - }), - ); + let var = self.get_or_insert_bound_var(t, CanonicalVarInfo { kind }); Ty::new_anon_bound(self.cx(), self.binder_index, var) } +} + +impl, I: Interner> TypeFolder for Canonicalizer<'_, D, I> { + fn cx(&self) -> I { + self.delegate.cx() + } + + fn fold_binder(&mut self, t: ty::Binder) -> ty::Binder + where + T: TypeFoldable, + { + self.binder_index.shift_in(1); + let t = t.super_fold_with(self); + self.binder_index.shift_out(1); + t + } + + fn fold_region(&mut self, r: I::Region) -> I::Region { + let kind = match r.kind() { + ty::ReBound(..) => return r, + + // We may encounter `ReStatic` in item signatures or the hidden type + // of an opaque. `ReErased` should only be encountered in the hidden + // type of an opaque for regions that are ignored for the purposes of + // captures. + // + // FIXME: We should investigate the perf implications of not uniquifying + // `ReErased`. We may be able to short-circuit registering region + // obligations if we encounter a `ReErased` on one side, for example. + ty::ReStatic | ty::ReErased | ty::ReError(_) => match self.canonicalize_mode { + CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Response { .. } => return r, + }, + + ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode { + CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + 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::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()) { + panic!("new placeholder in universe {max_input_universe:?}: {r:?}"); + } + CanonicalVarKind::PlaceholderRegion(placeholder) + } + }, + + ty::ReVar(vid) => { + assert_eq!( + self.delegate.opportunistic_resolve_lt_var(vid), + r, + "region vid should have been resolved fully before canonicalization" + ); + match self.canonicalize_mode { + CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Response { .. } => { + CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap()) + } + } + } + }; + + let var = self.get_or_insert_bound_var(r, CanonicalVarInfo { kind }); + + Region::new_anon_bound(self.cx(), self.binder_index, var) + } + + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { + if let Some(&ty) = self.cache.get(&(self.binder_index, t)) { + ty + } else { + let res = self.cached_fold_ty(t); + assert!(self.cache.insert((self.binder_index, t), res).is_none()); + res + } + } fn fold_const(&mut self, c: I::Const) -> I::Const { let kind = match c.kind() { @@ -419,14 +452,7 @@ impl, I: Interner> TypeFolder for Canonicaliz | ty::ConstKind::Expr(_) => return c.super_fold_with(self), }; - let var = ty::BoundVar::from( - self.variables.iter().position(|&v| v == c.into()).unwrap_or_else(|| { - let var = self.variables.len(); - self.variables.push(c.into()); - self.primitive_var_infos.push(CanonicalVarInfo { kind }); - var - }), - ); + let var = self.get_or_insert_bound_var(c, CanonicalVarInfo { kind }); Const::new_anon_bound(self.cx(), self.binder_index, var) } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 12ad0724b5cb1..d792862427a93 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -362,6 +362,12 @@ where goal: Goal, ) -> Result<(NestedNormalizationGoals, bool, Certainty), NoSolution> { let (orig_values, canonical_goal) = self.canonicalize_goal(goal); + + let num_orig_values = orig_values.iter().filter(|v| v.as_region().is_none()).count(); + if num_orig_values >= 128 { + println!("bail due to too many orig_values: {num_orig_values}"); + return Ok((Default::default(), false, Certainty::overflow(false))); + } let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); let canonical_response = EvalCtxt::evaluate_canonical_goal( From 0dac974719ee608f3ff7b3b01ca524d51d04c391 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 25 Sep 2024 11:13:23 +0200 Subject: [PATCH 2/4] cache the world --- .../src/infer/relate/type_relating.rs | 11 ++++- compiler/rustc_infer/src/infer/resolve.rs | 15 +++++-- compiler/rustc_middle/src/ty/fold.rs | 17 +++++-- .../rustc_next_trait_solver/src/resolve.rs | 15 +++++-- .../src/solve/eval_ctxt/mod.rs | 45 ++++++++++++++----- .../overflow}/coherence-alias-hang.rs | 12 ++++- 6 files changed, 92 insertions(+), 23 deletions(-) rename tests/ui/traits/{ => next-solver/overflow}/coherence-alias-hang.rs (56%) diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index a402a8009ff8d..805856244bc7d 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -1,3 +1,4 @@ +use rustc_data_structures::fx::FxHashSet; use rustc_middle::traits::solve::Goal; use rustc_middle::ty::relate::{ Relate, RelateResult, TypeRelation, relate_args_invariantly, relate_args_with_variances, @@ -16,6 +17,7 @@ pub struct TypeRelating<'combine, 'a, 'tcx> { fields: &'combine mut CombineFields<'a, 'tcx>, structurally_relate_aliases: StructurallyRelateAliases, ambient_variance: ty::Variance, + cache: FxHashSet<(ty::Variance, Ty<'tcx>, Ty<'tcx>)>, } impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> { @@ -24,7 +26,7 @@ impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> { structurally_relate_aliases: StructurallyRelateAliases, ambient_variance: ty::Variance, ) -> TypeRelating<'combine, 'infcx, 'tcx> { - TypeRelating { fields: f, structurally_relate_aliases, ambient_variance } + TypeRelating { fields: f, structurally_relate_aliases, ambient_variance, cache: Default::default() } } } @@ -74,9 +76,14 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { return Ok(a); } + let infcx = self.fields.infcx; let a = infcx.shallow_resolve(a); let b = infcx.shallow_resolve(b); + + if self.cache.contains(&(self.ambient_variance, a, b)) { + return Ok(a); + } match (a.kind(), b.kind()) { (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => { @@ -160,6 +167,8 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { } } + assert!(self.cache.insert((self.ambient_variance, a, b))); + Ok(a) } diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 34625ffb77879..0dda05e611353 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -1,3 +1,4 @@ +use rustc_data_structures::fx::FxHashMap; use rustc_middle::bug; use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::TypeVisitableExt; @@ -15,12 +16,13 @@ use super::{FixupError, FixupResult, InferCtxt}; /// points for correctness. pub struct OpportunisticVarResolver<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, + cache: FxHashMap, Ty<'tcx>>, } impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> { #[inline] pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { - OpportunisticVarResolver { infcx } + OpportunisticVarResolver { infcx, cache: Default::default() } } } @@ -31,12 +33,19 @@ impl<'a, 'tcx> TypeFolder> for OpportunisticVarResolver<'a, 'tcx> { #[inline] fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !t.has_non_region_infer() { + if let Some(&ty) = self.cache.get(&t) { + return ty; + } + + let res = if !t.has_non_region_infer() { t // micro-optimize -- if there is nothing in this type that this fold affects... } else { let t = self.infcx.shallow_resolve(t); t.super_fold_with(self) - } + }; + + assert!(self.cache.insert(t, res).is_none()); + res } fn fold_const(&mut self, ct: Const<'tcx>) -> Const<'tcx> { diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 2ee7497497aa9..c98e5c26cd71b 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -1,4 +1,4 @@ -use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_hir::def_id::DefId; pub use rustc_type_ir::fold::{ FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable, shift_region, shift_vars, @@ -164,11 +164,13 @@ struct BoundVarReplacer<'tcx, D> { current_index: ty::DebruijnIndex, delegate: D, + + cache: FxHashMap<(ty::DebruijnIndex, Ty<'tcx>), Ty<'tcx>>, } impl<'tcx, D: BoundVarReplacerDelegate<'tcx>> BoundVarReplacer<'tcx, D> { fn new(tcx: TyCtxt<'tcx>, delegate: D) -> Self { - BoundVarReplacer { tcx, current_index: ty::INNERMOST, delegate } + BoundVarReplacer { tcx, current_index: ty::INNERMOST, delegate, cache: Default::default() } } } @@ -191,7 +193,11 @@ where } fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match *t.kind() { + if let Some(&ty) = self.cache.get(&(self.current_index, t)) { + return ty; + } + + let res = match *t.kind() { ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => { let ty = self.delegate.replace_ty(bound_ty); debug_assert!(!ty.has_vars_bound_above(ty::INNERMOST)); @@ -199,7 +205,10 @@ where } _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), _ => t, - } + }; + + assert!(self.cache.insert((self.current_index, t), res).is_none()); + res } fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs index 132b7400300c5..9020b6491c277 100644 --- a/compiler/rustc_next_trait_solver/src/resolve.rs +++ b/compiler/rustc_next_trait_solver/src/resolve.rs @@ -2,6 +2,7 @@ use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::visit::TypeVisitableExt; use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; +use rustc_type_ir::data_structures::HashMap; use crate::delegate::SolverDelegate; @@ -15,11 +16,12 @@ where I: Interner, { delegate: &'a D, + cache: HashMap, } impl<'a, D: SolverDelegate> EagerResolver<'a, D> { pub fn new(delegate: &'a D) -> Self { - EagerResolver { delegate } + EagerResolver { delegate, cache: Default::default() } } } @@ -29,7 +31,11 @@ impl, I: Interner> TypeFolder for EagerResolv } fn fold_ty(&mut self, t: I::Ty) -> I::Ty { - match t.kind() { + if let Some(&ty) = self.cache.get(&t) { + return ty; + } + + let res = match t.kind() { ty::Infer(ty::TyVar(vid)) => { let resolved = self.delegate.opportunistic_resolve_ty_var(vid); if t != resolved && resolved.has_infer() { @@ -47,7 +53,10 @@ impl, I: Interner> TypeFolder for EagerResolv t } } - } + }; + + assert!(self.cache.insert(t, res).is_none()); + res } fn fold_region(&mut self, r: I::Region) -> I::Region { diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index d792862427a93..139fbf4703c46 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable}; -use rustc_type_ir::data_structures::ensure_sufficient_stack; +use rustc_type_ir::data_structures::{HashMap, HashSet, ensure_sufficient_stack}; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; @@ -587,7 +587,7 @@ where pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal>) { goal.predicate = goal .predicate - .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env }); + .fold_with(&mut ReplaceAliasWithInfer::new(self, goal.param_env)); self.inspect.add_normalizes_to_goal(self.delegate, self.max_input_universe, goal); self.nested_goals.normalizes_to_goals.push(goal); } @@ -596,7 +596,7 @@ where pub(super) fn add_goal(&mut self, source: GoalSource, mut goal: Goal) { goal.predicate = goal .predicate - .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env }); + .fold_with(&mut ReplaceAliasWithInfer::new(self, goal.param_env)); self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); self.nested_goals.goals.push((source, goal)); } @@ -660,6 +660,7 @@ where term: I::Term, universe_of_term: ty::UniverseIndex, delegate: &'a D, + cache: HashSet, } impl, I: Interner> ContainsTermOrNotNameable<'_, D, I> { @@ -677,6 +678,10 @@ where { type Result = ControlFlow<()>; fn visit_ty(&mut self, t: I::Ty) -> Self::Result { + if self.cache.contains(&t) { + return ControlFlow::Continue(()); + } + match t.kind() { ty::Infer(ty::TyVar(vid)) => { if let ty::TermKind::Ty(term) = self.term.kind() { @@ -689,17 +694,18 @@ where } } - self.check_nameable(self.delegate.universe_of_ty(vid).unwrap()) + self.check_nameable(self.delegate.universe_of_ty(vid).unwrap())?; } - ty::Placeholder(p) => self.check_nameable(p.universe()), + ty::Placeholder(p) => self.check_nameable(p.universe())?, _ => { if t.has_non_region_infer() || t.has_placeholders() { - t.super_visit_with(self) - } else { - ControlFlow::Continue(()) + t.super_visit_with(self)? } } } + + assert!(self.cache.insert(t)); + ControlFlow::Continue(()) } fn visit_const(&mut self, c: I::Const) -> Self::Result { @@ -734,6 +740,7 @@ where delegate: self.delegate, universe_of_term, term: goal.predicate.term, + cache: Default::default(), }; goal.predicate.alias.visit_with(&mut visitor).is_continue() && goal.param_env.visit_with(&mut visitor).is_continue() @@ -1021,6 +1028,17 @@ where { ecx: &'me mut EvalCtxt<'a, D>, param_env: I::ParamEnv, + cache: HashMap, +} + +impl<'me, 'a, D, I> ReplaceAliasWithInfer<'me, 'a, D, I> +where + D: SolverDelegate, + I: Interner, + { + fn new(ecx: &'me mut EvalCtxt<'a, D>, param_env: I::ParamEnv) -> Self { + ReplaceAliasWithInfer { ecx, param_env, cache: Default::default() } + } } impl TypeFolder for ReplaceAliasWithInfer<'_, '_, D, I> @@ -1033,7 +1051,11 @@ where } fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { - match ty.kind() { + if let Some(&entry) = self.cache.get(&ty) { + return entry; + } + + let res = match ty.kind() { ty::Alias(..) if !ty.has_escaping_bound_vars() => { let infer_ty = self.ecx.next_ty_infer(); let normalizes_to = ty::PredicateKind::AliasRelate( @@ -1048,7 +1070,10 @@ where infer_ty } _ => ty.super_fold_with(self), - } + }; + + assert!(self.cache.insert(ty, res).is_none()); + res } fn fold_const(&mut self, ct: I::Const) -> I::Const { diff --git a/tests/ui/traits/coherence-alias-hang.rs b/tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs similarity index 56% rename from tests/ui/traits/coherence-alias-hang.rs rename to tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs index c2b4d2e42d214..8c67078489228 100644 --- a/tests/ui/traits/coherence-alias-hang.rs +++ b/tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs @@ -1,6 +1,9 @@ //@ check-pass -//@ revisions: current next -//[next]@ compile-flags: -Znext-solver +//@ revisions: ai_current ai_next ia_current ia_next ii_current ii_next +//@[ai_next] compile-flags: -Znext-solver +//@[ia_next] compile-flags: -Znext-solver +//@[ii_next] compile-flags: -Znext-solver +// check-pass // Regression test for nalgebra hang . @@ -15,7 +18,12 @@ trait Trait { type Assoc: ?Sized; } impl Trait for W { + #[cfg(any(ai_current, ai_next))] type Assoc = W>; + #[cfg(any(ia_current, ia_next))] + type Assoc = W, T::Assoc>; + #[cfg(any(ii_current, ii_next))] + type Assoc = W, Id>; } trait Overlap {} From 5631939ab867f2716555446cb5c1f98ee2ac4a3e Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 25 Sep 2024 11:23:42 +0200 Subject: [PATCH 3/4] fmt --- .../rustc_infer/src/infer/relate/type_relating.rs | 10 +++++++--- compiler/rustc_infer/src/infer/resolve.rs | 2 +- compiler/rustc_next_trait_solver/src/resolve.rs | 2 +- .../src/solve/eval_ctxt/mod.rs | 14 ++++++-------- .../next-solver/overflow/coherence-alias-hang.rs | 1 - 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index 805856244bc7d..6167beccd8a55 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -26,7 +26,12 @@ impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> { structurally_relate_aliases: StructurallyRelateAliases, ambient_variance: ty::Variance, ) -> TypeRelating<'combine, 'infcx, 'tcx> { - TypeRelating { fields: f, structurally_relate_aliases, ambient_variance, cache: Default::default() } + TypeRelating { + fields: f, + structurally_relate_aliases, + ambient_variance, + cache: Default::default(), + } } } @@ -76,11 +81,10 @@ impl<'tcx> TypeRelation> for TypeRelating<'_, '_, 'tcx> { return Ok(a); } - let infcx = self.fields.infcx; let a = infcx.shallow_resolve(a); let b = infcx.shallow_resolve(b); - + if self.cache.contains(&(self.ambient_variance, a, b)) { return Ok(a); } diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 0dda05e611353..1a162c4dbcf93 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -36,7 +36,7 @@ impl<'a, 'tcx> TypeFolder> for OpportunisticVarResolver<'a, 'tcx> { if let Some(&ty) = self.cache.get(&t) { return ty; } - + let res = if !t.has_non_region_infer() { t // micro-optimize -- if there is nothing in this type that this fold affects... } else { diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs index 9020b6491c277..3b97028dbabfd 100644 --- a/compiler/rustc_next_trait_solver/src/resolve.rs +++ b/compiler/rustc_next_trait_solver/src/resolve.rs @@ -1,8 +1,8 @@ +use rustc_type_ir::data_structures::HashMap; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::visit::TypeVisitableExt; use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; -use rustc_type_ir::data_structures::HashMap; use crate::delegate::SolverDelegate; diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 139fbf4703c46..c6f37d7ea4149 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -585,18 +585,16 @@ where #[instrument(level = "trace", skip(self))] pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal>) { - goal.predicate = goal - .predicate - .fold_with(&mut ReplaceAliasWithInfer::new(self, goal.param_env)); + goal.predicate = + goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, goal.param_env)); self.inspect.add_normalizes_to_goal(self.delegate, self.max_input_universe, goal); self.nested_goals.normalizes_to_goals.push(goal); } #[instrument(level = "debug", skip(self))] pub(super) fn add_goal(&mut self, source: GoalSource, mut goal: Goal) { - goal.predicate = goal - .predicate - .fold_with(&mut ReplaceAliasWithInfer::new(self, goal.param_env)); + goal.predicate = + goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, goal.param_env)); self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal); self.nested_goals.goals.push((source, goal)); } @@ -681,7 +679,7 @@ where if self.cache.contains(&t) { return ControlFlow::Continue(()); } - + match t.kind() { ty::Infer(ty::TyVar(vid)) => { if let ty::TermKind::Ty(term) = self.term.kind() { @@ -1035,7 +1033,7 @@ impl<'me, 'a, D, I> ReplaceAliasWithInfer<'me, 'a, D, I> where D: SolverDelegate, I: Interner, - { +{ fn new(ecx: &'me mut EvalCtxt<'a, D>, param_env: I::ParamEnv) -> Self { ReplaceAliasWithInfer { ecx, param_env, cache: Default::default() } } diff --git a/tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs b/tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs index 8c67078489228..0d5f42231e4ad 100644 --- a/tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs +++ b/tests/ui/traits/next-solver/overflow/coherence-alias-hang.rs @@ -3,7 +3,6 @@ //@[ai_next] compile-flags: -Znext-solver //@[ia_next] compile-flags: -Znext-solver //@[ii_next] compile-flags: -Znext-solver -// check-pass // Regression test for nalgebra hang . From a1b67fa270dadccdb72d87a72795aa6cf1e58359 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 25 Sep 2024 14:58:42 +0200 Subject: [PATCH 4/4] SsoHashMap --- compiler/rustc_infer/src/infer/relate/type_relating.rs | 4 ++-- compiler/rustc_infer/src/infer/resolve.rs | 4 ++-- compiler/rustc_middle/src/ty/fold.rs | 5 +++-- compiler/rustc_next_trait_solver/src/resolve.rs | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index 6167beccd8a55..d4ad9c383db36 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -1,4 +1,4 @@ -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sso::SsoHashSet; use rustc_middle::traits::solve::Goal; use rustc_middle::ty::relate::{ Relate, RelateResult, TypeRelation, relate_args_invariantly, relate_args_with_variances, @@ -17,7 +17,7 @@ pub struct TypeRelating<'combine, 'a, 'tcx> { fields: &'combine mut CombineFields<'a, 'tcx>, structurally_relate_aliases: StructurallyRelateAliases, ambient_variance: ty::Variance, - cache: FxHashSet<(ty::Variance, Ty<'tcx>, Ty<'tcx>)>, + cache: SsoHashSet<(ty::Variance, Ty<'tcx>, Ty<'tcx>)>, } impl<'combine, 'infcx, 'tcx> TypeRelating<'combine, 'infcx, 'tcx> { diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 1a162c4dbcf93..965d3e93a3ecf 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -1,4 +1,4 @@ -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sso::SsoHashMap; use rustc_middle::bug; use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::TypeVisitableExt; @@ -16,7 +16,7 @@ use super::{FixupError, FixupResult, InferCtxt}; /// points for correctness. pub struct OpportunisticVarResolver<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, - cache: FxHashMap, Ty<'tcx>>, + cache: SsoHashMap, Ty<'tcx>>, } impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> { diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index c98e5c26cd71b..6884366e8fb6a 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -1,4 +1,5 @@ -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::sso::SsoHashMap; use rustc_hir::def_id::DefId; pub use rustc_type_ir::fold::{ FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable, shift_region, shift_vars, @@ -165,7 +166,7 @@ struct BoundVarReplacer<'tcx, D> { delegate: D, - cache: FxHashMap<(ty::DebruijnIndex, Ty<'tcx>), Ty<'tcx>>, + cache: SsoHashMap<(ty::DebruijnIndex, Ty<'tcx>), Ty<'tcx>>, } impl<'tcx, D: BoundVarReplacerDelegate<'tcx>> BoundVarReplacer<'tcx, D> { diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs index 3b97028dbabfd..f0fd79f6e4810 100644 --- a/compiler/rustc_next_trait_solver/src/resolve.rs +++ b/compiler/rustc_next_trait_solver/src/resolve.rs @@ -1,4 +1,4 @@ -use rustc_type_ir::data_structures::HashMap; +use rustc_data_structures::sso::SsoHashMap; use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_type_ir::inherent::*; use rustc_type_ir::visit::TypeVisitableExt; @@ -16,7 +16,7 @@ where I: Interner, { delegate: &'a D, - cache: HashMap, + cache: SsoHashMap, } impl<'a, D: SolverDelegate> EagerResolver<'a, D> {