]> git.lizzy.rs Git - rust.git/commitdiff
move the `region_obligations` processing code into `InferCtxt`
authorNiko Matsakis <niko@alum.mit.edu>
Fri, 3 Nov 2017 09:31:19 +0000 (05:31 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Wed, 15 Nov 2017 21:49:21 +0000 (16:49 -0500)
src/librustc/infer/mod.rs
src/librustc/infer/region_obligations.rs [new file with mode: 0644]
src/librustc/traits/mod.rs
src/librustc_typeck/check/mod.rs
src/librustc_typeck/check/regionck.rs
src/librustc_typeck/check/regionck_outlives.rs [deleted file]

index c76d098bd6995f6fa0d9fb7177e4ad405869695b..41e1bf303b38457fb08b3c5a14ff6d4b1d2bcf7d 100644 (file)
@@ -55,6 +55,7 @@
 pub mod lattice;
 mod lub;
 pub mod region_inference;
+mod region_obligations;
 pub mod resolve;
 mod freshen;
 mod sub;
@@ -160,6 +161,13 @@ pub struct InferCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     // regionck to be sure that it has found *all* the region
     // obligations (otherwise, it's easy to fail to walk to a
     // particular node-id).
+    //
+    // Before running `resolve_regions_and_report_errors`, the creator
+    // of the inference context is expected to invoke
+    // `process_region_obligations` (defined in `self::region_obligations`)
+    // for each body-id in this map, which will process the
+    // obligations within. This is expected to be done 'late enough'
+    // that all type inference variables have been bound and so forth.
     region_obligations: RefCell<NodeMap<Vec<RegionObligation<'tcx>>>>,
 }
 
@@ -984,33 +992,6 @@ pub fn region_outlives_predicate(&self,
         })
     }
 
-    /// Registers that the given region obligation must be resolved
-    /// from within the scope of `body_id`. These regions are enqueued
-    /// and later processed by regionck, when full type information is
-    /// available (see `region_obligations` field for more
-    /// information).
-    pub fn register_region_obligation(&self,
-                                      body_id: ast::NodeId,
-                                      obligation: RegionObligation<'tcx>)
-    {
-        self.region_obligations.borrow_mut().entry(body_id)
-                                            .or_insert(vec![])
-                                            .push(obligation);
-    }
-
-    /// Get the region obligations that must be proven (during
-    /// `regionck`) for the given `body_id` (removing them from the
-    /// map as a side-effect).
-    pub fn take_region_obligations(&self,
-                                   body_id: ast::NodeId)
-                                   -> Vec<RegionObligation<'tcx>>
-    {
-        match self.region_obligations.borrow_mut().remove(&body_id) {
-            None => vec![],
-            Some(vec) => vec,
-        }
-    }
-
     pub fn next_ty_var_id(&self, diverging: bool, origin: TypeVariableOrigin) -> TyVid {
         self.type_variables
             .borrow_mut()
diff --git a/src/librustc/infer/region_obligations.rs b/src/librustc/infer/region_obligations.rs
new file mode 100644 (file)
index 0000000..edcabd5
--- /dev/null
@@ -0,0 +1,619 @@
+//! Code that handles "type-outlives" constraints like `T: 'a`. This
+//! is based on the `outlives_components` function defined on the tcx,
+//! but it adds a bit of heuristics on top, in particular to deal with
+//! associated types and projections.
+//!
+//! When we process a given `T: 'a` obligation, we may produce two
+//! kinds of constraints for the region inferencer:
+//!
+//! - Relationships between inference variables and other regions.
+//!   For example, if we have `&'?0 u32: 'a`, then we would produce
+//!   a constraint that `'a <= '?0`.
+//! - "Verifys" that must be checked after inferencing is done.
+//!   For example, if we know that, for some type parameter `T`,
+//!   `T: 'a + 'b`, and we have a requirement that `T: '?1`,
+//!   then we add a "verify" that checks that `'?1 <= 'a || '?1 <= 'b`.
+//!   - Note the difference with the previous case: here, the region
+//!     variable must be less than something else, so this doesn't
+//!     affect how inference works (it finds the smallest region that
+//!     will do); it's just a post-condition that we have to check.
+//!
+//! **The key point is that once this function is done, we have
+//! reduced all of our "type-region outlives" obligations into relationships
+//! between individual regions.**
+//!
+//! One key input to this function is the set of "region-bound pairs".
+//! These are basically the relationships between type parameters and
+//! regions that are in scope at the point where the outlives
+//! obligation was incurred. **When type-checking a function,
+//! particularly in the face of closures, this is not known until
+//! regionck runs!** This is because some of those bounds come
+//! from things we have yet to infer.
+//!
+//! Consider:
+//!
+//! ```
+//! fn bar<T>(a: T, b: impl for<'a> Fn(&'a T));
+//! fn foo<T>(x: T) {
+//!     bar(x, |y| { ... })
+//!          // ^ closure arg
+//! }
+//! ```
+//!
+//! Here, the type of `y` may involve inference variables and the
+//! like, and it may also contain implied bounds that are needed to
+//! type-check the closure body (e.g., here it informs us that `T`
+//! outlives the late-bound region `'a`).
+//!
+//! > That said, in writing this, I have come to wonder: this
+//!   inference dependency, I think, is only interesting for
+//!   late-bound regions in the closure -- if the region appears free
+//!   in the closure signature, then the relationship must be known to
+//!   the caller (here, `foo`), and hence could be verified earlier
+//!   up. Moreover, we infer late-bound regions quite early on right
+//!   now, i.e., only when the expected signature is known.  So we
+//!   *may* be able to sidestep this. Regardless, once the NLL
+//!   transition is complete, this concern will be gone. -nmatsakis
+
+use infer::{self, GenericKind, InferCtxt, InferOk, RegionObligation, SubregionOrigin, VerifyBound};
+use traits::{self, ObligationCause, ObligationCauseCode, PredicateObligations};
+use ty::{self, Ty, TyCtxt, TypeFoldable};
+use ty::subst::Subst;
+use ty::outlives::Component;
+use syntax::ast;
+use syntax_pos::Span;
+
+impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
+    /// Registers that the given region obligation must be resolved
+    /// from within the scope of `body_id`. These regions are enqueued
+    /// and later processed by regionck, when full type information is
+    /// available (see `region_obligations` field for more
+    /// information).
+    pub fn register_region_obligation(
+        &self,
+        body_id: ast::NodeId,
+        obligation: RegionObligation<'tcx>,
+    ) {
+        self.region_obligations
+            .borrow_mut()
+            .entry(body_id)
+            .or_insert(vec![])
+            .push(obligation);
+    }
+
+    /// Process the region obligations that must be proven (during
+    /// `regionck`) for the given `body_id`, given information about
+    /// the region bounds in scope and so forth. This function must be
+    /// invoked for all relevant body-ids before region inference is
+    /// done (or else an assert will fire).
+    ///
+    /// See the `region_obligations` field of `InferCtxt` for some
+    /// comments about how this funtion fits into the overall expected
+    /// flow of the the inferencer. The key point is that it is
+    /// invoked after all type-inference variables have been bound --
+    /// towards the end of regionck. This also ensures that the
+    /// region-bound-pairs are available (see comments above regarding
+    /// closures).
+    ///
+    /// # Parameters
+    ///
+    /// - `region_bound_pairs`: the set of region bounds implied by
+    ///   the parameters and where-clauses. In particular, each pair
+    ///   `('a, K)` in this list tells us that the bounds in scope
+    ///   indicate that `K: 'a`, where `K` is either a generic
+    ///   parameter like `T` or a projection like `T::Item`.
+    /// - `implicit_region_bound`: if some, this is a region bound
+    ///   that is considered to hold for all type parameters (the
+    ///   function body).
+    /// - `param_env` is the parameter environment for the enclosing function.
+    /// - `body_id` is the body-id whose region obligations are being
+    ///   processed.
+    ///
+    /// # Returns
+    ///
+    /// This function may have to perform normalizations, and hence it
+    /// returns an `InferOk` with subobligations that must be
+    /// processed.
+    pub fn process_registered_region_obligations(
+        &self,
+        region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)],
+        implicit_region_bound: Option<ty::Region<'tcx>>,
+        param_env: ty::ParamEnv<'tcx>,
+        body_id: ast::NodeId,
+    ) -> InferOk<'tcx, ()> {
+        let region_obligations = match self.region_obligations.borrow_mut().remove(&body_id) {
+            None => vec![],
+            Some(vec) => vec,
+        };
+
+        let mut outlives = TypeOutlives::new(
+            self,
+            region_bound_pairs,
+            implicit_region_bound,
+            param_env,
+            body_id,
+        );
+
+        for RegionObligation {
+            sup_type,
+            sub_region,
+            cause,
+        } in region_obligations
+        {
+            let origin = SubregionOrigin::from_obligation_cause(
+                &cause,
+                || infer::RelateParamBound(cause.span, sup_type),
+            );
+
+            outlives.type_must_outlive(origin, sup_type, sub_region);
+        }
+
+        InferOk {
+            value: (),
+            obligations: outlives.into_accrued_obligations(),
+        }
+    }
+
+    /// Processes a single ad-hoc region obligation that was not
+    /// registered in advance.
+    pub fn type_must_outlive(
+        &self,
+        region_bound_pairs: &[(ty::Region<'tcx>, GenericKind<'tcx>)],
+        implicit_region_bound: Option<ty::Region<'tcx>>,
+        param_env: ty::ParamEnv<'tcx>,
+        body_id: ast::NodeId,
+        origin: infer::SubregionOrigin<'tcx>,
+        ty: Ty<'tcx>,
+        region: ty::Region<'tcx>,
+    ) -> InferOk<'tcx, ()> {
+        let mut outlives = TypeOutlives::new(
+            self,
+            region_bound_pairs,
+            implicit_region_bound,
+            param_env,
+            body_id,
+        );
+        outlives.type_must_outlive(origin, ty, region);
+        InferOk {
+            value: (),
+            obligations: outlives.into_accrued_obligations(),
+        }
+    }
+
+    /// Ignore the region obligations for a given `body_id`, not bothering to
+    /// prove them. This function should not really exist; it is used to accommodate some older
+    /// code for the time being.
+    pub fn ignore_region_obligations(&self, body_id: ast::NodeId) {
+        self.region_obligations.borrow_mut().remove(&body_id);
+    }
+}
+
+#[must_use] // you ought to invoke `into_accrued_obligations` when you are done =)
+struct TypeOutlives<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    // See the comments on `process_registered_region_obligations` for the meaning
+    // of these fields.
+    infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+    region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)],
+    implicit_region_bound: Option<ty::Region<'tcx>>,
+    param_env: ty::ParamEnv<'tcx>,
+    body_id: ast::NodeId,
+
+    /// These are sub-obligations that we accrue as we go; they result
+    /// from any normalizations we had to do.
+    obligations: PredicateObligations<'tcx>,
+}
+
+impl<'cx, 'gcx, 'tcx> TypeOutlives<'cx, 'gcx, 'tcx> {
+    fn new(
+        infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+        region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)],
+        implicit_region_bound: Option<ty::Region<'tcx>>,
+        param_env: ty::ParamEnv<'tcx>,
+        body_id: ast::NodeId,
+    ) -> Self {
+        Self {
+            infcx,
+            region_bound_pairs,
+            implicit_region_bound,
+            param_env,
+            body_id,
+            obligations: vec![],
+        }
+    }
+
+    /// Returns the obligations that accrued as a result of the
+    /// `type_must_outlive` calls.
+    fn into_accrued_obligations(self) -> PredicateObligations<'tcx> {
+        self.obligations
+    }
+
+    /// Adds constraints to inference such that `T: 'a` holds (or
+    /// reports an error if it cannot).
+    ///
+    /// # Parameters
+    ///
+    /// - `origin`, the reason we need this constraint
+    /// - `ty`, the type `T`
+    /// - `region`, the region `'a`
+    fn type_must_outlive(
+        &mut self,
+        origin: infer::SubregionOrigin<'tcx>,
+        ty: Ty<'tcx>,
+        region: ty::Region<'tcx>,
+    ) {
+        let ty = self.infcx.resolve_type_vars_if_possible(&ty);
+
+        debug!(
+            "type_must_outlive(ty={:?}, region={:?}, origin={:?})",
+            ty,
+            region,
+            origin
+        );
+
+        assert!(!ty.has_escaping_regions());
+
+        let components = self.tcx().outlives_components(ty);
+        self.components_must_outlive(origin, components, region);
+    }
+
+    fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> {
+        self.infcx.tcx
+    }
+
+    fn components_must_outlive(
+        &mut self,
+        origin: infer::SubregionOrigin<'tcx>,
+        components: Vec<Component<'tcx>>,
+        region: ty::Region<'tcx>,
+    ) {
+        for component in components {
+            let origin = origin.clone();
+            match component {
+                Component::Region(region1) => {
+                    self.infcx.sub_regions(origin, region, region1);
+                }
+                Component::Param(param_ty) => {
+                    self.param_ty_must_outlive(origin, region, param_ty);
+                }
+                Component::Projection(projection_ty) => {
+                    self.projection_must_outlive(origin, region, projection_ty);
+                }
+                Component::EscapingProjection(subcomponents) => {
+                    self.components_must_outlive(origin, subcomponents, region);
+                }
+                Component::UnresolvedInferenceVariable(v) => {
+                    // ignore this, we presume it will yield an error
+                    // later, since if a type variable is not resolved by
+                    // this point it never will be
+                    self.infcx.tcx.sess.delay_span_bug(
+                        origin.span(),
+                        &format!("unresolved inference variable in outlives: {:?}", v),
+                    );
+                }
+            }
+        }
+    }
+
+    fn param_ty_must_outlive(
+        &mut self,
+        origin: infer::SubregionOrigin<'tcx>,
+        region: ty::Region<'tcx>,
+        param_ty: ty::ParamTy,
+    ) {
+        debug!(
+            "param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})",
+            region,
+            param_ty,
+            origin
+        );
+
+        let verify_bound = self.param_bound(param_ty);
+        let generic = GenericKind::Param(param_ty);
+        self.infcx
+            .verify_generic_bound(origin, generic, region, verify_bound);
+    }
+
+    fn projection_must_outlive(
+        &mut self,
+        origin: infer::SubregionOrigin<'tcx>,
+        region: ty::Region<'tcx>,
+        projection_ty: ty::ProjectionTy<'tcx>,
+    ) {
+        debug!(
+            "projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})",
+            region,
+            projection_ty,
+            origin
+        );
+
+        // This case is thorny for inference. The fundamental problem is
+        // that there are many cases where we have choice, and inference
+        // doesn't like choice (the current region inference in
+        // particular). :) First off, we have to choose between using the
+        // OutlivesProjectionEnv, OutlivesProjectionTraitDef, and
+        // OutlivesProjectionComponent rules, any one of which is
+        // sufficient.  If there are no inference variables involved, it's
+        // not hard to pick the right rule, but if there are, we're in a
+        // bit of a catch 22: if we picked which rule we were going to
+        // use, we could add constraints to the region inference graph
+        // that make it apply, but if we don't add those constraints, the
+        // rule might not apply (but another rule might). For now, we err
+        // on the side of adding too few edges into the graph.
+
+        // Compute the bounds we can derive from the environment or trait
+        // definition.  We know that the projection outlives all the
+        // regions in this list.
+        let env_bounds = self.projection_declared_bounds(origin.span(), projection_ty);
+
+        debug!("projection_must_outlive: env_bounds={:?}", env_bounds);
+
+        // If we know that the projection outlives 'static, then we're
+        // done here.
+        if env_bounds.contains(&&ty::ReStatic) {
+            debug!("projection_must_outlive: 'static as declared bound");
+            return;
+        }
+
+        // If declared bounds list is empty, the only applicable rule is
+        // OutlivesProjectionComponent. If there are inference variables,
+        // then, we can break down the outlives into more primitive
+        // components without adding unnecessary edges.
+        //
+        // If there are *no* inference variables, however, we COULD do
+        // this, but we choose not to, because the error messages are less
+        // good. For example, a requirement like `T::Item: 'r` would be
+        // translated to a requirement that `T: 'r`; when this is reported
+        // to the user, it will thus say "T: 'r must hold so that T::Item:
+        // 'r holds". But that makes it sound like the only way to fix
+        // the problem is to add `T: 'r`, which isn't true. So, if there are no
+        // inference variables, we use a verify constraint instead of adding
+        // edges, which winds up enforcing the same condition.
+        let needs_infer = projection_ty.needs_infer();
+        if env_bounds.is_empty() && needs_infer {
+            debug!("projection_must_outlive: no declared bounds");
+
+            for component_ty in projection_ty.substs.types() {
+                self.type_must_outlive(origin.clone(), component_ty, region);
+            }
+
+            for r in projection_ty.substs.regions() {
+                self.infcx.sub_regions(origin.clone(), region, r);
+            }
+
+            return;
+        }
+
+        // If we find that there is a unique declared bound `'b`, and this bound
+        // appears in the trait reference, then the best action is to require that `'b:'r`,
+        // so do that. This is best no matter what rule we use:
+        //
+        // - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to
+        // the requirement that `'b:'r`
+        // - OutlivesProjectionComponent: this would require `'b:'r` in addition to
+        // other conditions
+        if !env_bounds.is_empty() && env_bounds[1..].iter().all(|b| *b == env_bounds[0]) {
+            let unique_bound = env_bounds[0];
+            debug!(
+                "projection_must_outlive: unique declared bound = {:?}",
+                unique_bound
+            );
+            if projection_ty
+                .substs
+                .regions()
+                .any(|r| env_bounds.contains(&r))
+            {
+                debug!("projection_must_outlive: unique declared bound appears in trait ref");
+                self.infcx.sub_regions(origin.clone(), region, unique_bound);
+                return;
+            }
+        }
+
+        // Fallback to verifying after the fact that there exists a
+        // declared bound, or that all the components appearing in the
+        // projection outlive; in some cases, this may add insufficient
+        // edges into the inference graph, leading to inference failures
+        // even though a satisfactory solution exists.
+        let verify_bound = self.projection_bound(origin.span(), env_bounds, projection_ty);
+        let generic = GenericKind::Projection(projection_ty);
+        self.infcx
+            .verify_generic_bound(origin, generic.clone(), region, verify_bound);
+    }
+
+    fn type_bound(&mut self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
+        match ty.sty {
+            ty::TyParam(p) => self.param_bound(p),
+            ty::TyProjection(data) => {
+                let declared_bounds = self.projection_declared_bounds(span, data);
+                self.projection_bound(span, declared_bounds, data)
+            }
+            _ => self.recursive_type_bound(span, ty),
+        }
+    }
+
+    fn param_bound(&mut self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> {
+        debug!("param_bound(param_ty={:?})", param_ty);
+
+        let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty));
+
+        // Add in the default bound of fn body that applies to all in
+        // scope type parameters:
+        param_bounds.extend(self.implicit_region_bound);
+
+        VerifyBound::AnyRegion(param_bounds)
+    }
+
+    fn projection_declared_bounds(
+        &mut self,
+        span: Span,
+        projection_ty: ty::ProjectionTy<'tcx>,
+    ) -> Vec<ty::Region<'tcx>> {
+        // First assemble bounds from where clauses and traits.
+
+        let mut declared_bounds =
+            self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty));
+
+        declared_bounds
+            .extend_from_slice(&mut self.declared_projection_bounds_from_trait(span, projection_ty));
+
+        declared_bounds
+    }
+
+    fn projection_bound(
+        &mut self,
+        span: Span,
+        declared_bounds: Vec<ty::Region<'tcx>>,
+        projection_ty: ty::ProjectionTy<'tcx>,
+    ) -> VerifyBound<'tcx> {
+        debug!(
+            "projection_bound(declared_bounds={:?}, projection_ty={:?})",
+            declared_bounds,
+            projection_ty
+        );
+
+        // see the extensive comment in projection_must_outlive
+        let ty = self.infcx
+            .tcx
+            .mk_projection(projection_ty.item_def_id, projection_ty.substs);
+        let recursive_bound = self.recursive_type_bound(span, ty);
+
+        VerifyBound::AnyRegion(declared_bounds).or(recursive_bound)
+    }
+
+    fn recursive_type_bound(&mut self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
+        let mut bounds = vec![];
+
+        for subty in ty.walk_shallow() {
+            bounds.push(self.type_bound(span, subty));
+        }
+
+        let mut regions = ty.regions();
+        regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions
+        bounds.push(VerifyBound::AllRegions(regions));
+
+        // remove bounds that must hold, since they are not interesting
+        bounds.retain(|b| !b.must_hold());
+
+        if bounds.len() == 1 {
+            bounds.pop().unwrap()
+        } else {
+            VerifyBound::AllBounds(bounds)
+        }
+    }
+
+    fn declared_generic_bounds_from_env(
+        &mut self,
+        generic: GenericKind<'tcx>,
+    ) -> Vec<ty::Region<'tcx>> {
+        let tcx = self.tcx();
+
+        // To start, collect bounds from user:
+        let mut param_bounds =
+            tcx.required_region_bounds(generic.to_ty(tcx), self.param_env.caller_bounds.to_vec());
+
+        // Next, collect regions we scraped from the well-formedness
+        // constraints in the fn signature. To do that, we walk the list
+        // of known relations from the fn ctxt.
+        //
+        // This is crucial because otherwise code like this fails:
+        //
+        //     fn foo<'a, A>(x: &'a A) { x.bar() }
+        //
+        // The problem is that the type of `x` is `&'a A`. To be
+        // well-formed, then, A must be lower-generic by `'a`, but we
+        // don't know that this holds from first principles.
+        for &(r, p) in self.region_bound_pairs {
+            debug!("generic={:?} p={:?}", generic, p);
+            if generic == p {
+                param_bounds.push(r);
+            }
+        }
+
+        param_bounds
+    }
+
+    fn declared_projection_bounds_from_trait(
+        &mut self,
+        span: Span,
+        projection_ty: ty::ProjectionTy<'tcx>,
+    ) -> Vec<ty::Region<'tcx>> {
+        debug!("projection_bounds(projection_ty={:?})", projection_ty);
+        let ty = self.tcx()
+            .mk_projection(projection_ty.item_def_id, projection_ty.substs);
+
+        // Say we have a projection `<T as SomeTrait<'a>>::SomeType`. We are interested
+        // in looking for a trait definition like:
+        //
+        // ```
+        // trait SomeTrait<'a> {
+        //     type SomeType : 'a;
+        // }
+        // ```
+        //
+        // we can thus deduce that `<T as SomeTrait<'a>>::SomeType : 'a`.
+        let trait_predicates = self.tcx()
+            .predicates_of(projection_ty.trait_ref(self.tcx()).def_id);
+        assert_eq!(trait_predicates.parent, None);
+        let predicates = trait_predicates.predicates.as_slice().to_vec();
+        traits::elaborate_predicates(self.tcx(), predicates)
+            .filter_map(|predicate| {
+                // we're only interesting in `T : 'a` style predicates:
+                let outlives = match predicate {
+                    ty::Predicate::TypeOutlives(data) => data,
+                    _ => {
+                        return None;
+                    }
+                };
+
+                debug!("projection_bounds: outlives={:?} (1)", outlives);
+
+                // apply the substitutions (and normalize any projected types)
+                let outlives = outlives.subst(self.tcx(), projection_ty.substs);
+                let outlives = self.infcx.partially_normalize_associated_types_in(
+                    span,
+                    self.body_id,
+                    self.param_env,
+                    &outlives,
+                );
+                let outlives = self.register_infer_ok_obligations(outlives);
+
+                debug!("projection_bounds: outlives={:?} (2)", outlives);
+
+                let region_result = self.infcx
+                    .commit_if_ok(|_| {
+                        let (outlives, _) = self.infcx.replace_late_bound_regions_with_fresh_var(
+                            span,
+                            infer::AssocTypeProjection(projection_ty.item_def_id),
+                            &outlives,
+                        );
+
+                        debug!("projection_bounds: outlives={:?} (3)", outlives);
+
+                        // check whether this predicate applies to our current projection
+                        let cause = ObligationCause::new(
+                            span,
+                            self.body_id,
+                            ObligationCauseCode::MiscObligation,
+                        );
+                        match self.infcx.at(&cause, self.param_env).eq(outlives.0, ty) {
+                            Ok(ok) => Ok((ok, outlives.1)),
+                            Err(_) => Err(()),
+                        }
+                    })
+                    .map(|(ok, result)| {
+                        self.register_infer_ok_obligations(ok);
+                        result
+                    });
+
+                debug!("projection_bounds: region_result={:?}", region_result);
+
+                region_result.ok()
+            })
+            .collect()
+    }
+
+    fn register_infer_ok_obligations<T>(&mut self, infer_ok: InferOk<'tcx, T>) -> T {
+        let InferOk { value, obligations } = infer_ok;
+        self.obligations.extend(obligations);
+        value
+    }
+}
index cd4a6878851c130fe8b1923b66eff5a6c11ba801..0489a316cb3e8c27f7e4e6f55f5abb198345b9b1 100644 (file)
@@ -543,7 +543,7 @@ pub fn normalize_param_env_or_error<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         // it, and it would take some refactoring to stop doing so.
         // (In particular, the needed methods currently live in
         // regionck.) -nmatsakis
-        let _ = infcx.take_region_obligations(body_id);
+        let _ = infcx.ignore_region_obligations(body_id);
 
         infcx.resolve_regions_and_report_errors(region_context, &region_scope_tree, &free_regions);
         let predicates = match infcx.fully_resolve(&predicates) {
index 0c4a5512dd601ec36f982451424ca641ab3da0b6..c8b2032a49871510fc973b0ff9ca546d31dc395c 100644 (file)
 pub mod _match;
 pub mod writeback;
 mod regionck;
-mod regionck_outlives;
 pub mod coercion;
 pub mod demand;
 pub mod method;
index 77a34023df3df58733a49675fce21d2daf302bc8..20487dda20190d9d17df6b74fb88df1f08050ba4 100644 (file)
@@ -90,9 +90,8 @@
 use middle::region;
 use rustc::hir::def_id::DefId;
 use rustc::ty::subst::Substs;
-use rustc::traits;
 use rustc::ty::{self, Ty, TypeFoldable};
-use rustc::infer::{self, GenericKind, SubregionOrigin};
+use rustc::infer::{self, InferOk, GenericKind};
 use rustc::ty::adjustment;
 use rustc::ty::outlives::Component;
 use rustc::ty::wf;
 use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
 use rustc::hir::{self, PatKind};
 
-use super::regionck_outlives::RegionckOutlives;
-
 // a variation on try that just returns unit
 macro_rules! ignore_err {
     ($e:expr) => (match $e { Ok(e) => e, Err(_) => return () })
@@ -360,28 +357,21 @@ fn visit_region_obligations(&mut self, node_id: ast::NodeId)
         // obligations. So make sure we process those.
         self.select_all_obligations_or_error();
 
-        // Make a copy of the region obligations vec because we'll need
-        // to be able to borrow the fulfillment-cx below when projecting.
-        let region_obligations = self.infcx.take_region_obligations(node_id);
-
-        for r_o in &region_obligations {
-            debug!("visit_region_obligations: r_o={:?} cause={:?}",
-                   r_o, r_o.cause);
-            let sup_type = self.resolve_type(r_o.sup_type);
-            let origin = self.code_to_origin(&r_o.cause, sup_type);
-            self.type_must_outlive(origin, sup_type, r_o.sub_region);
-        }
-
-        // Processing the region obligations should not cause the list to grow further:
-        assert!(self.infcx.take_region_obligations(node_id).is_empty());
-    }
-
-    fn code_to_origin(&self,
-                      cause: &traits::ObligationCause<'tcx>,
-                      sup_type: Ty<'tcx>)
-                      -> SubregionOrigin<'tcx> {
-        SubregionOrigin::from_obligation_cause(cause,
-                                               || infer::RelateParamBound(cause.span, sup_type))
+        let InferOk { value: (), obligations }  =
+            self.infcx.process_registered_region_obligations(
+                &self.region_bound_pairs,
+                self.implicit_region_bound,
+                self.param_env,
+                self.body_id);
+
+        // TODO -- It feels like we ought to loop here; these new
+        // obligations, when selected, could cause the list of region
+        // obligations to grow further. Fortunately, I believe that if
+        // that happens it will at least lead to an ICE today, because
+        // `resolve_regions_and_report_errors` (which runs after *all*
+        // obligations have been selected) will assert that there are
+        // no unsolved region obligations.
+        self.register_predicates(obligations);
     }
 
     /// This method populates the region map's `free_region_map`. It walks over the transformed
@@ -1147,12 +1137,14 @@ pub fn type_must_outlive(&self,
                              ty: Ty<'tcx>,
                              region: ty::Region<'tcx>)
     {
-        let outlives = RegionckOutlives::new(&self.infcx,
-                                             &self.region_bound_pairs,
-                                             self.implicit_region_bound,
-                                             self.param_env,
-                                             self.body_id);
-        self.register_infer_ok_obligations(outlives.type_must_outlive(origin, ty, region));
+        let infer_ok = self.infcx.type_must_outlive(&self.region_bound_pairs,
+                                                    self.implicit_region_bound,
+                                                    self.param_env,
+                                                    self.body_id,
+                                                    origin,
+                                                    ty,
+                                                    region);
+        self.register_infer_ok_obligations(infer_ok)
     }
 
     /// Computes the guarantor for an expression `&base` and then ensures that the lifetime of the
diff --git a/src/librustc_typeck/check/regionck_outlives.rs b/src/librustc_typeck/check/regionck_outlives.rs
deleted file mode 100644 (file)
index 9bd7384..0000000
+++ /dev/null
@@ -1,445 +0,0 @@
-//! Temporary holding spot for some code I want to factor out.
-
-use rustc::traits::{self, ObligationCause, ObligationCauseCode, PredicateObligations};
-use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
-use rustc::infer::{self, GenericKind, InferCtxt, InferOk, VerifyBound};
-use rustc::ty::subst::Subst;
-use rustc::ty::outlives::Component;
-use syntax::ast;
-use syntax_pos::Span;
-
-pub struct RegionckOutlives<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
-    // Context provided by the caller:
-    infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
-    region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)],
-    implicit_region_bound: Option<ty::Region<'tcx>>,
-    param_env: ty::ParamEnv<'tcx>,
-    body_id: ast::NodeId,
-
-    // Obligations that we accrue as we go:
-    obligations: PredicateObligations<'tcx>,
-}
-
-impl<'cx, 'gcx, 'tcx> RegionckOutlives<'cx, 'gcx, 'tcx> {
-    pub fn new(
-        infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
-        region_bound_pairs: &'cx [(ty::Region<'tcx>, GenericKind<'tcx>)],
-        implicit_region_bound: Option<ty::Region<'tcx>>,
-        param_env: ty::ParamEnv<'tcx>,
-        body_id: ast::NodeId,
-    ) -> Self {
-        Self {
-            infcx,
-            region_bound_pairs,
-            implicit_region_bound,
-            param_env,
-            body_id,
-            obligations: vec![],
-        }
-    }
-
-    /// Adds constraints to inference such that `T: 'a` holds (or
-    /// reports an error if it cannot).
-    ///
-    /// # Parameters
-    ///
-    /// - `origin`, the reason we need this constraint
-    /// - `ty`, the type `T`
-    /// - `region`, the region `'a`
-    pub fn type_must_outlive(
-        mut self,
-        origin: infer::SubregionOrigin<'tcx>,
-        ty: Ty<'tcx>,
-        region: ty::Region<'tcx>,
-    ) -> InferOk<'tcx, ()> {
-        self.type_must_outlive_pushing_obligations(origin, ty, region);
-        InferOk {
-            value: (),
-            obligations: self.obligations,
-        }
-    }
-
-    /// Internal helper: ensure that `ty_must_outlive` and push obligations onto
-    /// our internal vector.
-    fn type_must_outlive_pushing_obligations(
-        &mut self,
-        origin: infer::SubregionOrigin<'tcx>,
-        ty: Ty<'tcx>,
-        region: ty::Region<'tcx>,
-    ) {
-        let ty = self.infcx.resolve_type_vars_if_possible(&ty);
-
-        debug!(
-            "type_must_outlive(ty={:?}, region={:?}, origin={:?})",
-            ty,
-            region,
-            origin
-        );
-
-        assert!(!ty.has_escaping_regions());
-
-        let components = self.tcx().outlives_components(ty);
-        self.components_must_outlive(origin, components, region);
-    }
-
-    fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> {
-        self.infcx.tcx
-    }
-
-    fn components_must_outlive(
-        &mut self,
-        origin: infer::SubregionOrigin<'tcx>,
-        components: Vec<Component<'tcx>>,
-        region: ty::Region<'tcx>,
-    ) {
-        for component in components {
-            let origin = origin.clone();
-            match component {
-                Component::Region(region1) => {
-                    self.infcx.sub_regions(origin, region, region1);
-                }
-                Component::Param(param_ty) => {
-                    self.param_ty_must_outlive(origin, region, param_ty);
-                }
-                Component::Projection(projection_ty) => {
-                    self.projection_must_outlive(origin, region, projection_ty);
-                }
-                Component::EscapingProjection(subcomponents) => {
-                    self.components_must_outlive(origin, subcomponents, region);
-                }
-                Component::UnresolvedInferenceVariable(v) => {
-                    // ignore this, we presume it will yield an error
-                    // later, since if a type variable is not resolved by
-                    // this point it never will be
-                    self.infcx.tcx.sess.delay_span_bug(
-                        origin.span(),
-                        &format!("unresolved inference variable in outlives: {:?}", v),
-                    );
-                }
-            }
-        }
-    }
-
-    fn param_ty_must_outlive(
-        &mut self,
-        origin: infer::SubregionOrigin<'tcx>,
-        region: ty::Region<'tcx>,
-        param_ty: ty::ParamTy,
-    ) {
-        debug!(
-            "param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})",
-            region,
-            param_ty,
-            origin
-        );
-
-        let verify_bound = self.param_bound(param_ty);
-        let generic = GenericKind::Param(param_ty);
-        self.infcx
-            .verify_generic_bound(origin, generic, region, verify_bound);
-    }
-
-    fn projection_must_outlive(
-        &mut self,
-        origin: infer::SubregionOrigin<'tcx>,
-        region: ty::Region<'tcx>,
-        projection_ty: ty::ProjectionTy<'tcx>,
-    ) {
-        debug!(
-            "projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})",
-            region,
-            projection_ty,
-            origin
-        );
-
-        // This case is thorny for inference. The fundamental problem is
-        // that there are many cases where we have choice, and inference
-        // doesn't like choice (the current region inference in
-        // particular). :) First off, we have to choose between using the
-        // OutlivesProjectionEnv, OutlivesProjectionTraitDef, and
-        // OutlivesProjectionComponent rules, any one of which is
-        // sufficient.  If there are no inference variables involved, it's
-        // not hard to pick the right rule, but if there are, we're in a
-        // bit of a catch 22: if we picked which rule we were going to
-        // use, we could add constraints to the region inference graph
-        // that make it apply, but if we don't add those constraints, the
-        // rule might not apply (but another rule might). For now, we err
-        // on the side of adding too few edges into the graph.
-
-        // Compute the bounds we can derive from the environment or trait
-        // definition.  We know that the projection outlives all the
-        // regions in this list.
-        let env_bounds = self.projection_declared_bounds(origin.span(), projection_ty);
-
-        debug!("projection_must_outlive: env_bounds={:?}", env_bounds);
-
-        // If we know that the projection outlives 'static, then we're
-        // done here.
-        if env_bounds.contains(&&ty::ReStatic) {
-            debug!("projection_must_outlive: 'static as declared bound");
-            return;
-        }
-
-        // If declared bounds list is empty, the only applicable rule is
-        // OutlivesProjectionComponent. If there are inference variables,
-        // then, we can break down the outlives into more primitive
-        // components without adding unnecessary edges.
-        //
-        // If there are *no* inference variables, however, we COULD do
-        // this, but we choose not to, because the error messages are less
-        // good. For example, a requirement like `T::Item: 'r` would be
-        // translated to a requirement that `T: 'r`; when this is reported
-        // to the user, it will thus say "T: 'r must hold so that T::Item:
-        // 'r holds". But that makes it sound like the only way to fix
-        // the problem is to add `T: 'r`, which isn't true. So, if there are no
-        // inference variables, we use a verify constraint instead of adding
-        // edges, which winds up enforcing the same condition.
-        let needs_infer = projection_ty.needs_infer();
-        if env_bounds.is_empty() && needs_infer {
-            debug!("projection_must_outlive: no declared bounds");
-
-            for component_ty in projection_ty.substs.types() {
-                self.type_must_outlive_pushing_obligations(origin.clone(), component_ty, region);
-            }
-
-            for r in projection_ty.substs.regions() {
-                self.infcx.sub_regions(origin.clone(), region, r);
-            }
-
-            return;
-        }
-
-        // If we find that there is a unique declared bound `'b`, and this bound
-        // appears in the trait reference, then the best action is to require that `'b:'r`,
-        // so do that. This is best no matter what rule we use:
-        //
-        // - OutlivesProjectionEnv or OutlivesProjectionTraitDef: these would translate to
-        // the requirement that `'b:'r`
-        // - OutlivesProjectionComponent: this would require `'b:'r` in addition to
-        // other conditions
-        if !env_bounds.is_empty() && env_bounds[1..].iter().all(|b| *b == env_bounds[0]) {
-            let unique_bound = env_bounds[0];
-            debug!(
-                "projection_must_outlive: unique declared bound = {:?}",
-                unique_bound
-            );
-            if projection_ty
-                .substs
-                .regions()
-                .any(|r| env_bounds.contains(&r))
-            {
-                debug!("projection_must_outlive: unique declared bound appears in trait ref");
-                self.infcx.sub_regions(origin.clone(), region, unique_bound);
-                return;
-            }
-        }
-
-        // Fallback to verifying after the fact that there exists a
-        // declared bound, or that all the components appearing in the
-        // projection outlive; in some cases, this may add insufficient
-        // edges into the inference graph, leading to inference failures
-        // even though a satisfactory solution exists.
-        let verify_bound = self.projection_bound(origin.span(), env_bounds, projection_ty);
-        let generic = GenericKind::Projection(projection_ty);
-        self.infcx
-            .verify_generic_bound(origin, generic.clone(), region, verify_bound);
-    }
-
-    fn type_bound(&mut self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
-        match ty.sty {
-            ty::TyParam(p) => self.param_bound(p),
-            ty::TyProjection(data) => {
-                let declared_bounds = self.projection_declared_bounds(span, data);
-                self.projection_bound(span, declared_bounds, data)
-            }
-            _ => self.recursive_type_bound(span, ty),
-        }
-    }
-
-    fn param_bound(&mut self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> {
-        debug!("param_bound(param_ty={:?})", param_ty);
-
-        let mut param_bounds = self.declared_generic_bounds_from_env(GenericKind::Param(param_ty));
-
-        // Add in the default bound of fn body that applies to all in
-        // scope type parameters:
-        param_bounds.extend(self.implicit_region_bound);
-
-        VerifyBound::AnyRegion(param_bounds)
-    }
-
-    fn projection_declared_bounds(
-        &mut self,
-        span: Span,
-        projection_ty: ty::ProjectionTy<'tcx>,
-    ) -> Vec<ty::Region<'tcx>> {
-        // First assemble bounds from where clauses and traits.
-
-        let mut declared_bounds =
-            self.declared_generic_bounds_from_env(GenericKind::Projection(projection_ty));
-
-        declared_bounds
-            .extend_from_slice(&mut self.declared_projection_bounds_from_trait(span, projection_ty));
-
-        declared_bounds
-    }
-
-    fn projection_bound(
-        &mut self,
-        span: Span,
-        declared_bounds: Vec<ty::Region<'tcx>>,
-        projection_ty: ty::ProjectionTy<'tcx>,
-    ) -> VerifyBound<'tcx> {
-        debug!(
-            "projection_bound(declared_bounds={:?}, projection_ty={:?})",
-            declared_bounds,
-            projection_ty
-        );
-
-        // see the extensive comment in projection_must_outlive
-        let ty = self.infcx
-            .tcx
-            .mk_projection(projection_ty.item_def_id, projection_ty.substs);
-        let recursive_bound = self.recursive_type_bound(span, ty);
-
-        VerifyBound::AnyRegion(declared_bounds).or(recursive_bound)
-    }
-
-    fn recursive_type_bound(&mut self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
-        let mut bounds = vec![];
-
-        for subty in ty.walk_shallow() {
-            bounds.push(self.type_bound(span, subty));
-        }
-
-        let mut regions = ty.regions();
-        regions.retain(|r| !r.is_late_bound()); // ignore late-bound regions
-        bounds.push(VerifyBound::AllRegions(regions));
-
-        // remove bounds that must hold, since they are not interesting
-        bounds.retain(|b| !b.must_hold());
-
-        if bounds.len() == 1 {
-            bounds.pop().unwrap()
-        } else {
-            VerifyBound::AllBounds(bounds)
-        }
-    }
-
-    fn declared_generic_bounds_from_env(
-        &mut self,
-        generic: GenericKind<'tcx>,
-    ) -> Vec<ty::Region<'tcx>> {
-        let tcx = self.tcx();
-
-        // To start, collect bounds from user:
-        let mut param_bounds =
-            tcx.required_region_bounds(generic.to_ty(tcx), self.param_env.caller_bounds.to_vec());
-
-        // Next, collect regions we scraped from the well-formedness
-        // constraints in the fn signature. To do that, we walk the list
-        // of known relations from the fn ctxt.
-        //
-        // This is crucial because otherwise code like this fails:
-        //
-        //     fn foo<'a, A>(x: &'a A) { x.bar() }
-        //
-        // The problem is that the type of `x` is `&'a A`. To be
-        // well-formed, then, A must be lower-generic by `'a`, but we
-        // don't know that this holds from first principles.
-        for &(r, p) in self.region_bound_pairs {
-            debug!("generic={:?} p={:?}", generic, p);
-            if generic == p {
-                param_bounds.push(r);
-            }
-        }
-
-        param_bounds
-    }
-
-    fn declared_projection_bounds_from_trait(
-        &mut self,
-        span: Span,
-        projection_ty: ty::ProjectionTy<'tcx>,
-    ) -> Vec<ty::Region<'tcx>> {
-        debug!("projection_bounds(projection_ty={:?})", projection_ty);
-        let ty = self.tcx()
-            .mk_projection(projection_ty.item_def_id, projection_ty.substs);
-
-        // Say we have a projection `<T as SomeTrait<'a>>::SomeType`. We are interested
-        // in looking for a trait definition like:
-        //
-        // ```
-        // trait SomeTrait<'a> {
-        //     type SomeType : 'a;
-        // }
-        // ```
-        //
-        // we can thus deduce that `<T as SomeTrait<'a>>::SomeType : 'a`.
-        let trait_predicates = self.tcx()
-            .predicates_of(projection_ty.trait_ref(self.tcx()).def_id);
-        assert_eq!(trait_predicates.parent, None);
-        let predicates = trait_predicates.predicates.as_slice().to_vec();
-        traits::elaborate_predicates(self.tcx(), predicates)
-            .filter_map(|predicate| {
-                // we're only interesting in `T : 'a` style predicates:
-                let outlives = match predicate {
-                    ty::Predicate::TypeOutlives(data) => data,
-                    _ => {
-                        return None;
-                    }
-                };
-
-                debug!("projection_bounds: outlives={:?} (1)", outlives);
-
-                // apply the substitutions (and normalize any projected types)
-                let outlives = outlives.subst(self.tcx(), projection_ty.substs);
-                let outlives = self.infcx.partially_normalize_associated_types_in(
-                    span,
-                    self.body_id,
-                    self.param_env,
-                    &outlives,
-                );
-                let outlives = self.register_infer_ok_obligations(outlives);
-
-                debug!("projection_bounds: outlives={:?} (2)", outlives);
-
-                let region_result = self.infcx
-                    .commit_if_ok(|_| {
-                        let (outlives, _) = self.infcx.replace_late_bound_regions_with_fresh_var(
-                            span,
-                            infer::AssocTypeProjection(projection_ty.item_def_id),
-                            &outlives,
-                        );
-
-                        debug!("projection_bounds: outlives={:?} (3)", outlives);
-
-                        // check whether this predicate applies to our current projection
-                        let cause = ObligationCause::new(
-                            span,
-                            self.body_id,
-                            ObligationCauseCode::MiscObligation,
-                        );
-                        match self.infcx.at(&cause, self.param_env).eq(outlives.0, ty) {
-                            Ok(ok) => Ok((ok, outlives.1)),
-                            Err(_) => Err(()),
-                        }
-                    })
-                    .map(|(ok, result)| {
-                        self.register_infer_ok_obligations(ok);
-                        result
-                    });
-
-                debug!("projection_bounds: region_result={:?}", region_result);
-
-                region_result.ok()
-            })
-            .collect()
-    }
-
-    fn register_infer_ok_obligations<T>(&mut self, infer_ok: InferOk<'tcx, T>) -> T {
-        let InferOk { value, obligations } = infer_ok;
-        self.obligations.extend(obligations);
-        value
-    }
-}