diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index ecf67f0614884..da68ad5e5d502 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -1,14 +1,12 @@ //! Definition of `InferCtxtLike` from the librarified type layer. -use rustc_data_structures::sso::SsoHashMap; use rustc_hir::def_id::DefId; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::relate::RelateResult; use rustc_middle::ty::relate::combine::PredicateEmittingRelation; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; -use rustc_type_ir::{TypeSuperFoldable, TypeVisitableExt}; +use rustc_type_ir::lower_universe; -use super::type_variable::TypeVariableValue; use super::{ BoundRegionConversionTime, ConstVariableValue, InferCtxt, OpaqueTypeStorageEntries, RegionVariableOrigin, SubregionOrigin, @@ -100,6 +98,58 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { } } + fn universe_of_region(&self, region: ty::Region<'tcx>) -> ty::UniverseIndex { + InferCtxt::universe_of_region(self, region) + } + + fn lower_ty_var_to_universe(&self, vid: ty::TyVid, universe: ty::UniverseIndex) -> Ty<'tcx> { + let vid = self.root_var(vid); + let mut inner = self.inner.borrow_mut(); + let origin = inner.type_variables().var_origin(vid); + let new_var_id = inner.type_variables().new_var(universe, origin); + inner.type_variables().equate(vid, new_var_id); + + Ty::new_var(self.tcx, new_var_id) + } + + fn lower_const_var_to_universe( + &self, + vid: ty::ConstVid, + universe: ty::UniverseIndex, + ) -> ty::Const<'tcx> { + let vid = self.root_const_var(vid); + let origin = self.const_var_origin(vid).unwrap(); + + let mut inner = self.inner.borrow_mut(); + let new_var_id = inner + .const_unification_table() + .new_key(ConstVariableValue::Unknown { origin, universe }) + .vid; + + inner.const_unification_table().union(vid, new_var_id); + + ty::Const::new_var(self.tcx, new_var_id) + } + + fn lower_region_to_universe( + &self, + region: ty::Region<'tcx>, + universe: ty::UniverseIndex, + ) -> ty::Region<'tcx> { + let new_region = + self.next_region_var_in_universe(RegionVariableOrigin::Misc(DUMMY_SP), universe); + + InferCtxt::equate_regions( + self, + SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None), + region, + new_region, + ty::VisibleForLeakCheck::Yes, + ); + + new_region + } + fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid { self.root_var(var) } @@ -437,149 +487,3 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { let _ = self.take_opaque_types(); } } - -fn lower_universe<'tcx, T: TypeFoldable> + Copy>( - infcx: &InferCtxt<'tcx>, - for_universe: ty::UniverseIndex, - value: T, -) -> T { - let value = value.fold_with(&mut LowerUniverseFolder { - infcx, - for_universe, - cache: Default::default(), - }); - - // This assertion is needed because we don't lower the universes of placeholders - // in the folder. - #[cfg(debug_assertions)] - { - let value_universe = ty::max_universe(infcx, value); - assert!( - for_universe.can_name(value_universe), - "variable in universe {:?} can't name value in universe {:?}", - for_universe, - value_universe, - ); - } - - value -} - -/// Canonicalizing inputs puts all inference variables and placeholders -/// into the root universe. -/// -/// This means when instantiating the query response we need to pull -/// down the universe of returned `var_values` to the universe of -/// the inference variable in `orig_values`. -/// -/// This folder is similar to the `Generalizer`, except that it simply -/// structurally folds non-rigid aliases as these should have already -/// been generalized in the query so we shouldn't try to do it again. -struct LowerUniverseFolder<'a, 'tcx> { - infcx: &'a InferCtxt<'tcx>, - for_universe: ty::UniverseIndex, - cache: SsoHashMap, Ty<'tcx>>, -} -impl<'a, 'tcx> ty::TypeFolder> for LowerUniverseFolder<'a, 'tcx> { - fn cx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - if !(t.has_free_regions() || t.has_infer()) { - return t; - } - - if let Some(&answer) = self.cache.get(&t) { - return answer; - } - - let folded = match t.kind() { - ty::Infer(ty::TyVar(vid)) => { - let vid = self.infcx.root_var(*vid); - let probe = self.infcx.inner.borrow_mut().type_variables().probe(vid); - match probe { - TypeVariableValue::Known { value: u } => u.super_fold_with(self), - TypeVariableValue::Unknown { universe } => { - if self.for_universe.can_name(universe) { - t - } else { - let mut inner = self.infcx.inner.borrow_mut(); - let origin = inner.type_variables().var_origin(vid); - let new_var_id = - inner.type_variables().new_var(self.for_universe, origin); - inner.type_variables().equate(vid, new_var_id); - Ty::new_var(self.cx(), new_var_id) - } - } - } - } - _ => t.super_fold_with(self), - }; - - self.cache.insert(t, folded); - folded - } - - fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { - if !(c.has_free_regions() || c.has_infer()) { - return c; - } - - 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) { - Ok(value) => return value.fold_with(self), - Err(universe) => universe, - }; - if self.for_universe.can_name(universe) { - c - } else { - let origin = self.infcx.const_var_origin(vid).unwrap(); - let new_var_id = self - .infcx - .inner - .borrow_mut() - .const_unification_table() - .new_key(ConstVariableValue::Unknown { - origin, - universe: self.for_universe, - }) - .vid; - - self.infcx.inner.borrow_mut().const_unification_table().union(vid, new_var_id); - - ty::Const::new_var(self.cx(), new_var_id) - } - } - _ => c.super_fold_with(self), - } - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match r.kind() { - ty::ReBound(..) | ty::ReErased => r, - _ => { - let r_universe = self.infcx.universe_of_region(r); - if self.for_universe.can_name(r_universe) { - r - } else { - // FIXME: unfortunately we lose the relating span here unless we take another - // argument. - let new_region = self.infcx.next_region_var_in_universe( - RegionVariableOrigin::Misc(DUMMY_SP), - self.for_universe, - ); - self.infcx.equate_regions( - SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None), - r, - new_region, - ty::VisibleForLeakCheck::Yes, - ); - new_region - } - } - } - } -} diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index d8c11542be4a3..3383bc77c3bc3 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -405,6 +405,26 @@ pub trait InferCtxtLike: Sized { fn universe_of_lt(&self, lt: ty::RegionVid) -> Option; fn universe_of_ct(&self, ct: ty::ConstVid) -> Option; + fn universe_of_region(&self, region: Region) -> ty::UniverseIndex; + + fn lower_ty_var_to_universe( + &self, + vid: ty::TyVid, + universe: ty::UniverseIndex, + ) -> ::Ty; + + fn lower_const_var_to_universe( + &self, + vid: ty::ConstVid, + universe: ty::UniverseIndex, + ) -> ::Const; + + fn lower_region_to_universe( + &self, + region: Region, + universe: ty::UniverseIndex, + ) -> Region; + fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid; fn sub_unification_table_root_var(&self, var: ty::TyVid) -> ty::TyVid; fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid; diff --git a/compiler/rustc_type_ir/src/universe.rs b/compiler/rustc_type_ir/src/universe.rs index a5a4b2c02be89..b67a5456ffc38 100644 --- a/compiler/rustc_type_ir/src/universe.rs +++ b/compiler/rustc_type_ir/src/universe.rs @@ -1,11 +1,12 @@ use tracing::{debug, instrument}; -use crate::data_structures::HashSet; +use crate::data_structures::{HashMap, HashSet}; use crate::inherent::*; use crate::visit::TypeVisitableExt; use crate::{ - ConstKind, InferCtxtLike, InferTy, Interner, Region, RegionKind, TyKind, TypeFoldable, - TypeSuperVisitable, TypeVisitable, TypeVisitor, UniverseIndex, + ConstKind, InferConst, InferCtxtLike, InferTy, Interner, Region, RegionKind, TyKind, + TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, + UniverseIndex, }; /// The largest universe a variable or placeholder was from in `t` @@ -170,3 +171,140 @@ impl< } } } + +/// Lowers inference variables in `value` so they can be named by `for_universe`. +/// +/// Canonicalizing inputs puts all inference variables and placeholders into the +/// root universe. When instantiating a query response, variables therefore need +/// to be moved back into a universe nameable by the original inference variable. +pub fn lower_universe< + Infcx: InferCtxtLike, + I: Interner, + T: TypeFoldable + Copy, +>( + infcx: &Infcx, + for_universe: UniverseIndex, + value: T, +) -> T { + let value = value.fold_with(&mut LowerUniverseFolder { + infcx, + for_universe, + cache: Default::default(), + }); + + // Placeholders are intentionally not lowered by `LowerUniverseFolder`. + #[cfg(debug_assertions)] + { + let value_universe = max_universe(infcx, value); + assert!( + for_universe.can_name(value_universe), + "variable in universe {:?} can't name value in universe {:?}", + for_universe, + value_universe, + ); + } + + value +} + +/// Canonicalizing inputs puts all inference variables and placeholders +/// into the root universe. +/// +/// This means when instantiating the query response we need to pull +/// down the universe of returned `var_values` to the universe of +/// the inference variable in `orig_values`. +/// +/// This folder is similar to the `Generalizer`, except that it simply +/// structurally folds non-rigid aliases as these should have already +/// been generalized in the query so we shouldn't try to do it again. +struct LowerUniverseFolder<'a, Infcx: InferCtxtLike, I: Interner> { + infcx: &'a Infcx, + for_universe: UniverseIndex, + cache: HashMap, +} + +impl, I: Interner> TypeFolder + for LowerUniverseFolder<'_, Infcx, I> +{ + fn cx(&self) -> I { + self.infcx.cx() + } + + fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + if !(ty.has_free_regions() || ty.has_infer()) { + return ty; + } + + if let Some(&result) = self.cache.get(&ty) { + return result; + } + + let folded = match ty.kind() { + TyKind::Infer(InferTy::TyVar(vid)) => { + let vid = self.infcx.root_ty_var(vid); + let resolved = self.infcx.opportunistic_resolve_ty_var(vid); + + match resolved.kind() { + TyKind::Infer(InferTy::TyVar(resolved_vid)) if resolved_vid == vid => { + let universe = self.infcx.universe_of_ty(vid).unwrap(); + + if self.for_universe.can_name(universe) { + ty + } else { + self.infcx.lower_ty_var_to_universe(vid, self.for_universe) + } + } + _ => resolved.super_fold_with(self), + } + } + _ => ty.super_fold_with(self), + }; + + self.cache.insert(ty, folded); + folded + } + + fn fold_const(&mut self, ct: I::Const) -> I::Const { + if !(ct.has_free_regions() || ct.has_infer()) { + return ct; + } + + match ct.kind() { + ConstKind::Infer(InferConst::Var(vid)) => { + let vid = self.infcx.root_const_var(vid); + let resolved = self.infcx.opportunistic_resolve_ct_var(vid); + + match resolved.kind() { + ConstKind::Infer(InferConst::Var(resolved_vid)) if resolved_vid == vid => { + let universe = self.infcx.universe_of_ct(vid).unwrap(); + + if self.for_universe.can_name(universe) { + ct + } else { + self.infcx.lower_const_var_to_universe(vid, self.for_universe) + } + } + _ => resolved.fold_with(self), + } + } + _ => ct.super_fold_with(self), + } + } + + fn fold_region(&mut self, region: Region) -> Region { + match region.kind() { + RegionKind::ReBound(..) | RegionKind::ReErased => region, + _ => { + let universe = self.infcx.universe_of_region(region); + + if self.for_universe.can_name(universe) { + region + } else { + // FIXME: unfortunately we lose the relating span here unless we take another + // argument. + self.infcx.lower_region_to_universe(region, self.for_universe) + } + } + } + } +}