use core::panic; use rustc_type_ir::data_structures::IndexMap; use rustc_type_ir::inherent::*; use rustc_type_ir::{ self as ty, InferCtxtLike, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; pub struct BoundVarReplacer<'a, Infcx, I = ::Interner> where Infcx: InferCtxtLike, I: Interner, { infcx: &'a Infcx, // These three maps track the bound variable that were replaced by placeholders. It might be // nice to remove these since we already have the `kind` in the placeholder; we really just need // the `var` (but we *could* bring that into scope if we were to track them as we pass them). mapped_regions: IndexMap, mapped_types: IndexMap, mapped_consts: IndexMap, // The current depth relative to *this* folding, *not* the entire normalization. In other words, // the depth of binders we've passed here. current_index: ty::DebruijnIndex, // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy: // we don't actually create a universe until we see a bound var we have to replace. universe_indices: &'a mut Vec>, } impl<'a, Infcx, I> BoundVarReplacer<'a, Infcx, I> where Infcx: InferCtxtLike, I: Interner, { /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that /// use a binding level above `universe_indices.len()`, we fail. pub fn replace_bound_vars>( infcx: &'a Infcx, universe_indices: &'a mut Vec>, value: T, ) -> ( T, IndexMap, IndexMap, IndexMap, ) { let mut replacer = BoundVarReplacer { infcx, mapped_regions: Default::default(), mapped_types: Default::default(), mapped_consts: Default::default(), current_index: ty::INNERMOST, universe_indices, }; let value = value.fold_with(&mut replacer); (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts) } fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex { let infcx = self.infcx; let index = self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1; let universe = self.universe_indices[index].unwrap_or_else(|| { for i in self.universe_indices.iter_mut().take(index + 1) { *i = i.or_else(|| Some(infcx.create_next_universe())) } self.universe_indices[index].unwrap() }); universe } } impl TypeFolder for BoundVarReplacer<'_, Infcx, I> where Infcx: InferCtxtLike, I: Interner, { fn cx(&self) -> I { self.infcx.cx() } fn fold_binder>(&mut self, t: ty::Binder) -> ty::Binder { self.current_index.shift_in(1); let t = t.super_fold_with(self); self.current_index.shift_out(1); t } fn fold_region(&mut self, r: I::Region) -> I::Region { match r.kind() { ty::ReBound(debruijn, _) if debruijn.as_usize() >= self.current_index.as_usize() + self.universe_indices.len() => { panic!( "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}", self.universe_indices ); } ty::ReBound(debruijn, br) if debruijn >= self.current_index => { let universe = self.universe_for(debruijn); let p = PlaceholderLike::new(universe, br); self.mapped_regions.insert(p, br); Region::new_placeholder(self.cx(), p) } _ => r, } } fn fold_ty(&mut self, t: I::Ty) -> I::Ty { match t.kind() { ty::Bound(debruijn, _) if debruijn.as_usize() + 1 > self.current_index.as_usize() + self.universe_indices.len() => { panic!( "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}", self.universe_indices ); } ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { let universe = self.universe_for(debruijn); let p = PlaceholderLike::new(universe, bound_ty); self.mapped_types.insert(p, bound_ty); Ty::new_placeholder(self.cx(), p) } _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), _ => t, } } fn fold_const(&mut self, ct: I::Const) -> I::Const { match ct.kind() { ty::ConstKind::Bound(debruijn, _) if debruijn.as_usize() + 1 > self.current_index.as_usize() + self.universe_indices.len() => { panic!( "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}", self.universe_indices ); } ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => { let universe = self.universe_for(debruijn); let p = PlaceholderLike::new(universe, bound_const); self.mapped_consts.insert(p, bound_const); Const::new_placeholder(self.cx(), p) } _ => ct.super_fold_with(self), } } fn fold_predicate(&mut self, p: I::Predicate) -> I::Predicate { if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } } }