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 ea64b9c225c02..ec5d330222559 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1297,6 +1297,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), .. @@ -1322,6 +1323,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 f6a97f04d99e4..32eca365f4596 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -286,7 +286,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, @@ -313,13 +315,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 8dd38d2eaa407..50ad060cac6f1 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 4fe57994057cc..ec9daf13f780f 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 4d90e60736995..b34d8443fbb46 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -97,7 +97,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 { @@ -118,23 +118,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<'tcx, ty::TyVid> { 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<'tcx, ty::IntVid> { 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<'tcx, ty::FloatVid> { 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<'tcx, ty::ConstVid> { 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<'tcx, ty::RegionVid> { self.inner .borrow_mut() .unwrap_region_constraints() @@ -449,7 +458,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), @@ -481,8 +490,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 0e3292fc0c497..939d7bcb1931d 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -29,9 +29,9 @@ use rustc_middle::traits::solve::Goal; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{ self, BoundVarReplacerDelegate, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, - GenericArgsRef, GenericParamDefKind, InferConst, OpaqueTypeKey, ProvisionalHiddenType, - PseudoCanonicalInput, Term, Ty, TyCtxt, TyVid, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitable, TypeVisitableExt, TypingEnv, TypingMode, fold_regions, + GenericArgsRef, 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}; use rustc_type_ir::{CanonicalizerState, MayBeErased}; @@ -1226,24 +1226,154 @@ 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<'tcx, TyVid> { + 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(self.tcx, v, root_vid) + } + } + } + /// 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<'tcx, IntVid> { + let (root, value) = + self.inner.borrow_mut().int_unification_table().inlined_probe_key_value(v); match value { - TypeVariableValue::Known { value } => Ok(value), - TypeVariableValue::Unknown { universe: _ } => Err(root), + 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(self.tcx, v, root), + } + } + + /// 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<'tcx, FloatVid> { + let (root, value) = + self.inner.borrow_mut().float_unification_table().inlined_probe_key_value(v); + match value { + ty::FloatVarValue::Known(ty) => { + ty::MaybeResolved::resolved(Ty::new_float(self.tcx, ty)) + } + ty::FloatVarValue::Unknown => ty::MaybeResolved::unresolved(self.tcx, v, root), + } + } + + /// 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<'tcx, ConstVid> { + 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(self.tcx, v, root.vid) + } + } + } + + /// 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 } } @@ -1267,68 +1397,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. @@ -1336,19 +1405,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(_) @@ -1368,7 +1427,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) } @@ -1395,51 +1454,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, @@ -1471,6 +1485,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 c17933d2629a5..7130c5c2d787c 100644 --- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -676,12 +676,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<'tcx, ty::RegionVid> { + 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(tcx, vid, root_vid.vid) + } } } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 35d04597451c0..7e0db48e86331 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -288,12 +288,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_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 1ced1b336c817..7b25fd009ae16 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -104,7 +104,8 @@ impl<'a, 'tcx> TypeFolder> for DeepRegionResolver<'a, 'tcx> { .inner .borrow_mut() .unwrap_region_constraints() - .shallow_resolve_region_var(TypeFolder::cx(self), vid), + .shallow_resolve_region_var(TypeFolder::cx(self), vid) + .reuse_if_unchanged(r), _ => r, } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index a7a64fe7cb964..b4168ef2fe5da 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -162,6 +162,8 @@ mod structural_impls; mod typeck_results; mod visit; +pub type MaybeResolved<'tcx, T> = rustc_type_ir::MaybeResolved, T>; + // 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 f2c70ffd37ef3..177749fb6df28 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -942,6 +942,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 f0f0ebaf2b3bb..fc2306bd2f5ee 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -321,9 +321,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" ); @@ -338,17 +337,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 @@ -488,9 +485,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 { @@ -533,9 +529,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 84811a101fb11..26745c9792ec1 100644 --- a/compiler/rustc_next_trait_solver/src/placeholder.rs +++ b/compiler/rustc_next_trait_solver/src/placeholder.rs @@ -248,7 +248,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 c5665246710a3..3b3f973536c9b 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 @@ -1313,7 +1313,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 5605325309275..682ca01b698c1 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -638,7 +638,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 a86a07edb902f..586ff60448091 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -11,7 +11,7 @@ use crate::relate::RelateResult; use crate::relate::combine::PredicateEmittingRelation; use crate::solve::{TyOrConstInferVar, VisibleForLeakCheck}; use crate::{ - self as ty, Interner, PredicateProxy, Region, TyVid, TypeFoldable, TypeFolder, + self as ty, Interner, PredicateProxy, Region, RegionVid, TyVid, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; @@ -366,6 +366,90 @@ impl From> for TypingMode: Eq { + type Output; + fn intern(self, cx: I) -> Self::Output; +} +impl InternAfterResolve for RegionVid { + type Output = Region; + + fn intern(self, cx: I) -> Self::Output { + Region::new_var(cx, self) + } +} +impl InternAfterResolve for ty::IntVid { + type Output = I::Ty; + + fn intern(self, cx: I) -> Self::Output { + I::Ty::new_int_var(cx, self) + } +} +impl InternAfterResolve for ty::FloatVid { + type Output = I::Ty; + + fn intern(self, cx: I) -> Self::Output { + I::Ty::new_float_var(cx, self) + } +} +impl InternAfterResolve for TyVid { + type Output = I::Ty; + + fn intern(self, cx: I) -> Self::Output { + I::Ty::new_var(cx, self) + } +} +impl InternAfterResolve for ty::ConstVid { + type Output = I::Const; + + fn intern(self, cx: I) -> Self::Output { + I::Const::new_var(cx, self) + } +} + +pub enum MaybeResolved> { + Resolved(T::Output), + Unchanged { cx: I, root: T }, +} + +impl> MaybeResolved { + pub fn unresolved(cx: I, before: T, after: T) -> Self { + if before == after { + // If unchanged, mark as such so we can reuse. + Self::Unchanged { cx, root: after } + } else { + // If changed, immediately intern it, no reuse is possible anyway. + Self::Resolved(after.intern(cx)) + } + } + + pub fn resolved(r: T::Output) -> Self { + Self::Resolved(r) + } + + pub fn is_unchanged(&self) -> bool { + match self { + MaybeResolved::Resolved(_) => false, + MaybeResolved::Unchanged { .. } => true, + } + } + + #[inline] + pub fn intern(self) -> T::Output { + match self { + MaybeResolved::Resolved(r) => r, + MaybeResolved::Unchanged { cx, root: inp } => inp.intern(cx), + } + } + + #[inline] + pub fn reuse_if_unchanged(self, before: T::Output) -> T::Output { + match self { + MaybeResolved::Resolved(r) => r, + MaybeResolved::Unchanged { cx: _, root: _ } => 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 @@ -431,11 +515,21 @@ 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; + fn shallow_resolve_int_var(&self, vid: ty::IntVid) + -> MaybeResolved; + fn shallow_resolve_float_var( + &self, + vid: ty::FloatVid, + ) -> MaybeResolved; + 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; @@ -663,15 +757,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) { @@ -689,7 +787,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, } } @@ -697,7 +795,7 @@ impl, I: Interner> TypeFolder fn fold_const(&mut self, c: I::Const) -> I::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 bf90ef707c051..7a0a1cbf83817 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -42,6 +42,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 1f38edd78023d..84860ae7b55dc 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) }