]> git.lizzy.rs Git - rust.git/commitdiff
add a higher-ranked match routine
authorNiko Matsakis <niko@alum.mit.edu>
Sat, 21 May 2016 12:16:07 +0000 (08:16 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Tue, 31 May 2016 23:44:05 +0000 (19:44 -0400)
Currently, when projecting out of a higher-ranked where-clause, we
instantiate all higher-ranked regions with lifetime variables. This is
unnecessary since the language rules ought to guarantee (modulo #32330)
that each of those higher-ranked regions is equated with some regions
from the input types. This routine figures out what those regions are
and just uses them. Also, since #32330 is not fully fixed, it detects
when we may have unconstrained variables and indicates that in its
return value.

src/librustc/infer/higher_ranked/mod.rs
src/librustc/infer/mod.rs

index 3459f48f5c432ecc58b4b9b97b1256f896f88a84..84b72d9be60a1d3f71d573e27615d695c5e88e5d 100644 (file)
@@ -15,6 +15,7 @@
             InferCtxt,
             LateBoundRegion,
             HigherRankedType,
+            SubregionOrigin,
             SkolemizationMap};
 use super::combine::CombineFields;
 use super::region_inference::{TaintDirections};
 use syntax::codemap::Span;
 use util::nodemap::{FnvHashMap, FnvHashSet};
 
+pub struct HrMatchResult<U> {
+    pub value: U,
+
+    /// Normally, when we do a higher-ranked match operation, we
+    /// expect all higher-ranked regions to be constrained as part of
+    /// the match operation. However, in the transition period for
+    /// #32330, it can happen that we sometimes have unconstrained
+    /// regions that get instantiated with fresh variables. In that
+    /// case, we collect the set of unconstrained bound regions here
+    /// and replace them with fresh variables.
+    pub unconstrained_regions: Vec<ty::BoundRegion>,
+}
+
 impl<'a, 'gcx, 'tcx> CombineFields<'a, 'gcx, 'tcx> {
     pub fn higher_ranked_sub<T>(&self, a: &Binder<T>, b: &Binder<T>)
                                 -> RelateResult<'tcx, Binder<T>>
@@ -79,6 +93,134 @@ pub fn higher_ranked_sub<T>(&self, a: &Binder<T>, b: &Binder<T>)
         });
     }
 
+    /// The value consists of a pair `(t, u)` where `t` is the
+    /// *matcher* and `u` is a *value*. The idea is to find a
+    /// substitution `S` such that `S(t) == b`, and then return
+    /// `S(u)`. In other words, find values for the late-bound regions
+    /// in `a` that can make `t == b` and then replace the LBR in `u`
+    /// with those values.
+    ///
+    /// This routine is (as of this writing) used in trait matching,
+    /// particularly projection.
+    ///
+    /// NB. It should not happen that there are LBR appearing in `U`
+    /// that do not appear in `T`. If that happens, those regions are
+    /// unconstrained, and this routine replaces them with `'static`.
+    pub fn higher_ranked_match<T, U>(&self,
+                                     span: Span,
+                                     a_pair: &Binder<(T, U)>,
+                                     b_match: &T)
+                                     -> RelateResult<'tcx, HrMatchResult<U>>
+        where T: Relate<'tcx>,
+              U: TypeFoldable<'tcx>
+    {
+        debug!("higher_ranked_match(a={:?}, b={:?})",
+               a_pair, b_match);
+
+        // Start a snapshot so we can examine "all bindings that were
+        // created as part of this type comparison".
+        return self.infcx.commit_if_ok(|snapshot| {
+            // First, we instantiate each bound region in the matcher
+            // with a skolemized region.
+            let ((a_match, a_value), skol_map) =
+                self.infcx.skolemize_late_bound_regions(a_pair, snapshot);
+
+            debug!("higher_ranked_match: a_match={:?}", a_match);
+            debug!("higher_ranked_match: skol_map={:?}", skol_map);
+
+            // Equate types now that bound regions have been replaced.
+            try!(self.equate().relate(&a_match, &b_match));
+
+            // Map each skolemized region to a vector of other regions that it
+            // must be equated with. (Note that this vector may include other
+            // skolemized regions from `skol_map`.)
+            let skol_resolution_map: FnvHashMap<_, _> =
+                skol_map
+                .iter()
+                .map(|(&br, &skol)| {
+                    let tainted_regions =
+                        self.infcx.tainted_regions(snapshot,
+                                                   skol,
+                                                   TaintDirections::incoming()); // [1]
+
+                    // [1] this routine executes after the skolemized
+                    // regions have been *equated* with something
+                    // else, so examining the incoming edges ought to
+                    // be enough to collect all constraints
+
+                    (skol, (br, tainted_regions))
+                })
+                .collect();
+
+            // For each skolemized region, pick a representative -- which can
+            // be any region from the sets above, except for other members of
+            // `skol_map`. There should always be a representative if things
+            // are properly well-formed.
+            let mut unconstrained_regions = vec![];
+            let skol_representatives: FnvHashMap<_, _> =
+                skol_resolution_map
+                .iter()
+                .map(|(&skol, &(br, ref regions))| {
+                    let representative =
+                        regions.iter()
+                               .filter(|r| !skol_resolution_map.contains_key(r))
+                               .cloned()
+                               .next()
+                               .unwrap_or_else(|| { // [1]
+                                   unconstrained_regions.push(br);
+                                   self.infcx.next_region_var(
+                                       LateBoundRegion(span, br, HigherRankedType))
+                               });
+
+                    // [1] There should always be a representative,
+                    // unless the higher-ranked region did not appear
+                    // in the values being matched. We should reject
+                    // as ill-formed cases that can lead to this, but
+                    // right now we sometimes issue warnings (see
+                    // #32330).
+
+                    (skol, representative)
+                })
+                .collect();
+
+            // Equate all the members of each skolemization set with the
+            // representative.
+            for (skol, &(_br, ref regions)) in &skol_resolution_map {
+                let representative = &skol_representatives[skol];
+                debug!("higher_ranked_match: \
+                        skol={:?} representative={:?} regions={:?}",
+                       skol, representative, regions);
+                for region in regions.iter()
+                                     .filter(|&r| !skol_resolution_map.contains_key(r))
+                                     .filter(|&r| r != representative)
+                {
+                    let origin = SubregionOrigin::Subtype(self.trace.clone());
+                    self.infcx.region_vars.make_eqregion(origin,
+                                                         *representative,
+                                                         *region);
+                }
+            }
+
+            // Replace the skolemized regions appearing in value with
+            // their representatives
+            let a_value =
+                fold_regions_in(
+                    self.tcx(),
+                    &a_value,
+                    |r, _| skol_representatives.get(&r).cloned().unwrap_or(r));
+
+            debug!("higher_ranked_match: value={:?}", a_value);
+
+            // We are now done with these skolemized variables.
+            self.infcx.pop_skolemized(skol_map, snapshot);
+
+            Ok(HrMatchResult {
+                value: a_value,
+                unconstrained_regions: unconstrained_regions,
+            })
+        });
+    }
+
     pub fn higher_ranked_lub<T>(&self, a: &Binder<T>, b: &Binder<T>)
                                 -> RelateResult<'tcx, Binder<T>>
         where T: Relate<'tcx>
index 5a4d2c2c445736c8dfa47e893e1d5427d34df7ed..ab9c056644571206929dca012bb067c2df809f56 100644 (file)
@@ -45,6 +45,7 @@
 use util::nodemap::{FnvHashMap, FnvHashSet, NodeMap};
 
 use self::combine::CombineFields;
+use self::higher_ranked::HrMatchResult;
 use self::region_inference::{RegionVarBindings, RegionSnapshot};
 use self::unify_key::ToType;
 
@@ -63,6 +64,7 @@
 pub mod type_variable;
 pub mod unify_key;
 
+#[must_use]
 pub struct InferOk<'tcx, T> {
     pub value: T,
     pub obligations: PredicateObligations<'tcx>,
@@ -1576,6 +1578,39 @@ pub fn replace_late_bound_regions_with_fresh_var<T>(
             |br| self.next_region_var(LateBoundRegion(span, br, lbrct)))
     }
 
+    /// Given a higher-ranked projection predicate like:
+    ///
+    ///     for<'a> <T as Fn<&'a u32>>::Output = &'a u32
+    ///
+    /// and a target trait-ref like:
+    ///
+    ///     <T as Fn<&'x u32>>
+    ///
+    /// find a substitution `S` for the higher-ranked regions (here,
+    /// `['a => 'x]`) such that the predicate matches the trait-ref,
+    /// and then return the value (here, `&'a u32`) but with the
+    /// substitution applied (hence, `&'x u32`).
+    ///
+    /// See `higher_ranked_match` in `higher_ranked/mod.rs` for more
+    /// details.
+    pub fn match_poly_projection_predicate(&self,
+                                           origin: TypeOrigin,
+                                           match_a: ty::PolyProjectionPredicate<'tcx>,
+                                           match_b: ty::TraitRef<'tcx>)
+                                           -> RelateResult<HrMatchResult<Ty<'tcx>>>
+    {
+        let span = origin.span();
+        let match_trait_ref = match_a.skip_binder().projection_ty.trait_ref;
+        let trace = TypeTrace {
+            origin: origin,
+            values: TraitRefs(ExpectedFound::new(true, match_trait_ref, match_b))
+        };
+
+        let match_pair = match_a.map_bound(|p| (p.projection_ty.trait_ref, p.ty));
+        self.combine_fields(true, trace)
+            .higher_ranked_match(span, &match_pair, &match_b)
+    }
+
     /// See `verify_generic_bound` method in `region_inference`
     pub fn verify_generic_bound(&self,
                                 origin: SubregionOrigin<'tcx>,