about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2018-03-07 14:08:21 -0500
committerNiko Matsakis <niko@alum.mit.edu>2018-03-13 11:22:08 -0400
commit0d17f95465ae3f0c865153fc902eae55ea94e2a9 (patch)
tree5741ea7547ee1f1a4abeef577aab1d72686e8e23
parent03c5428be37eedd09f1f3b627c03c3de1beb0674 (diff)
downloadrust-0d17f95465ae3f0c865153fc902eae55ea94e2a9.tar.gz
rust-0d17f95465ae3f0c865153fc902eae55ea94e2a9.zip
short-circuit work when instantiating query responses
Also, perform substitution in smaller parts.
-rw-r--r--src/librustc/infer/canonical.rs108
1 files changed, 84 insertions, 24 deletions
diff --git a/src/librustc/infer/canonical.rs b/src/librustc/infer/canonical.rs
index c1c7337860e..83194b5e504 100644
--- a/src/librustc/infer/canonical.rs
+++ b/src/librustc/infer/canonical.rs
@@ -29,6 +29,7 @@
 use infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin, TypeVariableOrigin};
 use rustc_data_structures::indexed_vec::Idx;
 use std::fmt::Debug;
+use std::ops::Index;
 use syntax::codemap::Span;
 use traits::{Obligation, ObligationCause, PredicateObligation};
 use ty::{self, CanonicalVar, Lift, Region, Slice, Ty, TyCtxt, TypeFlags};
@@ -395,28 +396,27 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
                 .collect(),
         };
 
-        // Apply the result substitution to the query.
-        let QueryResult {
-            var_values: query_values,
-            region_constraints: query_region_constraints,
-            certainty: _,
-            value: user_result,
-        } = query_result.substitute(self.tcx, result_subst);
-
         // Unify the original values for the canonical variables in
         // the input with the value found in the query
         // post-substitution. Often, but not always, this is a no-op,
         // because we already found the mapping in the first step.
+        let substituted_values = |index: CanonicalVar| -> Kind<'tcx> {
+            query_result.substitute_projected(self.tcx, result_subst, |v| &v.var_values[index])
+        };
         let mut obligations =
-            self.unify_canonical_vars(cause, param_env, original_values, &query_values)?
+            self.unify_canonical_vars(cause, param_env, original_values, substituted_values)?
                 .into_obligations();
 
         obligations.extend(self.query_region_constraints_into_obligations(
             cause,
             param_env,
-            query_region_constraints,
+            &query_result.value.region_constraints,
+            result_subst,
         ));
 
+        let user_result: R =
+            query_result.substitute_projected(self.tcx, result_subst, |q_r| &q_r.value);
+
         Ok(InferOk {
             value: user_result,
             obligations,
@@ -426,17 +426,20 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
     /// Converts the region constraints resulting from a query into an
     /// iterator of obligations.
     fn query_region_constraints_into_obligations<'a>(
-        &self,
+        &'a self,
         cause: &'a ObligationCause<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
-        query_region_constraints: QueryRegionConstraints<'tcx>,
+        unsubstituted_region_constraints: &'a QueryRegionConstraints<'tcx>,
+        result_subst: &'a CanonicalVarValues<'tcx>,
     ) -> impl Iterator<Item = PredicateObligation<'tcx>> + 'a {
         let QueryRegionConstraints {
             region_outlives,
             ty_outlives,
-        } = query_region_constraints;
+        } = unsubstituted_region_constraints;
 
-        let region_obligations = region_outlives.into_iter().map(move |(r1, r2)| {
+        let region_obligations = region_outlives.iter().map(move |(r1, r2)| {
+            let r1 = substitute_value(self.tcx, result_subst, r1);
+            let r2 = substitute_value(self.tcx, result_subst, r2);
             Obligation::new(
                 cause.clone(),
                 param_env,
@@ -444,7 +447,9 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
             )
         });
 
-        let ty_obligations = ty_outlives.into_iter().map(move |(t1, r2)| {
+        let ty_obligations = ty_outlives.iter().map(move |(t1, r2)| {
+            let t1 = substitute_value(self.tcx, result_subst, t1);
+            let r2 = substitute_value(self.tcx, result_subst, r2);
             Obligation::new(
                 cause.clone(),
                 param_env,
@@ -456,17 +461,19 @@ impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
     }
 
     /// Given two sets of values for the same set of canonical variables, unify them.
-    pub fn unify_canonical_vars(
+    /// The second set is produced lazilly by supplying indices from the first set.
+    fn unify_canonical_vars(
         &self,
         cause: &ObligationCause<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         variables1: &CanonicalVarValues<'tcx>,
-        variables2: &CanonicalVarValues<'tcx>,
+        variables2: impl Fn(CanonicalVar) -> Kind<'tcx>,
     ) -> InferResult<'tcx, ()> {
-        assert_eq!(variables1.var_values.len(), variables2.var_values.len());
         self.commit_if_ok(|_| {
             let mut obligations = vec![];
-            for (value1, value2) in variables1.var_values.iter().zip(&variables2.var_values) {
+            for (index, value1) in variables1.var_values.iter_enumerated() {
+                let value2 = variables2(index);
+
                 match (value1.unpack(), value2.unpack()) {
                     (UnpackedKind::Type(v1), UnpackedKind::Type(v2)) => {
                         obligations
@@ -724,7 +731,9 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
                     value: out_value,
                 },
             );
-            let values = CanonicalVarValues { var_values: IndexVec::default() };
+            let values = CanonicalVarValues {
+                var_values: IndexVec::default(),
+            };
             return (canon_value, values);
         }
 
@@ -810,13 +819,50 @@ impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
 impl<'tcx, V> Canonical<'tcx, V> {
     /// Instantiate the wrapped value, replacing each canonical value
     /// with the value given in `var_values`.
-    pub fn substitute(&self, tcx: TyCtxt<'_, '_, 'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V
+    fn substitute(&self, tcx: TyCtxt<'_, '_, 'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V
     where
         V: TypeFoldable<'tcx>,
     {
+        self.substitute_projected(tcx, var_values, |value| value)
+    }
+
+    /// Invoke `projection_fn` with `self.value` to get a value V that
+    /// is expressed in terms of the same canonical variables bound in
+    /// `self`. Apply the substitution `var_values` to this value V,
+    /// replacing each of the canonical variables.
+    fn substitute_projected<T>(
+        &self,
+        tcx: TyCtxt<'_, '_, 'tcx>,
+        var_values: &CanonicalVarValues<'tcx>,
+        projection_fn: impl FnOnce(&V) -> &T,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
         assert_eq!(self.variables.len(), var_values.var_values.len());
-        self.value
-            .fold_with(&mut CanonicalVarValuesSubst { tcx, var_values })
+        let value = projection_fn(&self.value);
+        substitute_value(tcx, var_values, value)
+    }
+}
+
+/// Substitute the values from `var_values` into `value`. `var_values`
+/// must be values for the set of cnaonical variables that appear in
+/// `value`.
+fn substitute_value<'a, 'tcx, T>(
+    tcx: TyCtxt<'_, '_, 'tcx>,
+    var_values: &CanonicalVarValues<'tcx>,
+    value: &'a T,
+) -> T
+where
+    T: TypeFoldable<'tcx>,
+{
+    if var_values.var_values.is_empty() {
+        debug_assert!(!value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS));
+        value.clone()
+    } else if !value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS) {
+        value.clone()
+    } else {
+        value.fold_with(&mut CanonicalVarValuesSubst { tcx, var_values })
     }
 }
 
@@ -838,7 +884,13 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for CanonicalVarValuesSubst<'cx, 'g
                     r => bug!("{:?} is a type but value is {:?}", c, r),
                 }
             }
-            _ => t.super_fold_with(self),
+            _ => {
+                if !t.has_type_flags(TypeFlags::HAS_CANONICAL_VARS) {
+                    t
+                } else {
+                    t.super_fold_with(self)
+                }
+            }
         }
     }
 
@@ -926,3 +978,11 @@ BraceStructLiftImpl! {
         var_values, region_constraints, certainty, value
     } where R: Lift<'tcx>
 }
+
+impl<'tcx> Index<CanonicalVar> for CanonicalVarValues<'tcx> {
+    type Output = Kind<'tcx>;
+
+    fn index(&self, value: CanonicalVar) -> &Kind<'tcx> {
+        &self.var_values[value]
+    }
+}