diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 1e08e9b3ddf36..5f07a78d28bb7 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1345,6 +1345,7 @@ fn check_region_late_boundedness<'tcx>( .borrow_mut() .unwrap_region_constraints() .shallow_resolve_region_var(tcx, vid) + .reuse_if_unchanged(r) && let ty::ReLateParam(ty::LateParamRegion { kind: ty::LateParamRegionKind::Named(trait_param_def_id), .. @@ -1370,6 +1371,7 @@ fn check_region_late_boundedness<'tcx>( .borrow_mut() .unwrap_region_constraints() .shallow_resolve_region_var(tcx, vid) + .reuse_if_unchanged(r) && let ty::ReLateParam(ty::LateParamRegion { kind: ty::LateParamRegionKind::Named(impl_param_def_id), .. diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 07ce529f9174d..265bb4ef0b22d 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -285,7 +285,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Given the expected type, figures out what it can about this closure we - /// are about to type check: + /// are about to type check. + /// + /// WARNING: `expected_ty` must be resolved, to ensure that tyvars refer to root vids. #[instrument(skip(self), level = "debug", ret)] fn deduce_closure_signature( &self, @@ -312,13 +314,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .and_then(|did| self.tcx.fn_trait_kind_from_def_id(did)); (sig, kind) } - ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates( - Ty::new_var(self.tcx, self.root_var(vid)), - closure_kind, - self.obligations_for_self_ty(vid, UseSubtyping::No) - .into_iter() - .filter_map(|obl| Some((obl.predicate.as_clause()?, obl.cause.span))), - ), + ty::Infer(ty::TyVar(vid)) => { + // assert that the precondition (documented in the doc comments) is maintained. + debug_assert_eq!(self.root_ty_var(vid), vid); + self.deduce_closure_signature_from_predicates( + Ty::new_var(self.tcx, vid), + closure_kind, + self.obligations_for_self_ty(vid, UseSubtyping::No) + .into_iter() + .filter_map(|obl| Some((obl.predicate.as_clause()?, obl.cause.span))), + ) + } ty::FnPtr(sig_tys, hdr) => match closure_kind { hir::ClosureKind::Closure => { let expected_sig = ExpectedSig { cause_span: None, sig: sig_tys.with(hdr) }; diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 09c5e36f42847..a4fd7486c1056 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -315,12 +315,12 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// If `ty` is an unresolved type variable, returns its root vid. fn root_vid(&self, ty: Ty<'tcx>) -> Option { - Some(self.root_var(self.shallow_resolve(ty).ty_vid()?)) + Some(self.shallow_resolve(ty).ty_vid()?) } /// If `ty` is an unresolved float type variable, returns its root vid. pub(crate) fn root_float_vid(&self, ty: Ty<'tcx>) -> Option { - Some(self.root_float_var(self.shallow_resolve(ty).float_vid()?)) + Some(self.shallow_resolve(ty).float_vid()?) } /// Given a set of diverging vids and coercions, walk the HIR to gather a diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index ee9ebfea1fb38..16c584e9c462e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -95,7 +95,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *ty.kind() { ty::Infer(ty::TyVar(found_vid)) => match subtyping { - UseSubtyping::No => self.root_var(expected_vid) == self.root_var(found_vid), + UseSubtyping::No => self.root_ty_var(expected_vid) == found_vid, UseSubtyping::Yes => { self.sub_unification_table_root_var(expected_vid) == self.sub_unification_table_root_var(found_vid) diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index f1f2077f81937..768c7ab74cb08 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -165,7 +165,8 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { .inner .borrow_mut() .unwrap_region_constraints() - .shallow_resolve_region_var(canonicalizer.tcx, vid); + .shallow_resolve_region_var(canonicalizer.tcx, vid) + .reuse_if_unchanged(r); debug!( "canonical: region var found with vid {vid:?}, \ opportunistically resolved to {r:?}", @@ -335,7 +336,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { // We need to canonicalize the *root* of our ty var. // This is so that our canonical response correctly reflects // any equated inference vars correctly! - let root_vid = self.infcx.unwrap().root_var(vid); + let root_vid = self.infcx.unwrap().root_ty_var(vid); if root_vid != vid { t = Ty::new_var(self.tcx, root_vid); vid = root_vid; @@ -363,7 +364,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { } ty::Infer(ty::IntVar(vid)) => { - let nt = self.infcx.unwrap().shallow_resolve_int_var(vid); + let nt = self.infcx.unwrap().shallow_resolve_int_var(vid).reuse_if_unchanged(t); if nt != t { return self.fold_ty(nt); } else { @@ -371,7 +372,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { } } ty::Infer(ty::FloatVar(vid)) => { - let nt = self.infcx.unwrap().shallow_resolve_float_var(vid); + let nt = self.infcx.unwrap().shallow_resolve_float_var(vid).reuse_if_unchanged(t); if nt != t { return self.fold_ty(nt); } else { diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index f5fadd2ab8229..e98359d0fb316 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -103,7 +103,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { } fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid { - self.root_var(var) + self.root_ty_var(var) } fn sub_unification_table_root_var(&self, var: ty::TyVid) -> ty::TyVid { @@ -124,23 +124,32 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.root_const_var(var) } - fn shallow_resolve_ty_var(&self, vid: ty::TyVid) -> Ty<'tcx> { + fn shallow_resolve_ty_var(&self, vid: ty::TyVid) -> ty::MaybeResolved> { self.shallow_resolve_ty_var(vid) } - fn shallow_resolve_int_var(&self, vid: ty::IntVid) -> Ty<'tcx> { + fn shallow_resolve_int_var(&self, vid: ty::IntVid) -> ty::MaybeResolved> { self.shallow_resolve_int_var(vid) } - fn shallow_resolve_float_var(&self, vid: ty::FloatVid) -> Ty<'tcx> { + fn shallow_resolve_float_var( + &self, + vid: ty::FloatVid, + ) -> ty::MaybeResolved> { self.shallow_resolve_float_var(vid) } - fn shallow_resolve_const_var(&self, vid: ty::ConstVid) -> ty::Const<'tcx> { + fn shallow_resolve_const_var( + &self, + vid: ty::ConstVid, + ) -> ty::MaybeResolved> { self.shallow_resolve_const_var(vid) } - fn shallow_resolve_region_var(&self, vid: ty::RegionVid) -> ty::Region<'tcx> { + fn shallow_resolve_region_var( + &self, + vid: ty::RegionVid, + ) -> ty::MaybeResolved> { self.inner .borrow_mut() .unwrap_region_constraints() @@ -455,7 +464,7 @@ impl<'a, 'tcx> ty::TypeFolder> for LowerUniverseFolder<'a, 'tcx> { let folded = match t.kind() { ty::Infer(ty::TyVar(vid)) => { - let vid = self.infcx.root_var(*vid); + let vid = self.infcx.root_ty_var(*vid); let probe = self.infcx.inner.borrow_mut().type_variables().probe(vid); match probe { TypeVariableValue::Known { value: u } => u.super_fold_with(self), @@ -487,8 +496,8 @@ impl<'a, 'tcx> ty::TypeFolder> for LowerUniverseFolder<'a, 'tcx> { match c.kind() { ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { - let vid = self.infcx.root_const_var(vid); - let universe = match self.infcx.try_resolve_const_var(vid) { + let (res, vid) = self.infcx.try_resolve_const_var_with_root(vid); + let universe = match res { Ok(value) => return value.fold_with(self), Err(universe) => universe, }; diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 0df656768efde..d9ad3ed93de45 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -29,12 +29,12 @@ use rustc_middle::traits::solve::Goal; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{ self, BoundVarReplacerDelegate, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, - GenericParamDefKind, InferConst, OpaqueTypeKey, ProvisionalHiddenType, PseudoCanonicalInput, - Term, Ty, TyCtxt, TyVid, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, - TypeVisitableExt, TypingEnv, TypingMode, fold_regions, + GenericParamDefKind, InferConst, InferTy, IntVid, OpaqueTypeKey, ProvisionalHiddenType, + PseudoCanonicalInput, Term, Ty, TyCtxt, TyVid, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitable, TypeVisitableExt, TypingEnv, TypingMode, fold_regions, }; use rustc_span::{DUMMY_SP, Span, Symbol, bug}; -use rustc_type_ir::MayBeErased; +use rustc_type_ir::{Const, MayBeErased}; use snapshot::undo_log::InferCtxtUndoLogs; use tracing::{debug, instrument}; use ty::solve::TyOrConstInferVar; @@ -1221,24 +1221,161 @@ impl<'tcx> InferCtxt<'tcx> { self.deeply_resolve_ignoring_regions(t).to_string() } - /// If `TyVar(vid)` resolves to a type, return that type. Else, return the - /// universe index of `TyVar(vid)`. + /// If `TyVar(vid)` resolves to a type, return that type. + /// Else, return the universe index of `TyVar(vid)`. + /// + /// Also return the root `TyVid` of `vid`. + /// This is more efficient than calling [`try_resolve_ty_var`](Self::try_resolve_ty_var) + /// followed by [`root_ty_var`](Self::root_ty_var). + pub fn try_resolve_ty_var_with_root( + &self, + vid: TyVid, + ) -> (Result, ty::UniverseIndex>, TyVid) { + let (root, value) = self.inner.borrow_mut().type_variables().probe_with_root_vid(vid); + + ( + match value { + TypeVariableValue::Known { value } => Ok(self.shallow_resolve_non_recursive(value)), + TypeVariableValue::Unknown { universe } => Err(universe), + }, + root, + ) + } + + /// If `TyVar(vid)` resolves to a type, return that type. + /// Else, return the universe index of `TyVar(vid)`. pub fn try_resolve_ty_var(&self, vid: TyVid) -> Result, ty::UniverseIndex> { - use self::type_variable::TypeVariableValue; + let value = self.inner.borrow_mut().type_variables().probe(vid); - match self.inner.borrow_mut().type_variables().probe(vid) { - TypeVariableValue::Known { value } => Ok(value), + match value { + TypeVariableValue::Known { value } => Ok(self.shallow_resolve_non_recursive(value)), TypeVariableValue::Unknown { universe } => Err(universe), } } /// If `vid` resolves to a type, return that type. Otherwise return the root variable id for `vid`. pub fn shallow_resolve_ty_var_or_get_root(&self, vid: TyVid) -> Result, TyVid> { - let (root, value) = self.inner.borrow_mut().type_variables().probe_with_root_vid(vid); + let (res, root) = self.try_resolve_ty_var_with_root(vid); + res.map_err(|_| root) + } + + /// Resolves a type var to a rigid type, if it was constrained to one, + /// or else the root type var in the unification table. + #[inline] + fn shallow_resolve_ty_var(&self, v: TyVid) -> ty::MaybeResolved> { + let (root_vid, value) = self.inner.borrow_mut().type_variables().inlined_probe_with_vid(v); + match value { + // Not entirely obvious: + // It's possible for a type variable to resolve to an int/float variable. + // When that happens, the int/float variable may itself already be resolved + // to an int/float, which is the type we actually want to return, not the variable. + // + // Only one step of this is ever possible. We never resolve type variables to other + // type variables. Therefore, we use [`shallow_resolve_non_recursive`](Self::shallow_resolve_non_recursive), + // to call into a version of shallow_resolve that only knows about int/float variables + // and panics (and notably: doesn't recurse again) when it sees type variables. + // That way the compiler knows the recursion can only ever go two deep, which helps performance. + // + // `ty` is a type that we may already have available, which represents the `TyVid`. + // In cases where we do, this can aid performance. + TypeVariableValue::Known { value } => { + ty::MaybeResolved::resolved(self.shallow_resolve_non_recursive(value)) + } + TypeVariableValue::Unknown { .. } => { + ty::MaybeResolved::unresolved(v, root_vid, |v| Ty::new_var(self.tcx, v)) + } + } + } + + /// Resolve a type variable to an integer type, if known. + /// Otherwise return a type with the root int vid in it. + #[inline] + fn shallow_resolve_int_var(&self, v: IntVid) -> ty::MaybeResolved> { + let (root, value) = + self.inner.borrow_mut().int_unification_table().inlined_probe_key_value(v); + match value { + ty::IntVarValue::IntType(ty) => ty::MaybeResolved::resolved(Ty::new_int(self.tcx, ty)), + ty::IntVarValue::UintType(ty) => { + ty::MaybeResolved::resolved(Ty::new_uint(self.tcx, ty)) + } + ty::IntVarValue::Unknown => { + ty::MaybeResolved::unresolved(v, root, |v| Ty::new_int_var(self.tcx, v)) + } + } + } + /// Resolve a type variable to a float type, if known. + /// Otherwise return a type with the root float vid in it. + #[inline] + fn shallow_resolve_float_var(&self, v: FloatVid) -> ty::MaybeResolved> { + let (root, value) = + self.inner.borrow_mut().float_unification_table().inlined_probe_key_value(v); match value { - TypeVariableValue::Known { value } => Ok(value), - TypeVariableValue::Unknown { universe: _ } => Err(root), + ty::FloatVarValue::Known(ty) => { + ty::MaybeResolved::resolved(Ty::new_float(self.tcx, ty)) + } + ty::FloatVarValue::Unknown => { + ty::MaybeResolved::unresolved(v, root, |v| Ty::new_float_var(self.tcx, v)) + } + } + } + + /// Resolve a const variable to a const, if known. + /// Otherwise return a const with the root const vid in it. + #[inline] + fn shallow_resolve_const_var( + &self, + v: ConstVid, + ) -> ty::MaybeResolved> { + let (root, value) = + self.inner.borrow_mut().const_unification_table().inlined_probe_key_value(v); + match value { + ConstVariableValue::Known { value } => ty::MaybeResolved::resolved(value), + ConstVariableValue::Unknown { .. } => { + ty::MaybeResolved::unresolved(v, root.vid, |v| Const::new_var(self.tcx, v)) + } + } + } + + /// Shallow resolve a type/int infer var, panics on type variables. + /// + /// See docs on [`shallow_resolve_ty_var`](Self::shallow_resolve_ty_var) for why this exists. + #[inline(never)] + // Cold because the case in which a tyvar resolves to an intvar which resolves to a type is + // quite rare. It's way more common for `shallow_resolve_non_recursive` to return ty. + #[cold] + fn shallow_resolve_infer_non_recursive(&self, infer: InferTy, ty: Ty<'tcx>) -> Ty<'tcx> { + match infer { + ty::TyVar(_) => { + unreachable!() + } + ty::IntVar(v) => self.shallow_resolve_int_var(v).reuse_if_unchanged(ty), + ty::FloatVar(v) => self.shallow_resolve_float_var(v).reuse_if_unchanged(ty), + ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => ty, + } + } + + #[inline] + fn shallow_resolve_infer(&self, infer: InferTy, ty: Ty<'tcx>) -> Ty<'tcx> { + match infer { + ty::TyVar(v) => self.shallow_resolve_ty_var(v).reuse_if_unchanged(ty), + ty::IntVar(v) => self.shallow_resolve_int_var(v).reuse_if_unchanged(ty), + ty::FloatVar(v) => self.shallow_resolve_float_var(v).reuse_if_unchanged(ty), + ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => ty, + } + } + + /// Shallow resolve a type, panics on type variables. + /// See [`shallow_resolve`](Self::shallow_resolve) for more docs. + /// + /// See docs on [`shallow_resolve_ty_var`](Self::shallow_resolve_ty_var) for why this alternate + /// version of shallow_resolve exists. + #[inline(always)] + fn shallow_resolve_non_recursive(&self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Infer(infer) = *ty.kind() { + self.shallow_resolve_infer_non_recursive(infer, ty) + } else { + ty } } @@ -1262,68 +1399,7 @@ impl<'tcx> InferCtxt<'tcx> { /// these nested variables. If that's what you want, use [`deeply_resolve_ignoring_regions`](Self::deeply_resolve_ignoring_regions), /// or better [`deeply_resolve_via_unification_table`](rustc_type_ir::InferCtxtLike::deeply_resolve_via_unification_table), if you can, which *does* resolve regions. pub fn shallow_resolve(&self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Infer(v) = *ty.kind() { - match v { - ty::TyVar(v) => { - // Not entirely obvious: if `typ` is a type variable, - // it can be resolved to an int/float variable, which - // can then be recursively resolved, hence the - // recursion. Note though that we prevent type - // variables from unifying to other type variables - // directly (though they may be embedded - // structurally), and we prevent cycles in any case, - // so this recursion should always be of very limited - // depth. - // - // Note: if these two lines are combined into one we get - // dynamic borrow errors on `self.inner`. - let (root_vid, value) = - self.inner.borrow_mut().type_variables().probe_with_root_vid(v); - value.known().map_or_else( - || if root_vid == v { ty } else { Ty::new_var(self.tcx, root_vid) }, - |t| self.shallow_resolve(t), - ) - } - - ty::IntVar(v) => { - let (root, value) = - self.inner.borrow_mut().int_unification_table().inlined_probe_key_value(v); - match value { - ty::IntVarValue::IntType(ty) => Ty::new_int(self.tcx, ty), - ty::IntVarValue::UintType(ty) => Ty::new_uint(self.tcx, ty), - ty::IntVarValue::Unknown => { - if root == v { - ty - } else { - Ty::new_int_var(self.tcx, root) - } - } - } - } - - ty::FloatVar(v) => { - let (root, value) = self - .inner - .borrow_mut() - .float_unification_table() - .inlined_probe_key_value(v); - match value { - ty::FloatVarValue::Known(ty) => Ty::new_float(self.tcx, ty), - ty::FloatVarValue::Unknown => { - if root == v { - ty - } else { - Ty::new_float_var(self.tcx, root) - } - } - } - } - - ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => ty, - } - } else { - ty - } + if let ty::Infer(infer) = *ty.kind() { self.shallow_resolve_infer(infer, ty) } else { ty } } /// See docs on [`shallow_resolve`](Self::shallow_resolve) for more explanation. @@ -1331,19 +1407,9 @@ impl<'tcx> InferCtxt<'tcx> { pub fn shallow_resolve_const(&self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { match ct.kind() { ty::ConstKind::Infer(infer_ct) => match infer_ct { - InferConst::Var(vid) => { - let (root, value) = self - .inner - .borrow_mut() - .const_unification_table() - .inlined_probe_key_value(vid); - value.known().unwrap_or_else(|| { - if root.vid == vid { ct } else { ty::Const::new_var(self.tcx, root.vid) } - }) - } + InferConst::Var(vid) => self.shallow_resolve_const_var(vid).reuse_if_unchanged(ct), InferConst::Fresh(_) => ct, }, - ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) @@ -1363,7 +1429,7 @@ impl<'tcx> InferCtxt<'tcx> { } } - pub fn root_var(&self, var: ty::TyVid) -> ty::TyVid { + pub fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid { self.inner.borrow_mut().type_variables().root_var(var) } @@ -1390,51 +1456,6 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().const_unification_table().find(var).vid } - /// Resolves a const var to a rigid const, if it was constrained to one, - /// or else the root const var in the unification table. - pub fn shallow_resolve_const_var(&self, vid: ty::ConstVid) -> ty::Const<'tcx> { - match self.try_resolve_const_var(vid) { - Ok(ct) => ct, - Err(_) => ty::Const::new_var(self.tcx, self.root_const_var(vid)), - } - } - - /// Resolves a type var to a rigid type, if it was constrained to one, - /// or else the root type var in the unification table. - pub fn shallow_resolve_ty_var(&self, vid: ty::TyVid) -> Ty<'tcx> { - match self.try_resolve_ty_var(vid) { - Ok(ty) => ty, - Err(_) => Ty::new_var(self.tcx, self.root_var(vid)), - } - } - - /// Resolves an int var to a rigid int type, if it was constrained to one, - /// or else the root int var in the unification table. - pub fn shallow_resolve_int_var(&self, vid: ty::IntVid) -> Ty<'tcx> { - let mut inner = self.inner.borrow_mut(); - let value = inner.int_unification_table().probe_value(vid); - match value { - ty::IntVarValue::IntType(ty) => Ty::new_int(self.tcx, ty), - ty::IntVarValue::UintType(ty) => Ty::new_uint(self.tcx, ty), - ty::IntVarValue::Unknown => { - Ty::new_int_var(self.tcx, inner.int_unification_table().find(vid)) - } - } - } - - /// Resolves a float var to a rigid type, if it was constrained to one, - /// or else the root float var in the unification table. - pub fn shallow_resolve_float_var(&self, vid: ty::FloatVid) -> Ty<'tcx> { - let mut inner = self.inner.borrow_mut(); - let value = inner.float_unification_table().probe_value(vid); - match value { - ty::FloatVarValue::Known(ty) => Ty::new_float(self.tcx, ty), - ty::FloatVarValue::Unknown => { - Ty::new_float_var(self.tcx, inner.float_unification_table().find(vid)) - } - } - } - /// If a type/const variable has not (yet) been unified, it is left as is. /// /// This is an idempotent operation that does not affect inference state in any way, @@ -1480,6 +1501,29 @@ impl<'tcx> InferCtxt<'tcx> { value.fold_with(&mut r) } + /// If `ConstVar(vid)` resolves to a const, return that const. + /// Else, return the universe index of `ConstVar(vid)`. + /// + /// Also return the root `ConstVid` of `vid`. + /// This is more efficient than calling [`try_resolve_const_var`](Self::try_resolve_const_var) + /// followed by [`root_const_var`](Self::root_const_var). + pub fn try_resolve_const_var_with_root( + &self, + vid: ty::ConstVid, + ) -> (Result, ty::UniverseIndex>, ty::ConstVid) { + let (root, value) = + self.inner.borrow_mut().const_unification_table().inlined_probe_key_value(vid); + ( + match value { + ConstVariableValue::Known { value } => Ok(value), + ConstVariableValue::Unknown { origin: _, universe } => Err(universe), + }, + root.vid, + ) + } + + /// If `ConstVar(vid)` resolves to a const, return that const. + /// Else, return the universe index of `ConstVar(vid)`. pub fn try_resolve_const_var( &self, vid: ty::ConstVid, diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs index cde1fc8491488..b59c180110925 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -749,12 +749,13 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> { &mut self, tcx: TyCtxt<'tcx>, vid: ty::RegionVid, - ) -> ty::Region<'tcx> { - let mut ut = self.unification_table_mut(); - let root_vid = ut.find(vid).vid; - match ut.probe_value(root_vid) { - RegionVariableValue::Known { value } => value, - RegionVariableValue::Unknown { .. } => ty::Region::new_var(tcx, root_vid), + ) -> ty::MaybeResolved> { + let (root_vid, value) = self.unification_table_mut().inlined_probe_key_value(vid); + match value { + RegionVariableValue::Known { value } => ty::MaybeResolved::resolved(value), + RegionVariableValue::Unknown { .. } => { + ty::MaybeResolved::unresolved(vid, root_vid.vid, |r| ty::Region::new_var(tcx, r)) + } } } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index d885c4be1e511..67b9d9b3f36b0 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -287,12 +287,13 @@ impl<'tcx> InferCtxt<'tcx> { assert!(!source_term.has_escaping_bound_vars()); let (for_universe, root_vid) = match target_vid { TermVid::Ty(ty_vid) => { - (self.try_resolve_ty_var(ty_vid).unwrap_err(), TermVid::Ty(self.root_var(ty_vid))) + let (res, root) = self.try_resolve_ty_var_with_root(ty_vid); + (res.unwrap_err(), TermVid::Ty(root)) + } + TermVid::Const(ct_vid) => { + let (res, root) = self.try_resolve_const_var_with_root(ct_vid); + (res.unwrap_err(), TermVid::Const(root)) } - TermVid::Const(ct_vid) => ( - self.try_resolve_const_var(ct_vid).unwrap_err(), - TermVid::Const(self.inner.borrow_mut().const_unification_table().find(ct_vid).vid), - ), }; let mut generalizer = Generalizer { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 406a42c2b9a80..8f28029a972cb 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -163,6 +163,8 @@ mod structural_impls; mod typeck_results; mod visit; +pub type MaybeResolved = rustc_type_ir::MaybeResolved; + // Data types #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, StableHash)] diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index b7719ec0598de..5f5110d93c38b 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -950,6 +950,14 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { Ty::new_var(tcx, vid) } + fn new_int_var(tcx: TyCtxt<'tcx>, vid: ty::IntVid) -> Self { + Ty::new_int_var(tcx, vid) + } + + fn new_float_var(tcx: TyCtxt<'tcx>, vid: ty::FloatVid) -> Self { + Ty::new_float_var(tcx, vid) + } + fn new_param(tcx: TyCtxt<'tcx>, param: ty::ParamTy) -> Self { Ty::new_param(tcx, param.index, param.name) } diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index 569fbd41e1b22..4c45bd349540f 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -322,9 +322,8 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { let kind = match t.kind() { ty::Infer(i) => match i { ty::TyVar(vid) => { - debug_assert_eq!( - self.delegate.shallow_resolve_ty_var(vid), - t, + debug_assert!( + self.delegate.shallow_resolve_ty_var(vid).is_unchanged(), "ty vid should have been resolved fully before canonicalization" ); @@ -339,17 +338,15 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { CanonicalVarKind::Ty { ui, sub_root } } ty::IntVar(vid) => { - debug_assert_eq!( - self.delegate.shallow_resolve_int_var(vid), - t, + debug_assert!( + self.delegate.shallow_resolve_int_var(vid).is_unchanged(), "ty vid should have been resolved fully before canonicalization" ); CanonicalVarKind::Int } ty::FloatVar(vid) => { - debug_assert_eq!( - self.delegate.shallow_resolve_float_var(vid), - t, + debug_assert!( + self.delegate.shallow_resolve_float_var(vid).is_unchanged(), "ty vid should have been resolved fully before canonicalization" ); CanonicalVarKind::Float @@ -489,9 +486,8 @@ impl, I: Interner> TypeFolder for Canonicaliz }, ty::ReVar(vid) => { - debug_assert_eq!( - self.delegate.shallow_resolve_region_var(vid), - r, + debug_assert!( + self.delegate.shallow_resolve_region_var(vid).is_unchanged(), "region vid should have been resolved fully before canonicalization" ); match self.canonicalize_mode { @@ -534,9 +530,8 @@ impl, I: Interner> TypeFolder for Canonicaliz let kind = match c.kind() { ty::ConstKind::Infer(i) => match i { ty::InferConst::Var(vid) => { - debug_assert_eq!( - self.delegate.shallow_resolve_const_var(vid), - c, + debug_assert!( + self.delegate.shallow_resolve_const_var(vid).is_unchanged(), "const vid should have been resolved fully before canonicalization" ); diff --git a/compiler/rustc_next_trait_solver/src/placeholder.rs b/compiler/rustc_next_trait_solver/src/placeholder.rs index fdf702248196a..c732b08300750 100644 --- a/compiler/rustc_next_trait_solver/src/placeholder.rs +++ b/compiler/rustc_next_trait_solver/src/placeholder.rs @@ -249,7 +249,7 @@ where fn fold_region(&mut self, r0: Region) -> Region { let r1 = match r0.kind() { - ty::ReVar(vid) => self.infcx.shallow_resolve_region_var(vid), + ty::ReVar(vid) => self.infcx.shallow_resolve_region_var(vid).reuse_if_unchanged(r0), _ => r0, }; 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 9c32cf73f39a8..45f0b0fca9dc5 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 @@ -1463,7 +1463,7 @@ where pub(super) fn eager_resolve_region(&self, r: Region) -> Region { if let ty::ReVar(vid) = r.kind() { - self.delegate.shallow_resolve_region_var(vid) + self.delegate.shallow_resolve_region_var(vid).reuse_if_unchanged(r) } else { r } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index ac4b42d393d53..67155e3454401 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -529,7 +529,8 @@ fn plug_infer_with_placeholders<'tcx>( .inner .borrow_mut() .unwrap_region_constraints() - .shallow_resolve_region_var(self.infcx.tcx, vid); + .shallow_resolve_region_var(self.infcx.tcx, vid) + .reuse_if_unchanged(r); if r.is_var() { let Ok(InferOk { value: (), obligations }) = self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index d6d4d27764c46..c90ae55885bbe 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -12,8 +12,8 @@ use crate::relate::RelateResult; use crate::relate::combine::PredicateEmittingRelation; use crate::solve::{TyOrConstInferVar, VisibleForLeakCheck}; use crate::{ - self as ty, Const, Interner, PredicateProxy, Region, TyVid, TypeFoldable, TypeFolder, - TypeSuperFoldable, TypeVisitableExt, + self as ty, Const, Interner, PredicateProxy, Region, RegionVid, TyVid, TypeFoldable, + TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; mod private { @@ -367,6 +367,50 @@ impl From> for TypingMode { + Resolved(TI), + Unchanged(T), +} + +impl MaybeResolved { + pub fn unresolved(before: T, after: T, intern: impl FnOnce(T) -> TI) -> Self { + if before == after { + // If unchanged, mark as such so we can reuse. + Self::Unchanged(before) + } else { + // If changed, immediately intern it, no reuse is possible anyway. + Self::Resolved(intern(before)) + } + } + + pub fn resolved(r: TI) -> Self { + Self::Resolved(r) + } + + pub fn is_unchanged(&self) -> bool { + match self { + MaybeResolved::Resolved(_) => false, + MaybeResolved::Unchanged { .. } => true, + } + } + + #[inline] + pub fn intern(self, intern: impl FnOnce(T) -> TI) -> TI { + match self { + MaybeResolved::Resolved(r) => r, + MaybeResolved::Unchanged(u) => intern(u), + } + } + + #[inline] + pub fn reuse_if_unchanged(self, before: TI) -> TI { + match self { + MaybeResolved::Resolved(r) => r, + MaybeResolved::Unchanged(_u) => before, + } + } +} + /// `InferCtxtLike` is one of the two traits abstracting over the [InferCtxt][inferctxt-doc], which /// had to be split due to coherence reasons: /// - `InferCtxtLike`] contains the parts that have to live in `rustc_infer`, and thus aren't only @@ -433,11 +477,26 @@ pub trait InferCtxtLike: Sized { fn is_sub_unification_table_root_var(&self, var: ty::TyVid) -> bool; fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid; - fn shallow_resolve_ty_var(&self, vid: ty::TyVid) -> ::Ty; - fn shallow_resolve_int_var(&self, vid: ty::IntVid) -> ::Ty; - fn shallow_resolve_float_var(&self, vid: ty::FloatVid) -> ::Ty; - fn shallow_resolve_const_var(&self, vid: ty::ConstVid) -> Const; - fn shallow_resolve_region_var(&self, vid: ty::RegionVid) -> Region; + fn shallow_resolve_ty_var( + &self, + vid: ty::TyVid, + ) -> MaybeResolved::Ty>; + fn shallow_resolve_int_var( + &self, + vid: ty::IntVid, + ) -> MaybeResolved::Ty>; + fn shallow_resolve_float_var( + &self, + vid: ty::FloatVid, + ) -> MaybeResolved::Ty>; + fn shallow_resolve_const_var( + &self, + vid: ty::ConstVid, + ) -> MaybeResolved>; + fn shallow_resolve_region_var( + &self, + vid: ty::RegionVid, + ) -> MaybeResolved>; fn ty_or_const_infer_var_changed(&self, var: TyOrConstInferVar) -> bool; @@ -662,15 +721,19 @@ impl, I: Interner> TypeFolder fn fold_ty(&mut self, t: I::Ty) -> I::Ty { match t.kind() { ty::Infer(ty::TyVar(vid)) => { - let resolved = self.delegate.shallow_resolve_ty_var(vid); + let resolved = self.delegate.shallow_resolve_ty_var(vid).reuse_if_unchanged(t); if t != resolved && resolved.has_infer() { resolved.fold_with(self) } else { resolved } } - ty::Infer(ty::IntVar(vid)) => self.delegate.shallow_resolve_int_var(vid), - ty::Infer(ty::FloatVar(vid)) => self.delegate.shallow_resolve_float_var(vid), + ty::Infer(ty::IntVar(vid)) => { + self.delegate.shallow_resolve_int_var(vid).reuse_if_unchanged(t) + } + ty::Infer(ty::FloatVar(vid)) => { + self.delegate.shallow_resolve_float_var(vid).reuse_if_unchanged(t) + } _ => { if t.has_infer() { if let Some(&ty) = self.cache.get(&t) { @@ -688,7 +751,7 @@ impl, I: Interner> TypeFolder fn fold_region(&mut self, r: Region) -> Region { match r.kind() { - ty::ReVar(vid) => self.delegate.shallow_resolve_region_var(vid), + ty::ReVar(vid) => self.delegate.shallow_resolve_region_var(vid).reuse_if_unchanged(r), _ => r, } } @@ -696,7 +759,7 @@ impl, I: Interner> TypeFolder fn fold_const(&mut self, c: Const) -> Const { match c.kind() { ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { - let resolved = self.delegate.shallow_resolve_const_var(vid); + let resolved = self.delegate.shallow_resolve_const_var(vid).reuse_if_unchanged(c); if c != resolved && resolved.has_infer() { resolved.fold_with(self) } else { diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 3135523889014..4ed4f6d3dc1e5 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -44,6 +44,8 @@ pub trait Ty>: fn new_infer(interner: I, var: ty::InferTy) -> Self; fn new_var(interner: I, var: ty::TyVid) -> Self; + fn new_int_var(interner: I, var: ty::IntVid) -> Self; + fn new_float_var(interner: I, var: ty::FloatVid) -> Self; fn new_param(interner: I, param: I::ParamTy) -> Self; diff --git a/compiler/rustc_type_ir/src/universe.rs b/compiler/rustc_type_ir/src/universe.rs index 34210a0251fb8..10228aa8ea24c 100644 --- a/compiler/rustc_type_ir/src/universe.rs +++ b/compiler/rustc_type_ir/src/universe.rs @@ -154,7 +154,7 @@ impl< self.max_universe = self.max_universe.max(p.universe) } RegionKind::ReVar(var) if VISIT_INFER => { - match self.infcx.shallow_resolve_region_var(var).kind() { + match self.infcx.shallow_resolve_region_var(var).reuse_if_unchanged(r).kind() { RegionKind::RePlaceholder(p) if VISIT_PLACEHOLDER => { self.max_universe = self.max_universe.max(p.universe) }