Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/next_solver/fold.rs')
-rw-r--r--crates/hir-ty/src/next_solver/fold.rs109
1 files changed, 100 insertions, 9 deletions
diff --git a/crates/hir-ty/src/next_solver/fold.rs b/crates/hir-ty/src/next_solver/fold.rs
index 405a57d9e8..7836419e8b 100644
--- a/crates/hir-ty/src/next_solver/fold.rs
+++ b/crates/hir-ty/src/next_solver/fold.rs
@@ -1,12 +1,11 @@
//! Fold impls for the next-trait-solver.
use rustc_type_ir::{
- BoundVar, DebruijnIndex, RegionKind, TypeFoldable, TypeFolder, TypeSuperFoldable,
- TypeVisitableExt,
- inherent::{IntoKind, Region as _},
+ BoundVarIndexKind, DebruijnIndex, RegionKind, TypeFoldable, TypeFolder, TypeSuperFoldable,
+ TypeVisitableExt, inherent::IntoKind,
};
-use crate::next_solver::BoundConst;
+use crate::next_solver::{BoundConst, FxIndexMap};
use super::{
Binder, BoundRegion, BoundTy, Const, ConstKind, DbInterner, Predicate, Region, Ty, TyKind,
@@ -55,7 +54,7 @@ pub(crate) struct BoundVarReplacer<'db, D> {
}
impl<'db, D: BoundVarReplacerDelegate<'db>> BoundVarReplacer<'db, D> {
- pub fn new(tcx: DbInterner<'db>, delegate: D) -> Self {
+ pub(crate) fn new(tcx: DbInterner<'db>, delegate: D) -> Self {
BoundVarReplacer { interner: tcx, current_index: DebruijnIndex::ZERO, delegate }
}
}
@@ -80,7 +79,9 @@ where
fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
match t.kind() {
- TyKind::Bound(debruijn, bound_ty) if debruijn == self.current_index => {
+ TyKind::Bound(BoundVarIndexKind::Bound(debruijn), bound_ty)
+ if debruijn == self.current_index =>
+ {
let ty = self.delegate.replace_ty(bound_ty);
debug_assert!(!ty.has_vars_bound_above(DebruijnIndex::ZERO));
rustc_type_ir::shift_vars(self.interner, ty, self.current_index.as_u32())
@@ -97,9 +98,12 @@ where
fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
match r.kind() {
- RegionKind::ReBound(debruijn, br) if debruijn == self.current_index => {
+ RegionKind::ReBound(BoundVarIndexKind::Bound(debruijn), br)
+ if debruijn == self.current_index =>
+ {
let region = self.delegate.replace_region(br);
- if let RegionKind::ReBound(debruijn1, br) = region.kind() {
+ if let RegionKind::ReBound(BoundVarIndexKind::Bound(debruijn1), br) = region.kind()
+ {
// If the callback returns a bound region,
// that region should always use the INNERMOST
// debruijn index. Then we adjust it to the
@@ -116,7 +120,9 @@ where
fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
match ct.kind() {
- ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => {
+ ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), bound_const)
+ if debruijn == self.current_index =>
+ {
let ct = self.delegate.replace_const(bound_const);
debug_assert!(!ct.has_vars_bound_above(DebruijnIndex::ZERO));
rustc_type_ir::shift_vars(self.interner, ct, self.current_index.as_u32())
@@ -129,3 +135,88 @@ where
if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
}
}
+
+pub fn fold_tys<'db, T: TypeFoldable<DbInterner<'db>>>(
+ interner: DbInterner<'db>,
+ t: T,
+ callback: impl FnMut(Ty<'db>) -> Ty<'db>,
+) -> T {
+ struct Folder<'db, F> {
+ interner: DbInterner<'db>,
+ callback: F,
+ }
+ impl<'db, F: FnMut(Ty<'db>) -> Ty<'db>> TypeFolder<DbInterner<'db>> for Folder<'db, F> {
+ fn cx(&self) -> DbInterner<'db> {
+ self.interner
+ }
+
+ fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
+ let t = t.super_fold_with(self);
+ (self.callback)(t)
+ }
+ }
+
+ t.fold_with(&mut Folder { interner, callback })
+}
+
+impl<'db> DbInterner<'db> {
+ /// Replaces all regions bound by the given `Binder` with the
+ /// results returned by the closure; the closure is expected to
+ /// return a free region (relative to this binder), and hence the
+ /// binder is removed in the return type. The closure is invoked
+ /// once for each unique `BoundRegionKind`; multiple references to the
+ /// same `BoundRegionKind` will reuse the previous result. A map is
+ /// returned at the end with each bound region and the free region
+ /// that replaced it.
+ ///
+ /// # Panics
+ ///
+ /// This method only replaces late bound regions. Any types or
+ /// constants bound by `value` will cause an ICE.
+ pub fn instantiate_bound_regions<T, F>(
+ self,
+ value: Binder<'db, T>,
+ mut fld_r: F,
+ ) -> (T, FxIndexMap<BoundRegion, Region<'db>>)
+ where
+ F: FnMut(BoundRegion) -> Region<'db>,
+ T: TypeFoldable<DbInterner<'db>>,
+ {
+ let mut region_map = FxIndexMap::default();
+ let real_fld_r = |br: BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br));
+ let value = self.instantiate_bound_regions_uncached(value, real_fld_r);
+ (value, region_map)
+ }
+
+ pub fn instantiate_bound_regions_uncached<T, F>(
+ self,
+ value: Binder<'db, T>,
+ mut replace_regions: F,
+ ) -> T
+ where
+ F: FnMut(BoundRegion) -> Region<'db>,
+ T: TypeFoldable<DbInterner<'db>>,
+ {
+ let value = value.skip_binder();
+ if !value.has_escaping_bound_vars() {
+ value
+ } else {
+ let delegate = FnMutDelegate {
+ regions: &mut replace_regions,
+ types: &mut |b| panic!("unexpected bound ty in binder: {b:?}"),
+ consts: &mut |b| panic!("unexpected bound ct in binder: {b:?}"),
+ };
+ let mut replacer = BoundVarReplacer::new(self, delegate);
+ value.fold_with(&mut replacer)
+ }
+ }
+
+ /// Replaces any late-bound regions bound in `value` with `'erased`. Useful in codegen but also
+ /// method lookup and a few other places where precise region relationships are not required.
+ pub fn instantiate_bound_regions_with_erased<T>(self, value: Binder<'db, T>) -> T
+ where
+ T: TypeFoldable<DbInterner<'db>>,
+ {
+ self.instantiate_bound_regions(value, |_| Region::new_erased(self)).0
+ }
+}