about summary refs log tree commit diff
path: root/src/librustc/ty/fold.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustc/ty/fold.rs')
-rw-r--r--src/librustc/ty/fold.rs300
1 files changed, 208 insertions, 92 deletions
diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs
index f54dcfa37e9..a897afa0ca6 100644
--- a/src/librustc/ty/fold.rs
+++ b/src/librustc/ty/fold.rs
@@ -67,22 +67,22 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
     /// bound by `binder` or bound by some binder outside of `binder`.
     /// If `binder` is `ty::INNERMOST`, this indicates whether
     /// there are any late-bound regions that appear free.
-    fn has_regions_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool {
-        self.visit_with(&mut HasEscapingRegionsVisitor { outer_index: binder })
+    fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool {
+        self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder })
     }
 
     /// True if this `self` has any regions that escape `binder` (and
     /// hence are not bound by it).
-    fn has_regions_bound_above(&self, binder: ty::DebruijnIndex) -> bool {
-        self.has_regions_bound_at_or_above(binder.shifted_in(1))
+    fn has_vars_bound_above(&self, binder: ty::DebruijnIndex) -> bool {
+        self.has_vars_bound_at_or_above(binder.shifted_in(1))
     }
 
-    fn has_escaping_regions(&self) -> bool {
-        self.has_regions_bound_at_or_above(ty::INNERMOST)
+    fn has_escaping_bound_vars(&self) -> bool {
+        self.has_vars_bound_at_or_above(ty::INNERMOST)
     }
 
     fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        self.visit_with(&mut HasTypeFlagsVisitor { flags: flags })
+        self.visit_with(&mut HasTypeFlagsVisitor { flags })
     }
     fn has_projections(&self) -> bool {
         self.has_type_flags(TypeFlags::HAS_PROJECTION)
@@ -416,11 +416,10 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionFolder<'a, 'gcx, 'tcx> {
 }
 
 ///////////////////////////////////////////////////////////////////////////
-// Late-bound region replacer
+// Bound vars replacer
 
-// Replaces the escaping regions in a type.
-
-struct RegionReplacer<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
+/// Replaces the escaping bound vars (late bound regions or bound types) in a type.
+struct BoundVarReplacer<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
     tcx: TyCtxt<'a, 'gcx, 'tcx>,
 
     /// As with `RegionFolder`, represents the index of a binder *just outside*
@@ -428,7 +427,82 @@ struct RegionReplacer<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     current_index: ty::DebruijnIndex,
 
     fld_r: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a),
-    map: BTreeMap<ty::BoundRegion, ty::Region<'tcx>>
+    fld_t: &'a mut (dyn FnMut(ty::BoundTy) -> ty::Ty<'tcx> + 'a),
+}
+
+impl<'a, 'gcx, 'tcx> BoundVarReplacer<'a, 'gcx, 'tcx> {
+    fn new<F, G>(
+        tcx: TyCtxt<'a, 'gcx, 'tcx>,
+        fld_r: &'a mut F,
+        fld_t: &'a mut G
+    ) -> Self
+        where F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
+              G: FnMut(ty::BoundTy) -> ty::Ty<'tcx>
+    {
+        BoundVarReplacer {
+            tcx,
+            current_index: ty::INNERMOST,
+            fld_r,
+            fld_t,
+        }
+    }
+}
+
+impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for BoundVarReplacer<'a, 'gcx, 'tcx> {
+    fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { self.tcx }
+
+    fn fold_binder<T: TypeFoldable<'tcx>>(&mut self, t: &ty::Binder<T>) -> ty::Binder<T> {
+        self.current_index.shift_in(1);
+        let t = t.super_fold_with(self);
+        self.current_index.shift_out(1);
+        t
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        match t.sty {
+            ty::Bound(bound_ty) => {
+                if bound_ty.index == self.current_index {
+                    let fld_t = &mut self.fld_t;
+                    let ty = fld_t(bound_ty);
+                    ty::fold::shift_vars(
+                        self.tcx,
+                        &ty,
+                        self.current_index.as_u32()
+                    )
+                } else {
+                    t
+                }
+            }
+            _ => {
+                if !t.has_vars_bound_at_or_above(self.current_index) {
+                    // Nothing more to substitute.
+                    t
+                } else {
+                    t.super_fold_with(self)
+                }
+            }
+        }
+    }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        match *r {
+            ty::ReLateBound(debruijn, br) if debruijn == self.current_index => {
+                let fld_r = &mut self.fld_r;
+                let region = fld_r(br);
+                if let ty::ReLateBound(debruijn1, br) = *region {
+                    // If the callback returns a late-bound region,
+                    // that region should always use the INNERMOST
+                    // debruijn index. Then we adjust it to the
+                    // correct depth.
+                    assert_eq!(debruijn1, ty::INNERMOST);
+                    self.tcx.mk_region(ty::ReLateBound(debruijn, br))
+                } else {
+                    region
+                }
+            }
+            _ => r
+        }
+    }
 }
 
 impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
@@ -440,16 +514,63 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     /// same `BoundRegion` will reuse the previous result.  A map is
     /// returned at the end with each bound region and the free region
     /// that replaced it.
-    pub fn replace_late_bound_regions<T,F>(self,
+    ///
+    /// This method only replaces late bound regions and the result may still
+    /// contain escaping bound types.
+    pub fn replace_late_bound_regions<T, F>(
+        self,
+        value: &Binder<T>,
+        fld_r: F
+    ) -> (T, BTreeMap<ty::BoundRegion, ty::Region<'tcx>>)
+        where F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
+              T: TypeFoldable<'tcx>
+    {
+        // identity for bound types
+        let fld_t = |bound_ty| self.mk_ty(ty::Bound(bound_ty));
+        self.replace_escaping_bound_vars(value.skip_binder(), fld_r, fld_t)
+    }
+
+    /// Replace all escaping bound vars. The `fld_r` closure replaces escaping
+    /// bound regions while the `fld_t` closure replaces escaping bound types.
+    pub fn replace_escaping_bound_vars<T, F, G>(
+        self,
+        value: &T,
+        mut fld_r: F,
+        mut fld_t: G
+    ) -> (T, BTreeMap<ty::BoundRegion, ty::Region<'tcx>>)
+        where F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
+              G: FnMut(ty::BoundTy) -> ty::Ty<'tcx>,
+              T: TypeFoldable<'tcx>
+    {
+        let mut map = BTreeMap::new();
+
+        if !value.has_escaping_bound_vars() {
+            (value.clone(), map)
+        } else {
+            let mut real_fld_r = |br| {
+                *map.entry(br).or_insert_with(|| fld_r(br))
+            };
+
+            let mut replacer = BoundVarReplacer::new(self, &mut real_fld_r, &mut fld_t);
+            let result = value.fold_with(&mut replacer);
+            (result, map)
+        }
+    }
+
+    /// Replace all types or regions bound by the given `Binder`. The `fld_r`
+    /// closure replaces bound regions while the `fld_t` closure replaces bound
+    /// types.
+    pub fn replace_bound_vars<T, F, G>(
+        self,
         value: &Binder<T>,
-        mut f: F)
-        -> (T, BTreeMap<ty::BoundRegion, ty::Region<'tcx>>)
-        where F : FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
-              T : TypeFoldable<'tcx>,
+        fld_r: F,
+        fld_t: G
+    ) -> (T, BTreeMap<ty::BoundRegion, ty::Region<'tcx>>)
+        where F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
+              G: FnMut(ty::BoundTy) -> ty::Ty<'tcx>,
+              T: TypeFoldable<'tcx>
     {
-        let mut replacer = RegionReplacer::new(self, &mut f);
-        let result = value.skip_binder().fold_with(&mut replacer);
-        (result, replacer.map)
+        self.replace_escaping_bound_vars(value.skip_binder(), fld_r, fld_t)
     }
 
     /// Replace any late-bound regions bound in `value` with
@@ -549,21 +670,33 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     }
 }
 
-impl<'a, 'gcx, 'tcx> RegionReplacer<'a, 'gcx, 'tcx> {
-    fn new<F>(tcx: TyCtxt<'a, 'gcx, 'tcx>, fld_r: &'a mut F)
-              -> RegionReplacer<'a, 'gcx, 'tcx>
-        where F : FnMut(ty::BoundRegion) -> ty::Region<'tcx>
-    {
-        RegionReplacer {
+///////////////////////////////////////////////////////////////////////////
+// Shifter
+//
+// Shifts the De Bruijn indices on all escaping bound vars by a
+// fixed amount. Useful in substitution or when otherwise introducing
+// a binding level that is not intended to capture the existing bound
+// vars. See comment on `shift_vars_through_binders` method in
+// `subst.rs` for more details.
+
+struct Shifter<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
+
+    current_index: ty::DebruijnIndex,
+    amount: u32,
+}
+
+impl Shifter<'a, 'gcx, 'tcx> {
+    pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, amount: u32) -> Self {
+        Shifter {
             tcx,
             current_index: ty::INNERMOST,
-            fld_r,
-            map: BTreeMap::default()
+            amount,
         }
     }
 }
 
-impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionReplacer<'a, 'gcx, 'tcx> {
+impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Shifter<'a, 'gcx, 'tcx> {
     fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> { self.tcx }
 
     fn fold_binder<T: TypeFoldable<'tcx>>(&mut self, t: &ty::Binder<T>) -> ty::Binder<T> {
@@ -573,64 +706,48 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionReplacer<'a, 'gcx, 'tcx> {
         t
     }
 
-    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        if !t.has_regions_bound_at_or_above(self.current_index) {
-            return t;
-        }
-
-        t.super_fold_with(self)
-    }
-
     fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
         match *r {
-            ty::ReLateBound(debruijn, br) if debruijn == self.current_index => {
-                let fld_r = &mut self.fld_r;
-                let region = *self.map.entry(br).or_insert_with(|| fld_r(br));
-                if let ty::ReLateBound(debruijn1, br) = *region {
-                    // If the callback returns a late-bound region,
-                    // that region should always use the INNERMOST
-                    // debruijn index. Then we adjust it to the
-                    // correct depth.
-                    assert_eq!(debruijn1, ty::INNERMOST);
-                    self.tcx.mk_region(ty::ReLateBound(debruijn, br))
+            ty::ReLateBound(debruijn, br) => {
+                if self.amount == 0 || debruijn < self.current_index {
+                    r
                 } else {
-                    region
+                    let shifted = ty::ReLateBound(debruijn.shifted_in(self.amount), br);
+                    self.tcx.mk_region(shifted)
                 }
             }
             _ => r
         }
     }
-}
 
-///////////////////////////////////////////////////////////////////////////
-// Region shifter
-//
-// Shifts the De Bruijn indices on all escaping bound regions by a
-// fixed amount. Useful in substitution or when otherwise introducing
-// a binding level that is not intended to capture the existing bound
-// regions. See comment on `shift_regions_through_binders` method in
-// `subst.rs` for more details.
+    fn fold_ty(&mut self, ty: ty::Ty<'tcx>) -> ty::Ty<'tcx> {
+        match ty.sty {
+            ty::Bound(bound_ty) => {
+                if self.amount == 0 || bound_ty.index < self.current_index {
+                    ty
+                } else {
+                    let shifted = ty::BoundTy {
+                        index: bound_ty.index.shifted_in(self.amount),
+                        var: bound_ty.var,
+                        kind: bound_ty.kind,
+                    };
+                    self.tcx.mk_ty(ty::Bound(shifted))
+                }
+            }
 
-pub fn shift_region(region: ty::RegionKind, amount: u32) -> ty::RegionKind {
-    match region {
-        ty::ReLateBound(debruijn, br) => {
-            ty::ReLateBound(debruijn.shifted_in(amount), br)
-        }
-        _ => {
-            region
+            _ => ty.super_fold_with(self),
         }
     }
 }
 
-pub fn shift_region_ref<'a, 'gcx, 'tcx>(
+pub fn shift_region<'a, 'gcx, 'tcx>(
     tcx: TyCtxt<'a, 'gcx, 'tcx>,
     region: ty::Region<'tcx>,
-    amount: u32)
-    -> ty::Region<'tcx>
-{
+    amount: u32
+) -> ty::Region<'tcx> {
     match region {
-        &ty::ReLateBound(debruijn, br) if amount > 0 => {
-            tcx.mk_region(ty::ReLateBound(debruijn.shifted_in(amount), br))
+        ty::ReLateBound(debruijn, br) if amount > 0 => {
+            tcx.mk_region(ty::ReLateBound(debruijn.shifted_in(amount), *br))
         }
         _ => {
             region
@@ -638,20 +755,19 @@ pub fn shift_region_ref<'a, 'gcx, 'tcx>(
     }
 }
 
-pub fn shift_regions<'a, 'gcx, 'tcx, T>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
-                                        amount: u32,
-                                        value: &T) -> T
-    where T: TypeFoldable<'tcx>
-{
-    debug!("shift_regions(value={:?}, amount={})",
+pub fn shift_vars<'a, 'gcx, 'tcx, T>(
+    tcx: TyCtxt<'a, 'gcx, 'tcx>,
+    value: &T,
+    amount: u32
+) -> T where T: TypeFoldable<'tcx> {
+    debug!("shift_vars(value={:?}, amount={})",
            value, amount);
 
-    value.fold_with(&mut RegionFolder::new(tcx, &mut false, &mut |region, _current_depth| {
-        shift_region_ref(tcx, region, amount)
-    }))
+    value.fold_with(&mut Shifter::new(tcx, amount))
 }
 
-/// An "escaping region" is a bound region whose binder is not part of `t`.
+/// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a
+/// bound region or a bound type.
 ///
 /// So, for example, consider a type like the following, which has two binders:
 ///
@@ -663,24 +779,24 @@ pub fn shift_regions<'a, 'gcx, 'tcx, T>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
 /// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner
 /// fn type*, that type has an escaping region: `'a`.
 ///
-/// Note that what I'm calling an "escaping region" is often just called a "free region". However,
-/// we already use the term "free region". It refers to the regions that we use to represent bound
-/// regions on a fn definition while we are typechecking its body.
+/// Note that what I'm calling an "escaping var" is often just called a "free var". However,
+/// we already use the term "free var". It refers to the regions or types that we use to represent
+/// bound regions or type params on a fn definition while we are typechecking its body.
 ///
 /// To clarify, conceptually there is no particular difference between
-/// an "escaping" region and a "free" region. However, there is a big
+/// an "escaping" var and a "free" var. However, there is a big
 /// difference in practice. Basically, when "entering" a binding
 /// level, one is generally required to do some sort of processing to
-/// a bound region, such as replacing it with a fresh/placeholder
-/// region, or making an entry in the environment to represent the
-/// scope to which it is attached, etc. An escaping region represents
-/// a bound region for which this processing has not yet been done.
-struct HasEscapingRegionsVisitor {
+/// a bound var, such as replacing it with a fresh/placeholder
+/// var, or making an entry in the environment to represent the
+/// scope to which it is attached, etc. An escaping var represents
+/// a bound var for which this processing has not yet been done.
+struct HasEscapingVarsVisitor {
     /// Anything bound by `outer_index` or "above" is escaping
     outer_index: ty::DebruijnIndex,
 }
 
-impl<'tcx> TypeVisitor<'tcx> for HasEscapingRegionsVisitor {
+impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor {
     fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &Binder<T>) -> bool {
         self.outer_index.shift_in(1);
         let result = t.super_visit_with(self);
@@ -693,7 +809,7 @@ impl<'tcx> TypeVisitor<'tcx> for HasEscapingRegionsVisitor {
         // `outer_index`, that means that `t` contains some content
         // bound at `outer_index` or above (because
         // `outer_exclusive_binder` is always 1 higher than the
-        // content in `t`). Therefore, `t` has some escaping regions.
+        // content in `t`). Therefore, `t` has some escaping vars.
         t.outer_exclusive_binder > self.outer_index
     }
 
@@ -753,7 +869,7 @@ impl LateBoundRegionsCollector {
     fn new(just_constrained: bool) -> Self {
         LateBoundRegionsCollector {
             current_index: ty::INNERMOST,
-            regions: FxHashSet(),
+            regions: Default::default(),
             just_constrained,
         }
     }