From 4652295a3e60b569536dcf703ed5081c6d36787a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 22 Dec 2025 14:51:02 +1100 Subject: [PATCH 1/6] Simplify `TypeFreshener` methods. `freshen_{ty,const}` take a `Result` and just do a fold if the input is `Ok`. It's simpler to do those folds at the call site, and only call `freshen_{ty,const}` in the `Err` case. That way we can also avoid useless fold operations on the results of `new_{int,uint,float}`. Also, make some `bug!` calls more concise. --- compiler/rustc_infer/src/infer/freshen.rs | 122 ++++++++++------------ 1 file changed, 54 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index ddc0b11680627..d614fac408316 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -60,45 +60,35 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { } } - fn freshen_ty(&mut self, input: Result, ty::InferTy>, mk_fresh: F) -> Ty<'tcx> + fn freshen_ty(&mut self, input: ty::InferTy, mk_fresh: F) -> Ty<'tcx> where F: FnOnce(u32) -> Ty<'tcx>, { - match input { - Ok(ty) => ty.fold_with(self), - Err(key) => match self.ty_freshen_map.entry(key) { - Entry::Occupied(entry) => *entry.get(), - Entry::Vacant(entry) => { - let index = self.ty_freshen_count; - self.ty_freshen_count += 1; - let t = mk_fresh(index); - entry.insert(t); - t - } - }, + match self.ty_freshen_map.entry(input) { + Entry::Occupied(entry) => *entry.get(), + Entry::Vacant(entry) => { + let index = self.ty_freshen_count; + self.ty_freshen_count += 1; + let t = mk_fresh(index); + entry.insert(t); + t + } } } - fn freshen_const( - &mut self, - input: Result, ty::InferConst>, - freshener: F, - ) -> ty::Const<'tcx> + fn freshen_const(&mut self, input: ty::InferConst, freshener: F) -> ty::Const<'tcx> where F: FnOnce(u32) -> ty::InferConst, { - match input { - Ok(ct) => ct.fold_with(self), - Err(key) => match self.const_freshen_map.entry(key) { - Entry::Occupied(entry) => *entry.get(), - Entry::Vacant(entry) => { - let index = self.const_freshen_count; - self.const_freshen_count += 1; - let ct = ty::Const::new_infer(self.infcx.tcx, freshener(index)); - entry.insert(ct); - ct - } - }, + match self.const_freshen_map.entry(input) { + Entry::Occupied(entry) => *entry.get(), + Entry::Vacant(entry) => { + let index = self.const_freshen_count; + self.const_freshen_count += 1; + let ct = ty::Const::new_infer(self.infcx.tcx, freshener(index)); + entry.insert(ct); + ct + } } } } @@ -146,21 +136,21 @@ impl<'a, 'tcx> TypeFolder> for TypeFreshener<'a, 'tcx> { match ct.kind() { ty::ConstKind::Infer(ty::InferConst::Var(v)) => { let mut inner = self.infcx.inner.borrow_mut(); - let input = - inner.const_unification_table().probe_value(v).known().ok_or_else(|| { - ty::InferConst::Var(inner.const_unification_table().find(v).vid) - }); - drop(inner); - self.freshen_const(input, ty::InferConst::Fresh) + match inner.const_unification_table().probe_value(v).known() { + Some(const_) => { + drop(inner); + const_.fold_with(self) + } + None => { + let input = + ty::InferConst::Var(inner.const_unification_table().find(v).vid); + self.freshen_const(input, ty::InferConst::Fresh) + } + } } ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => { if i >= self.const_freshen_count { - bug!( - "Encountered a freshend const with id {} \ - but our counter is only at {}", - i, - self.const_freshen_count, - ); + bug!("freshened const id {} exceeds counter {}", i, self.const_freshen_count); } ct } @@ -185,50 +175,46 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { match v { ty::TyVar(v) => { let mut inner = self.infcx.inner.borrow_mut(); - let input = inner - .type_variables() - .probe(v) - .known() - .ok_or_else(|| ty::TyVar(inner.type_variables().root_var(v))); - drop(inner); - Some(self.freshen_ty(input, |n| Ty::new_fresh(self.infcx.tcx, n))) + match inner.type_variables().probe(v).known() { + Some(ty) => { + drop(inner); + Some(ty.fold_with(self)) + } + None => { + let input = ty::TyVar(inner.type_variables().root_var(v)); + Some(self.freshen_ty(input, |n| Ty::new_fresh(self.infcx.tcx, n))) + } + } } ty::IntVar(v) => { let mut inner = self.infcx.inner.borrow_mut(); let value = inner.int_unification_table().probe_value(v); - let input = match value { - ty::IntVarValue::IntType(ty) => Ok(Ty::new_int(self.infcx.tcx, ty)), - ty::IntVarValue::UintType(ty) => Ok(Ty::new_uint(self.infcx.tcx, ty)), + match value { + ty::IntVarValue::IntType(ty) => Some(Ty::new_int(self.infcx.tcx, ty)), + ty::IntVarValue::UintType(ty) => Some(Ty::new_uint(self.infcx.tcx, ty)), ty::IntVarValue::Unknown => { - Err(ty::IntVar(inner.int_unification_table().find(v))) + let input = ty::IntVar(inner.int_unification_table().find(v)); + Some(self.freshen_ty(input, |n| Ty::new_fresh_int(self.infcx.tcx, n))) } - }; - drop(inner); - Some(self.freshen_ty(input, |n| Ty::new_fresh_int(self.infcx.tcx, n))) + } } ty::FloatVar(v) => { let mut inner = self.infcx.inner.borrow_mut(); let value = inner.float_unification_table().probe_value(v); - let input = match value { - ty::FloatVarValue::Known(ty) => Ok(Ty::new_float(self.infcx.tcx, ty)), + match value { + ty::FloatVarValue::Known(ty) => Some(Ty::new_float(self.infcx.tcx, ty)), ty::FloatVarValue::Unknown => { - Err(ty::FloatVar(inner.float_unification_table().find(v))) + let input = ty::FloatVar(inner.float_unification_table().find(v)); + Some(self.freshen_ty(input, |n| Ty::new_fresh_float(self.infcx.tcx, n))) } - }; - drop(inner); - Some(self.freshen_ty(input, |n| Ty::new_fresh_float(self.infcx.tcx, n))) + } } ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct) => { if ct >= self.ty_freshen_count { - bug!( - "Encountered a freshend type with id {} \ - but our counter is only at {}", - ct, - self.ty_freshen_count - ); + bug!("freshened type id {} exceeds counter {}", ct, self.ty_freshen_count); } None } From 1bb9ff05c077756e77bcce2d34cab5b079bb4012 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 22 Dec 2025 17:48:37 +1100 Subject: [PATCH 2/6] Remove `InferCtxt::freshener`. I have always found this confusingly named, because it creates a new freshener rather than returning an existing one. We can remove it and just use `TypeFreshener::new()` at the two call sites, avoiding this confusion. --- compiler/rustc_infer/src/infer/mod.rs | 6 +----- compiler/rustc_trait_selection/src/traits/select/mod.rs | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 53567c6071e3c..571d5117bc91c 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -638,7 +638,7 @@ impl<'tcx> InferCtxt<'tcx> { } pub fn freshen>>(&self, t: T) -> T { - t.fold_with(&mut self.freshener()) + t.fold_with(&mut TypeFreshener::new(self)) } /// Returns the origin of the type variable identified by `vid`. @@ -658,10 +658,6 @@ impl<'tcx> InferCtxt<'tcx> { } } - pub fn freshener<'b>(&'b self) -> TypeFreshener<'b, 'tcx> { - freshen::TypeFreshener::new(self) - } - pub fn unresolved_variables(&self) -> Vec> { let mut inner = self.inner.borrow_mut(); let mut vars: Vec> = inner diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index ea903bac9d617..43f9d6249771e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -195,7 +195,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { infcx, - freshener: infcx.freshener(), + freshener: TypeFreshener::new(infcx), intercrate_ambiguity_causes: None, query_mode: TraitQueryMode::Standard, } From 5c49aee9ec94ab4e6d64560e973c47b22141a1e1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 22 Dec 2025 17:56:49 +1100 Subject: [PATCH 3/6] Remove `InferCtxt::freshen`. Sometimes we freshen using a new `TypeFreshener`, and sometimes we freshen with an existing `TypeFreshener`. For the former we have the method `InferCtxt::freshen`. For the latter we just call `fold_with`. This asymmetry has been confusing to me. This commit removes `InferCtxt::freshen` so that all the freshening sites consistently use `fold_with` and it's obvious if each one is using a new or existing `TypeFreshener`. --- compiler/rustc_infer/src/infer/mod.rs | 4 ---- compiler/rustc_trait_selection/src/traits/auto_trait.rs | 3 ++- compiler/rustc_trait_selection/src/traits/select/mod.rs | 3 ++- compiler/rustc_type_ir/src/const_kind.rs | 2 +- compiler/rustc_type_ir/src/ty_kind.rs | 2 +- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 571d5117bc91c..c9ea420944e23 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -637,10 +637,6 @@ impl<'tcx> InferCtxt<'tcx> { self.typing_mode } - pub fn freshen>>(&self, t: T) -> T { - t.fold_with(&mut TypeFreshener::new(self)) - } - /// Returns the origin of the type variable identified by `vid`. /// /// No attempt is made to resolve `vid` to its root variable. diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 09709291a4b95..2da7c4448ce54 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -13,6 +13,7 @@ use tracing::debug; use super::*; use crate::errors::UnableToConstructConstantValue; +use crate::infer::TypeFreshener; use crate::infer::region_constraints::{ConstraintKind, RegionConstraintData}; use crate::regions::OutlivesEnvironmentBuildExt; use crate::traits::project::ProjectAndUnifyResult; @@ -817,6 +818,6 @@ impl<'tcx> AutoTraitFinder<'tcx> { infcx: &InferCtxt<'tcx>, p: ty::Predicate<'tcx>, ) -> ty::Predicate<'tcx> { - infcx.freshen(p) + p.fold_with(&mut TypeFreshener::new(infcx)) } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 43f9d6249771e..0a1542c1d16d8 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -324,7 +324,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // separately rather than using `stack.fresh_trait_ref` -- // this is because we want the unbound variables to be // replaced with fresh types starting from index 0. - let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate); + let cache_fresh_trait_pred = + stack.obligation.predicate.fold_with(&mut TypeFreshener::new(self.infcx)); debug!(?cache_fresh_trait_pred); debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars()); diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index f315e8b3e11ce..9a83055d06c4e 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -103,7 +103,7 @@ rustc_index::newtype_index! { pub enum InferConst { /// Infer the value of the const. Var(ConstVid), - /// A fresh const variable. See `infer::freshen` for more details. + /// A fresh const variable. See `TypeFreshener` for more details. Fresh(u32), } diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 34bb3cd8a6c35..498797bef653a 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -594,7 +594,7 @@ pub enum InferTy { /// A [`FreshTy`][Self::FreshTy] is one that is generated as a replacement /// for an unbound type variable. This is convenient for caching etc. See - /// `rustc_infer::infer::freshen` for more details. + /// `TypeFreshener` for more details. /// /// Compare with [`TyVar`][Self::TyVar]. FreshTy(u32), From 63d30f1a0ad12ba12b5de4e5e77b15b30dc91e8b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 22 Dec 2025 23:33:09 +1100 Subject: [PATCH 4/6] `s/fresh_trait_ref/fresh_trait_pred`. Because `fresh_trait_pred` is the name of the field/argument. The `_ref` suffix appears to be a typo, or left over from earlier versions of the code. --- .../rustc_trait_selection/src/traits/select/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 0a1542c1d16d8..13f3a76824f59 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -321,7 +321,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.check_recursion_limit(stack.obligation, stack.obligation)?; // Check the cache. Note that we freshen the trait-ref - // separately rather than using `stack.fresh_trait_ref` -- + // separately rather than using `stack.fresh_trait_pred` -- // this is because we want the unbound variables to be // replaced with fresh types starting from index 0. let cache_fresh_trait_pred = @@ -1136,7 +1136,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// `Option>>` is `Send` if `Box>` is /// `Send`. /// - /// Note that we do this comparison using the `fresh_trait_ref` + /// Note that we do this comparison using the `fresh_trait_pred` /// fields. Because these have all been freshened using /// `self.freshener`, we can be sure that (a) this will not /// affect the inferencer state and (b) that if we see two @@ -1220,7 +1220,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if unbound_input_types && stack.iter().skip(1).any(|prev| { stack.obligation.param_env == prev.obligation.param_env - && self.match_fresh_trait_refs(stack.fresh_trait_pred, prev.fresh_trait_pred) + && self.match_fresh_trait_preds(stack.fresh_trait_pred, prev.fresh_trait_pred) }) { debug!("evaluate_stack --> unbound argument, recursive --> giving up",); @@ -2743,7 +2743,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { /////////////////////////////////////////////////////////////////////////// // Miscellany - fn match_fresh_trait_refs( + fn match_fresh_trait_preds( &self, previous: ty::PolyTraitPredicate<'tcx>, current: ty::PolyTraitPredicate<'tcx>, @@ -3032,7 +3032,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { } /// Check the provisional cache for any result for - /// `fresh_trait_ref`. If there is a hit, then you must consider + /// `fresh_trait_pred`. If there is a hit, then you must consider /// it an access to the stack slots at depth /// `reached_depth` (from the returned value). fn get_provisional( From 2d10f476938faf3993a7fb746ccf4d371601c5ca Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 22 Dec 2025 23:45:38 +1100 Subject: [PATCH 5/6] Disallow freshening an already-freshened type/const. It never happens in practice. --- compiler/rustc_infer/src/infer/freshen.rs | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index d614fac408316..1a2e5e93c8644 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -148,15 +148,12 @@ impl<'a, 'tcx> TypeFolder> for TypeFreshener<'a, 'tcx> { } } } - ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => { - if i >= self.const_freshen_count { - bug!("freshened const id {} exceeds counter {}", i, self.const_freshen_count); - } - ct + ty::ConstKind::Infer(ty::InferConst::Fresh(_)) => { + bug!("trying to freshen already-freshened const {ct:?}"); } ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => { - bug!("unexpected const {:?}", ct) + bug!("unexpected const {ct:?}") } ty::ConstKind::Param(_) @@ -171,8 +168,8 @@ impl<'a, 'tcx> TypeFolder> for TypeFreshener<'a, 'tcx> { impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { // This is separate from `fold_ty` to keep that method small and inlinable. #[inline(never)] - fn fold_infer_ty(&mut self, v: ty::InferTy) -> Option> { - match v { + fn fold_infer_ty(&mut self, ty: ty::InferTy) -> Option> { + match ty { ty::TyVar(v) => { let mut inner = self.infcx.inner.borrow_mut(); match inner.type_variables().probe(v).known() { @@ -212,11 +209,8 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { } } - ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct) => { - if ct >= self.ty_freshen_count { - bug!("freshened type id {} exceeds counter {}", ct, self.ty_freshen_count); - } - None + ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => { + bug!("trying to freshen already-freshened type {ty:?}"); } } } From ec5e96e58b55e1161f69e6515bb3f070f41c57ec Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 23 Dec 2025 07:46:12 +1100 Subject: [PATCH 6/6] Remove `Option` from `fold_infer_ty` return type. It's no longer needed. --- compiler/rustc_infer/src/infer/freshen.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs index 1a2e5e93c8644..c058bebddcf03 100644 --- a/compiler/rustc_infer/src/infer/freshen.rs +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -120,7 +120,7 @@ impl<'a, 'tcx> TypeFolder> for TypeFreshener<'a, 'tcx> { t } else { match *t.kind() { - ty::Infer(v) => self.fold_infer_ty(v).unwrap_or(t), + ty::Infer(v) => self.fold_infer_ty(v), // This code is hot enough that a non-debug assertion here makes a noticeable // difference on benchmarks like `wg-grammar`. @@ -168,18 +168,18 @@ impl<'a, 'tcx> TypeFolder> for TypeFreshener<'a, 'tcx> { impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { // This is separate from `fold_ty` to keep that method small and inlinable. #[inline(never)] - fn fold_infer_ty(&mut self, ty: ty::InferTy) -> Option> { + fn fold_infer_ty(&mut self, ty: ty::InferTy) -> Ty<'tcx> { match ty { ty::TyVar(v) => { let mut inner = self.infcx.inner.borrow_mut(); match inner.type_variables().probe(v).known() { Some(ty) => { drop(inner); - Some(ty.fold_with(self)) + ty.fold_with(self) } None => { let input = ty::TyVar(inner.type_variables().root_var(v)); - Some(self.freshen_ty(input, |n| Ty::new_fresh(self.infcx.tcx, n))) + self.freshen_ty(input, |n| Ty::new_fresh(self.infcx.tcx, n)) } } } @@ -188,11 +188,11 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { let mut inner = self.infcx.inner.borrow_mut(); let value = inner.int_unification_table().probe_value(v); match value { - ty::IntVarValue::IntType(ty) => Some(Ty::new_int(self.infcx.tcx, ty)), - ty::IntVarValue::UintType(ty) => Some(Ty::new_uint(self.infcx.tcx, ty)), + ty::IntVarValue::IntType(ty) => Ty::new_int(self.infcx.tcx, ty), + ty::IntVarValue::UintType(ty) => Ty::new_uint(self.infcx.tcx, ty), ty::IntVarValue::Unknown => { let input = ty::IntVar(inner.int_unification_table().find(v)); - Some(self.freshen_ty(input, |n| Ty::new_fresh_int(self.infcx.tcx, n))) + self.freshen_ty(input, |n| Ty::new_fresh_int(self.infcx.tcx, n)) } } } @@ -201,10 +201,10 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { let mut inner = self.infcx.inner.borrow_mut(); let value = inner.float_unification_table().probe_value(v); match value { - ty::FloatVarValue::Known(ty) => Some(Ty::new_float(self.infcx.tcx, ty)), + ty::FloatVarValue::Known(ty) => Ty::new_float(self.infcx.tcx, ty), ty::FloatVarValue::Unknown => { let input = ty::FloatVar(inner.float_unification_table().find(v)); - Some(self.freshen_ty(input, |n| Ty::new_fresh_float(self.infcx.tcx, n))) + self.freshen_ty(input, |n| Ty::new_fresh_float(self.infcx.tcx, n)) } } }