]> git.lizzy.rs Git - rust.git/commitdiff
refactor how we extract outlives bounds from trait definitions
authorNiko Matsakis <niko@alum.mit.edu>
Fri, 3 Nov 2017 22:03:43 +0000 (18:03 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Wed, 15 Nov 2017 21:49:22 +0000 (16:49 -0500)
This new way is **slightly** less expressive (I would be shocked if it
affects any code, though) when it comes to higher-ranked bounds or a
few other weird tricks. But we don't handle those consistently
regardless, and the new way does not require normalization and is just
wildly simpler.

src/librustc/infer/region_obligations.rs
src/librustc/ty/mod.rs
src/librustc_typeck/check/regionck.rs

index edcabd531814291126bb9257cd4773c8abe4deec..4cd9c3f9ae39cd80c5b3e8861303af0943ef4c73 100644 (file)
 //!   *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 hir::def_id::DefId;
+use infer::{self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, VerifyBound};
+use traits;
 use ty::{self, Ty, TyCtxt, TypeFoldable};
-use ty::subst::Subst;
+use ty::subst::{Subst, Substs};
 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
@@ -120,19 +120,14 @@ pub fn process_registered_region_obligations(
         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,
-        );
+        let outlives =
+            TypeOutlives::new(self, region_bound_pairs, implicit_region_bound, param_env);
 
         for RegionObligation {
             sup_type,
@@ -147,11 +142,6 @@ pub fn process_registered_region_obligations(
 
             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
@@ -161,23 +151,13 @@ pub fn type_must_outlive(
         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,
-        );
+    ) {
+        let outlives =
+            TypeOutlives::new(self, region_bound_pairs, implicit_region_bound, param_env);
         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
@@ -196,11 +176,6 @@ struct TypeOutlives<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
     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> {
@@ -209,24 +184,15 @@ fn new(
         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).
     ///
@@ -236,7 +202,7 @@ fn into_accrued_obligations(self) -> PredicateObligations<'tcx> {
     /// - `ty`, the type `T`
     /// - `region`, the region `'a`
     fn type_must_outlive(
-        &mut self,
+        &self,
         origin: infer::SubregionOrigin<'tcx>,
         ty: Ty<'tcx>,
         region: ty::Region<'tcx>,
@@ -261,7 +227,7 @@ fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> {
     }
 
     fn components_must_outlive(
-        &mut self,
+        &self,
         origin: infer::SubregionOrigin<'tcx>,
         components: Vec<Component<'tcx>>,
         region: ty::Region<'tcx>,
@@ -295,7 +261,7 @@ fn components_must_outlive(
     }
 
     fn param_ty_must_outlive(
-        &mut self,
+        &self,
         origin: infer::SubregionOrigin<'tcx>,
         region: ty::Region<'tcx>,
         param_ty: ty::ParamTy,
@@ -314,7 +280,7 @@ fn param_ty_must_outlive(
     }
 
     fn projection_must_outlive(
-        &mut self,
+        &self,
         origin: infer::SubregionOrigin<'tcx>,
         region: ty::Region<'tcx>,
         projection_ty: ty::ProjectionTy<'tcx>,
@@ -343,7 +309,7 @@ fn projection_must_outlive(
         // 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);
+        let env_bounds = self.projection_declared_bounds(projection_ty);
 
         debug!("projection_must_outlive: env_bounds={:?}", env_bounds);
 
@@ -413,24 +379,24 @@ fn projection_must_outlive(
         // 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 verify_bound = self.projection_bound(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> {
+    fn type_bound(&self, 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)
+                let declared_bounds = self.projection_declared_bounds(data);
+                self.projection_bound(declared_bounds, data)
             }
-            _ => self.recursive_type_bound(span, ty),
+            _ => self.recursive_type_bound(ty),
         }
     }
 
-    fn param_bound(&mut self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> {
+    fn param_bound(&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));
@@ -443,8 +409,7 @@ fn param_bound(&mut self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> {
     }
 
     fn projection_declared_bounds(
-        &mut self,
-        span: Span,
+        &self,
         projection_ty: ty::ProjectionTy<'tcx>,
     ) -> Vec<ty::Region<'tcx>> {
         // First assemble bounds from where clauses and traits.
@@ -453,14 +418,13 @@ fn projection_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));
+            .extend_from_slice(&self.declared_projection_bounds_from_trait(projection_ty));
 
         declared_bounds
     }
 
     fn projection_bound(
-        &mut self,
-        span: Span,
+        &self,
         declared_bounds: Vec<ty::Region<'tcx>>,
         projection_ty: ty::ProjectionTy<'tcx>,
     ) -> VerifyBound<'tcx> {
@@ -474,16 +438,16 @@ fn projection_bound(
         let ty = self.infcx
             .tcx
             .mk_projection(projection_ty.item_def_id, projection_ty.substs);
-        let recursive_bound = self.recursive_type_bound(span, ty);
+        let recursive_bound = self.recursive_type_bound(ty);
 
         VerifyBound::AnyRegion(declared_bounds).or(recursive_bound)
     }
 
-    fn recursive_type_bound(&mut self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
+    fn recursive_type_bound(&self, ty: Ty<'tcx>) -> VerifyBound<'tcx> {
         let mut bounds = vec![];
 
         for subty in ty.walk_shallow() {
-            bounds.push(self.type_bound(span, subty));
+            bounds.push(self.type_bound(subty));
         }
 
         let mut regions = ty.regions();
@@ -501,7 +465,7 @@ fn recursive_type_bound(&mut self, span: Span, ty: Ty<'tcx>) -> VerifyBound<'tcx
     }
 
     fn declared_generic_bounds_from_env(
-        &mut self,
+        &self,
         generic: GenericKind<'tcx>,
     ) -> Vec<ty::Region<'tcx>> {
         let tcx = self.tcx();
@@ -531,89 +495,75 @@ fn declared_generic_bounds_from_env(
         param_bounds
     }
 
+    /// Given a projection like `<T as Foo<'x>>::Bar`, returns any bounds
+    /// declared in the trait definition. For example, if the trait were
+    ///
+    /// ```rust
+    /// trait Foo<'a> {
+    ///     type Bar: 'a;
+    /// }
+    /// ```
+    ///
+    /// then this function would return `'x`. This is subject to the
+    /// limitations around higher-ranked bounds described in
+    /// `region_bounds_declared_on_associated_item`.
     fn declared_projection_bounds_from_trait(
-        &mut self,
-        span: Span,
+        &self,
         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()
+        let mut bounds = self.region_bounds_declared_on_associated_item(projection_ty.item_def_id);
+        for r in &mut bounds {
+            *r = r.subst(self.tcx(), projection_ty.substs);
+        }
+        bounds
     }
 
-    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
+    /// Given the def-id of an associated item, returns any region
+    /// bounds attached to that associated item from the trait definition.
+    ///
+    /// For example:
+    ///
+    /// ```rust
+    /// trait Foo<'a> {
+    ///     type Bar: 'a;
+    /// }
+    /// ```
+    ///
+    /// If we were given the def-id of `Foo::Bar`, we would return
+    /// `'a`. You could then apply the substitutions from the
+    /// projection to convert this into your namespace. This also
+    /// works if the user writes `where <Self as Foo<'a>>::Bar: 'a` on
+    /// the trait. In fact, it works by searching for just such a
+    /// where-clause.
+    ///
+    /// It will not, however, work for higher-ranked bounds like:
+    ///
+    /// ```rust
+    /// trait Foo<'a, 'b>
+    /// where for<'x> <Self as Foo<'x, 'b>>::Bar: 'x
+    /// {
+    ///     type Bar;
+    /// }
+    /// ```
+    ///
+    /// This is for simplicity, and because we are not really smart
+    /// enough to cope with such bounds anywhere.
+    fn region_bounds_declared_on_associated_item(
+        &self,
+        assoc_item_def_id: DefId,
+    ) -> Vec<ty::Region<'tcx>> {
+        let tcx = self.tcx();
+        let assoc_item = tcx.associated_item(assoc_item_def_id);
+        let trait_def_id = assoc_item.container.assert_trait();
+        let trait_predicates = tcx.predicates_of(trait_def_id);
+        let identity_substs = Substs::identity_for_item(tcx, assoc_item_def_id);
+        let identity_proj = tcx.mk_projection(assoc_item_def_id, identity_substs);
+        traits::elaborate_predicates(tcx, trait_predicates.predicates)
+            .filter_map(|p| p.to_opt_type_outlives())
+            .filter_map(|p| tcx.no_late_bound_regions(&p))
+            .filter(|p| p.0 == identity_proj)
+            .map(|p| p.1)
+            .collect()
     }
 }
index bf1cc682a8aac2377b7375988302adf3d820bcb5..8973d1e0c5a5be024e41854e901ccd46f124435c 100644 (file)
@@ -144,6 +144,15 @@ pub enum AssociatedItemContainer {
 }
 
 impl AssociatedItemContainer {
+    /// Asserts that this is the def-id of an associated item declared
+    /// in a trait, and returns the trait def-id.
+    pub fn assert_trait(&self) -> DefId {
+        match *self {
+            TraitContainer(id) => id,
+            _ => bug!("associated item has wrong container type: {:?}", self)
+        }
+    }
+
     pub fn id(&self) -> DefId {
         match *self {
             TraitContainer(id) => id,
@@ -1200,6 +1209,25 @@ pub fn to_opt_poly_trait_ref(&self) -> Option<PolyTraitRef<'tcx>> {
             }
         }
     }
+
+    pub fn to_opt_type_outlives(&self) -> Option<PolyTypeOutlivesPredicate<'tcx>> {
+        match *self {
+            Predicate::TypeOutlives(data) => {
+                Some(data)
+            }
+            Predicate::Trait(..) |
+            Predicate::Projection(..) |
+            Predicate::Equate(..) |
+            Predicate::Subtype(..) |
+            Predicate::RegionOutlives(..) |
+            Predicate::WellFormed(..) |
+            Predicate::ObjectSafe(..) |
+            Predicate::ClosureKind(..) |
+            Predicate::ConstEvaluatable(..) => {
+                None
+            }
+        }
+    }
 }
 
 /// Represents the bounds declared on a particular set of type
index 20487dda20190d9d17df6b74fb88df1f08050ba4..7f1547c0c44d306a0f9fdf2221d769e3e634ffd8 100644 (file)
@@ -91,7 +91,7 @@
 use rustc::hir::def_id::DefId;
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, Ty, TypeFoldable};
-use rustc::infer::{self, InferOk, GenericKind};
+use rustc::infer::{self, GenericKind};
 use rustc::ty::adjustment;
 use rustc::ty::outlives::Component;
 use rustc::ty::wf;
@@ -357,21 +357,11 @@ fn visit_region_obligations(&mut self, node_id: ast::NodeId)
         // obligations. So make sure we process those.
         self.select_all_obligations_or_error();
 
-        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);
+        self.infcx.process_registered_region_obligations(
+            &self.region_bound_pairs,
+            self.implicit_region_bound,
+            self.param_env,
+            self.body_id);
     }
 
     /// This method populates the region map's `free_region_map`. It walks over the transformed
@@ -1137,14 +1127,12 @@ pub fn type_must_outlive(&self,
                              ty: Ty<'tcx>,
                              region: ty::Region<'tcx>)
     {
-        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)
+        self.infcx.type_must_outlive(&self.region_bound_pairs,
+                                     self.implicit_region_bound,
+                                     self.param_env,
+                                     origin,
+                                     ty,
+                                     region);
     }
 
     /// Computes the guarantor for an expression `&base` and then ensures that the lifetime of the