]> git.lizzy.rs Git - rust.git/commitdiff
Define the `wf` and `outlives` relation separately, unlike the existing
authorNiko Matsakis <niko@alum.mit.edu>
Thu, 6 Aug 2015 18:27:21 +0000 (14:27 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Wed, 12 Aug 2015 21:57:57 +0000 (17:57 -0400)
`implicator`. These definitions are also in accordance with RFC 1214 (or
more so), and hence somewhat different from the implicator. This commit
also modifies the implicator to remove the older rules for projections,
which can easily trigger infinite loops.

src/librustc/lib.rs
src/librustc/middle/free_region.rs
src/librustc/middle/implicator.rs
src/librustc/middle/outlives.rs [new file with mode: 0644]
src/librustc/middle/ty.rs
src/librustc/middle/wf.rs [new file with mode: 0644]
src/librustc_typeck/astconv.rs

index 6b53f835be59218b3405b6b5c75a9323bfa01654..f48a28965c685e37bf922550b6a786c97383a12b 100644 (file)
@@ -130,6 +130,7 @@ pub mod middle {
     pub mod lang_items;
     pub mod liveness;
     pub mod mem_categorization;
+    pub mod outlives;
     pub mod pat_util;
     pub mod privacy;
     pub mod reachable;
@@ -144,6 +145,7 @@ pub mod middle {
     pub mod ty_match;
     pub mod ty_relate;
     pub mod ty_walk;
+    pub mod wf;
     pub mod weak_lang_items;
 }
 
index 102cd001a296a563d070117c4a8c08749c24db47..9a5f03672316a6a54330f1c1da2624d8caa218d8 100644 (file)
@@ -10,7 +10,7 @@
 
 //! This file defines
 
-use middle::implicator::Implication;
+use middle::wf::ImpliedBound;
 use middle::ty::{self, FreeRegion};
 use util::common::can_reach;
 use util::nodemap::{FnvHashMap, FnvHashSet};
@@ -30,18 +30,19 @@ pub fn new() -> FreeRegionMap {
         FreeRegionMap { map: FnvHashMap(), statics: FnvHashSet() }
     }
 
-    pub fn relate_free_regions_from_implications<'tcx>(&mut self,
-                                                       implications: &[Implication<'tcx>])
+    pub fn relate_free_regions_from_implied_bounds<'tcx>(&mut self,
+                                                        implied_bounds: &[ImpliedBound<'tcx>])
     {
-        for implication in implications {
-            debug!("implication: {:?}", implication);
-            match *implication {
-                Implication::RegionSubRegion(_, ty::ReFree(free_a), ty::ReFree(free_b)) => {
+        debug!("relate_free_regions_from_implied_bounds()");
+        for implied_bound in implied_bounds {
+            debug!("implied bound: {:?}", implied_bound);
+            match *implied_bound {
+                ImpliedBound::RegionSubRegion(ty::ReFree(free_a), ty::ReFree(free_b)) => {
                     self.relate_free_regions(free_a, free_b);
                 }
-                Implication::RegionSubRegion(..) |
-                Implication::RegionSubGeneric(..) |
-                Implication::Predicate(..) => {
+                ImpliedBound::RegionSubRegion(..) |
+                ImpliedBound::RegionSubParam(..) |
+                ImpliedBound::RegionSubProjection(..) => {
                 }
             }
         }
index 84fc2f7b2e5401e9ebfd6d3225174be6570d72a3..21f09574a3fc884e38447b1a6b9e5fb9d6d5acf7 100644 (file)
@@ -278,9 +278,7 @@ fn accumulate_from_adt(&mut self,
 
         for predicate in predicates.predicates.as_slice() {
             match *predicate {
-                ty::Predicate::Trait(ref data) => {
-                    self.accumulate_from_assoc_types_transitive(data);
-                }
+                ty::Predicate::Trait(..) => { }
                 ty::Predicate::Equate(..) => { }
                 ty::Predicate::Projection(..) => { }
                 ty::Predicate::RegionOutlives(ref data) => {
@@ -349,53 +347,6 @@ fn accumulate_from_substs(&mut self,
         }
     }
 
-    /// Given that there is a requirement that `Foo<X> : 'a`, where
-    /// `Foo` is declared like `struct Foo<T> where T : SomeTrait`,
-    /// this code finds all the associated types defined in
-    /// `SomeTrait` (and supertraits) and adds a requirement that `<X
-    /// as SomeTrait>::N : 'a` (where `N` is some associated type
-    /// defined in `SomeTrait`). This rule only applies to
-    /// trait-bounds that are not higher-ranked, because we cannot
-    /// project out of a HRTB. This rule helps code using associated
-    /// types to compile, see Issue #22246 for an example.
-    fn accumulate_from_assoc_types_transitive(&mut self,
-                                              data: &ty::PolyTraitPredicate<'tcx>)
-    {
-        debug!("accumulate_from_assoc_types_transitive({:?})",
-               data);
-
-        for poly_trait_ref in traits::supertraits(self.tcx(), data.to_poly_trait_ref()) {
-            match self.tcx().no_late_bound_regions(&poly_trait_ref) {
-                Some(trait_ref) => { self.accumulate_from_assoc_types(trait_ref); }
-                None => { }
-            }
-        }
-    }
-
-    fn accumulate_from_assoc_types(&mut self,
-                                   trait_ref: ty::TraitRef<'tcx>)
-    {
-        debug!("accumulate_from_assoc_types({:?})",
-               trait_ref);
-
-        let trait_def_id = trait_ref.def_id;
-        let trait_def = self.tcx().lookup_trait_def(trait_def_id);
-        let assoc_type_projections: Vec<_> =
-            trait_def.associated_type_names
-                     .iter()
-                     .map(|&name| self.tcx().mk_projection(trait_ref.clone(), name))
-                     .collect();
-        debug!("accumulate_from_assoc_types: assoc_type_projections={:?}",
-               assoc_type_projections);
-        let tys = match self.fully_normalize(&assoc_type_projections) {
-            Ok(tys) => { tys }
-            Err(ErrorReported) => { return; }
-        };
-        for ty in tys {
-            self.accumulate_from_ty(ty);
-        }
-    }
-
     fn accumulate_from_object_ty(&mut self,
                                  ty: Ty<'tcx>,
                                  region_bound: ty::Region,
diff --git a/src/librustc/middle/outlives.rs b/src/librustc/middle/outlives.rs
new file mode 100644 (file)
index 0000000..fa2b78b
--- /dev/null
@@ -0,0 +1,191 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// The outlines relation `T: 'a` or `'a: 'b`.
+
+use middle::infer::InferCtxt;
+use middle::ty::{self, RegionEscape, Ty};
+
+#[derive(Debug)]
+pub enum Component<'tcx> {
+    Region(ty::Region),
+    Param(ty::ParamTy),
+    UnresolvedInferenceVariable(ty::InferTy),
+
+    // Projections like `T::Foo` are tricky because a constraint like
+    // `T::Foo: 'a` can be satisfied in so many ways. There may be a
+    // where-clause that says `T::Foo: 'a`, or the defining trait may
+    // include a bound like `type Foo: 'static`, or -- in the most
+    // conservative way -- we can prove that `T: 'a` (more generally,
+    // that all components in the projection outlive `'a`). This code
+    // is not in a position to judge which is the best technique, so
+    // we just product the projection as a component and leave it to
+    // the consumer to decide (but see `EscapingProjection` below).
+    Projection(ty::ProjectionTy<'tcx>),
+
+    // In the case where a projection has escaping regions -- meaning
+    // regions bound within the type itself -- we always use
+    // the most conservative rule, which requires that all components
+    // outlive the bound. So for example if we had a type like this:
+    //
+    //     for<'a> Trait1<  <T as Trait2<'a,'b>>::Foo  >
+    //                      ~~~~~~~~~~~~~~~~~~~~~~~~~
+    //
+    // then the inner projection (underlined) has an escaping region
+    // `'a`. We consider that outer trait `'c` to meet a bound if `'b`
+    // outlives `'b: 'c`, and we don't consider whether the trait
+    // declares that `Foo: 'static` etc. Therefore, we just return the
+    // free components of such a projection (in this case, `'b`).
+    //
+    // However, in the future, we may want to get smarter, and
+    // actually return a "higher-ranked projection" here. Therefore,
+    // we mark that these components are part of an escaping
+    // projection, so that implied bounds code can avoid relying on
+    // them. This gives us room to improve the regionck reasoning in
+    // the future without breaking backwards compat.
+    EscapingProjection(Vec<Component<'tcx>>),
+
+    RFC1214(Vec<Component<'tcx>>),
+}
+
+/// Returns all the things that must outlive `'a` for the condition
+/// `ty0: 'a` to hold.
+pub fn components<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
+                           ty0: Ty<'tcx>)
+                           -> Vec<Component<'tcx>> {
+    let mut components = vec![];
+    compute_components(infcx, ty0, &mut components);
+    debug!("outlives({:?}) = {:?}", ty0, components);
+    components
+}
+
+fn compute_components<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
+                               ty0: Ty<'tcx>,
+                               out: &mut Vec<Component<'tcx>>) {
+    // Descend through the types, looking for the various "base"
+    // components and collecting them into `out`. This is not written
+    // with `collect()` because of the need to sometimes skip subtrees
+    // in the `subtys` iterator (e.g., when encountering a
+    // projection).
+    let mut subtys = ty0.walk();
+    while let Some(ty) = subtys.next() {
+        match ty.sty {
+            ty::TyClosure(_, ref substs) => {
+                // FIXME(#27086). We do not accumulate from substs, since they
+                // don't represent reachable data. This means that, in
+                // practice, some of the lifetime parameters might not
+                // be in scope when the body runs, so long as there is
+                // no reachable data with that lifetime. For better or
+                // worse, this is consistent with fn types, however,
+                // which can also encapsulate data in this fashion
+                // (though it's somewhat harder, and typically
+                // requires virtual dispatch).
+                //
+                // Note that changing this (in a naive way, at least)
+                // causes regressions for what appears to be perfectly
+                // reasonable code like this:
+                //
+                // ```
+                // fn foo<'a>(p: &Data<'a>) {
+                //    bar(|q: &mut Parser| q.read_addr())
+                // }
+                // fn bar(p: Box<FnMut(&mut Parser)+'static>) {
+                // }
+                // ```
+                //
+                // Note that `p` (and `'a`) are not used in the
+                // closure at all, but to meet the requirement that
+                // the closure type `C: 'static` (so it can be coerced
+                // to the object type), we get the requirement that
+                // `'a: 'static` since `'a` appears in the closure
+                // type `C`.
+                //
+                // A smarter fix might "prune" unused `func_substs` --
+                // this would avoid breaking simple examples like
+                // this, but would still break others (which might
+                // indeed be invalid, depending on your POV). Pruning
+                // would be a subtle process, since we have to see
+                // what func/type parameters are used and unused,
+                // taking into consideration UFCS and so forth.
+
+                for &upvar_ty in &substs.upvar_tys {
+                    compute_components(infcx, upvar_ty, out);
+                }
+                subtys.skip_current_subtree();
+            }
+            ty::TyBareFn(..) | ty::TyTrait(..) => {
+                subtys.skip_current_subtree();
+                let temp = capture_components(infcx, ty);
+                out.push(Component::RFC1214(temp));
+            }
+            ty::TyParam(p) => {
+                out.push(Component::Param(p));
+                subtys.skip_current_subtree();
+            }
+            ty::TyProjection(ref data) => {
+                // For projections, we prefer to generate an
+                // obligation like `<P0 as Trait<P1...Pn>>::Foo: 'a`,
+                // because this gives the regionck more ways to prove
+                // that it holds. However, regionck is not (at least
+                // currently) prepared to deal with higher-ranked
+                // regions that may appear in the
+                // trait-ref. Therefore, if we see any higher-ranke
+                // regions, we simply fallback to the most restrictive
+                // rule, which requires that `Pi: 'a` for all `i`.
+
+                if !data.has_escaping_regions() {
+                    // best case: no escaping reions, so push the
+                    // projection and skip the subtree (thus
+                    // generating no constraints for Pi).
+                    out.push(Component::Projection(*data));
+                } else {
+                    // fallback case: continue walking through and
+                    // constrain Pi.
+                    let temp = capture_components(infcx, ty);
+                    out.push(Component::EscapingProjection(temp));
+                }
+                subtys.skip_current_subtree();
+            }
+            ty::TyInfer(_) => {
+                let ty = infcx.resolve_type_vars_if_possible(&ty);
+                if let ty::TyInfer(infer_ty) = ty.sty {
+                    out.push(Component::UnresolvedInferenceVariable(infer_ty));
+                } else {
+                    compute_components(infcx, ty, out);
+                }
+            }
+            _ => {
+                // for all other types, just constrain the regions and
+                // keep walking to find any other types.
+                push_region_constraints(out, ty.regions());
+            }
+        }
+    }
+}
+
+fn capture_components<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
+                               ty: Ty<'tcx>)
+                               -> Vec<Component<'tcx>> {
+    let mut temp = vec![];
+    push_region_constraints(&mut temp, ty.regions());
+    for subty in ty.walk_shallow() {
+        compute_components(infcx, subty, &mut temp);
+    }
+    temp
+}
+
+fn push_region_constraints<'tcx>(out: &mut Vec<Component<'tcx>>, regions: Vec<ty::Region>) {
+    for r in regions {
+        if !r.is_bound() {
+            out.push(Component::Region(r));
+        }
+    }
+}
+
index 2fe1f14d521ba3a7e881dbc36f8c9edfceba411d..7d807bf2431ff0adc39cba8cf270c5214c60efd0 100644 (file)
@@ -1670,6 +1670,13 @@ pub fn is_bound(&self) -> bool {
         }
     }
 
+    pub fn needs_infer(&self) -> bool {
+        match *self {
+            ty::ReInfer(..) => true,
+            _ => false
+        }
+    }
+
     pub fn escapes_depth(&self, depth: u32) -> bool {
         match *self {
             ty::ReLateBound(debruijn, _) => debruijn.depth > depth,
@@ -2567,7 +2574,7 @@ pub fn sort_key(&self) -> (ast::DefId, ast::Name) {
 
 /// Represents the projection of an associated type. In explicit UFCS
 /// form this would be written `<T as Trait<..>>::N`.
-#[derive(Clone, PartialEq, Eq, Hash, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
 pub struct ProjectionTy<'tcx> {
     /// The trait reference `T as Trait<..>`.
     pub trait_ref: ty::TraitRef<'tcx>,
@@ -4144,6 +4151,49 @@ pub fn is_param(&self, space: ParamSpace, index: u32) -> bool {
         }
     }
 
+    /// Returns the regions directly referenced from this type (but
+    /// not types reachable from this type via `walk_tys`). This
+    /// ignores late-bound regions binders.
+    pub fn regions(&self) -> Vec<ty::Region> {
+        match self.sty {
+            TyRef(region, _) => {
+                vec![*region]
+            }
+            TyTrait(ref obj) => {
+                let mut v = vec![obj.bounds.region_bound];
+                v.push_all(obj.principal.skip_binder().substs.regions().as_slice());
+                v
+            }
+            TyEnum(_, substs) |
+            TyStruct(_, substs) => {
+                substs.regions().as_slice().to_vec()
+            }
+            TyClosure(_, ref substs) => {
+                substs.func_substs.regions().as_slice().to_vec()
+            }
+            TyProjection(ref data) => {
+                data.trait_ref.substs.regions().as_slice().to_vec()
+            }
+            TyBareFn(..) |
+            TyBool |
+            TyChar |
+            TyInt(_) |
+            TyUint(_) |
+            TyFloat(_) |
+            TyBox(_) |
+            TyStr |
+            TyArray(_, _) |
+            TySlice(_) |
+            TyRawPtr(_) |
+            TyTuple(_) |
+            TyParam(_) |
+            TyInfer(_) |
+            TyError => {
+                vec![]
+            }
+        }
+    }
+
     /// Walks `ty` and any types appearing within `ty`, invoking the
     /// callback `f` on each type. If the callback returns false, then the
     /// children of the current type are ignored.
@@ -6951,6 +7001,20 @@ fn has_regions_escaping_depth(&self, depth: u32) -> bool {
     }
 }
 
+impl<'tcx> RegionEscape for TraitTy<'tcx> {
+    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
+        self.principal.has_regions_escaping_depth(depth) ||
+            self.bounds.has_regions_escaping_depth(depth)
+    }
+}
+
+impl<'tcx> RegionEscape for ExistentialBounds<'tcx> {
+    fn has_regions_escaping_depth(&self, depth: u32) -> bool {
+        self.region_bound.has_regions_escaping_depth(depth) ||
+            self.projection_bounds.has_regions_escaping_depth(depth)
+    }
+}
+
 impl<'tcx> RegionEscape for Substs<'tcx> {
     fn has_regions_escaping_depth(&self, depth: u32) -> bool {
         self.types.has_regions_escaping_depth(depth) ||
diff --git a/src/librustc/middle/wf.rs b/src/librustc/middle/wf.rs
new file mode 100644 (file)
index 0000000..d780859
--- /dev/null
@@ -0,0 +1,546 @@
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use middle::infer::InferCtxt;
+use middle::outlives::{self, Component};
+use middle::subst::Substs;
+use middle::traits;
+use middle::ty::{self, RegionEscape, ToPredicate, Ty};
+use std::iter::once;
+use std::mem;
+use std::rc::Rc;
+use syntax::ast;
+use syntax::codemap::Span;
+use util::common::ErrorReported;
+
+/// Returns the set of obligations needed to make `ty` well-formed.
+/// If `ty` contains unresolved inference variables, this may include
+/// further WF obligations. However, if `ty` IS an unresolved
+/// inference variable, returns `None`, because we are not able to
+/// make any progress at all. This is to prevent "livelock" where we
+/// say "$0 is WF if $0 is WF".
+pub fn obligations<'a,'tcx>(infcx: &InferCtxt<'a, 'tcx>,
+                            body_id: ast::NodeId,
+                            ty: Ty<'tcx>,
+                            span: Span,
+                            rfc1214: bool)
+                            -> Option<Vec<traits::PredicateObligation<'tcx>>>
+{
+    let mut wf = WfPredicates { infcx: infcx,
+                                body_id: body_id,
+                                span: span,
+                                out: vec![],
+                                rfc1214: rfc1214 };
+    if wf.compute(ty) {
+        debug!("wf::obligations({:?}, body_id={:?}) = {:?}", ty, body_id, wf.out);
+        let result = wf.normalize();
+        debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", ty, body_id, result);
+        Some(result)
+    } else {
+        None // no progress made, return None
+    }
+}
+
+/// Returns the obligations that make this trait reference
+/// well-formed.  For example, if there is a trait `Set` defined like
+/// `trait Set<K:Eq>`, then the trait reference `Foo: Set<Bar>` is WF
+/// if `Bar: Eq`.
+pub fn trait_obligations<'a,'tcx>(infcx: &InferCtxt<'a, 'tcx>,
+                                  body_id: ast::NodeId,
+                                  trait_ref: &ty::TraitRef<'tcx>,
+                                  span: Span,
+                                  rfc1214: bool)
+                                  -> Vec<traits::PredicateObligation<'tcx>>
+{
+    let mut wf = WfPredicates { infcx: infcx, body_id: body_id, span: span,
+                                out: vec![], rfc1214: rfc1214 };
+    wf.compute_trait_ref(trait_ref);
+    wf.normalize()
+}
+
+pub fn predicate_obligations<'a,'tcx>(infcx: &InferCtxt<'a, 'tcx>,
+                                      body_id: ast::NodeId,
+                                      predicate: &ty::Predicate<'tcx>,
+                                      span: Span,
+                                      rfc1214: bool)
+                                      -> Vec<traits::PredicateObligation<'tcx>>
+{
+    let mut wf = WfPredicates { infcx: infcx, body_id: body_id, span: span,
+                                out: vec![], rfc1214: rfc1214 };
+
+    // (*) ok to skip binders, because wf code is prepared for it
+    match *predicate {
+        ty::Predicate::Trait(ref t) => {
+            wf.compute_trait_ref(&t.skip_binder().trait_ref); // (*)
+        }
+        ty::Predicate::Equate(ref t) => {
+            wf.compute(t.skip_binder().0);
+            wf.compute(t.skip_binder().1);
+        }
+        ty::Predicate::RegionOutlives(..) => {
+        }
+        ty::Predicate::TypeOutlives(ref t) => {
+            wf.compute(t.skip_binder().0);
+        }
+        ty::Predicate::Projection(ref t) => {
+            let t = t.skip_binder(); // (*)
+            wf.compute_projection(t.projection_ty);
+            wf.compute(t.ty);
+        }
+        ty::Predicate::WellFormed(t) => {
+            wf.compute(t);
+        }
+        ty::Predicate::ObjectSafe(_) => {
+        }
+    }
+
+    wf.normalize()
+}
+
+/// Implied bounds are region relationships that we deduce
+/// automatically.  The idea is that (e.g.) a caller must check that a
+/// function's argument types are well-formed immediately before
+/// calling that fn, and hence the *callee* can assume that its
+/// argument types are well-formed. This may imply certain relationships
+/// between generic parameters. For example:
+///
+///     fn foo<'a,T>(x: &'a T)
+///
+/// can only be called with a `'a` and `T` such that `&'a T` is WF.
+/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
+#[derive(Debug)]
+pub enum ImpliedBound<'tcx> {
+    RegionSubRegion(ty::Region, ty::Region),
+    RegionSubParam(ty::Region, ty::ParamTy),
+    RegionSubProjection(ty::Region, ty::ProjectionTy<'tcx>),
+}
+
+/// This routine computes the full set of well-formedness constraints
+/// that must hold for the type `ty` to appear in a context with
+/// lifetime `outer_region`.
+pub fn implied_bounds<'a,'tcx>(
+    infcx: &'a InferCtxt<'a,'tcx>,
+    body_id: ast::NodeId,
+    ty: Ty<'tcx>,
+    span: Span)
+    -> Vec<ImpliedBound<'tcx>>
+{
+    // Sometimes when we ask what it takes for T: WF, we get back that
+    // U: WF is required; in that case, we push U onto this stack and
+    // process it next. Currently (at least) these resulting
+    // predicates are always guaranteed to be a subset of the original
+    // type, so we need not fear non-termination.
+    let mut wf_types = vec![ty];
+
+    let mut implied_bounds = vec![];
+
+    while let Some(ty) = wf_types.pop() {
+        // Compute the obligations for `ty` to be well-formed. If `ty` is
+        // an unresolved inference variable, just substituted an empty set
+        // -- because the return type here is going to be things we *add*
+        // to the environment, it's always ok for this set to be smaller
+        // than the ultimate set. (Note: normally there won't be
+        // unresolved inference variables here anyway, but there might be
+        // during typeck under some circumstances.)
+        let obligations = obligations(infcx, body_id, ty, span, false).unwrap_or(vec![]);
+
+        // From the full set of obligations, just filter down to the
+        // region relationships.
+        implied_bounds.extend(
+            obligations
+            .into_iter()
+            .flat_map(|obligation| {
+                assert!(!obligation.has_escaping_regions());
+                match obligation.predicate {
+                    ty::Predicate::Trait(..) |
+                    ty::Predicate::Equate(..) |
+                    ty::Predicate::Projection(..) |
+                    ty::Predicate::ObjectSafe(..) =>
+                        vec![],
+
+                    ty::Predicate::WellFormed(subty) => {
+                        wf_types.push(subty);
+                        vec![]
+                    }
+
+                    ty::Predicate::RegionOutlives(ref data) =>
+                        match infcx.tcx.no_late_bound_regions(data) {
+                            None =>
+                                vec![],
+                            Some(ty::OutlivesPredicate(r_a, r_b)) =>
+                                vec![ImpliedBound::RegionSubRegion(r_b, r_a)],
+                        },
+
+                    ty::Predicate::TypeOutlives(ref data) =>
+                        match infcx.tcx.no_late_bound_regions(data) {
+                            None => vec![],
+                            Some(ty::OutlivesPredicate(ty_a, r_b)) => {
+                                let components = outlives::components(infcx, ty_a);
+                                implied_bounds_from_components(r_b, components)
+                            }
+                        },
+                }}));
+    }
+
+    implied_bounds
+}
+
+/// When we have an implied bound that `T: 'a`, we can further break
+/// this down to determine what relationships would have to hold for
+/// `T: 'a` to hold. We get to assume that the caller has validated
+/// those relationships.
+fn implied_bounds_from_components<'tcx>(sub_region: ty::Region,
+                                        sup_components: Vec<Component<'tcx>>)
+                                        -> Vec<ImpliedBound<'tcx>>
+{
+    sup_components
+        .into_iter()
+        .flat_map(|component| {
+            match component {
+                Component::Region(r) =>
+                    vec!(ImpliedBound::RegionSubRegion(sub_region, r)),
+                Component::Param(p) =>
+                    vec!(ImpliedBound::RegionSubParam(sub_region, p)),
+                Component::Projection(p) =>
+                    vec!(ImpliedBound::RegionSubProjection(sub_region, p)),
+                Component::EscapingProjection(_) =>
+                    // If the projection has escaping regions, don't
+                    // try to infer any implied bounds even for its
+                    // free components. This is conservative, because
+                    // the caller will still have to prove that those
+                    // free components outlive `sub_region`. But the
+                    // idea is that the WAY that the caller proves
+                    // that may change in the future and we want to
+                    // give ourselves room to get smarter here.
+                    vec!(),
+                Component::UnresolvedInferenceVariable(..) =>
+                    vec!(),
+                Component::RFC1214(components) =>
+                    implied_bounds_from_components(sub_region, components),
+            }
+        })
+        .collect()
+}
+
+struct WfPredicates<'a,'tcx:'a> {
+    infcx: &'a InferCtxt<'a, 'tcx>,
+    body_id: ast::NodeId,
+    span: Span,
+    out: Vec<traits::PredicateObligation<'tcx>>,
+    rfc1214: bool
+}
+
+impl<'a,'tcx> WfPredicates<'a,'tcx> {
+    fn rfc1214<R,F:FnOnce(&mut WfPredicates<'a,'tcx>) -> R>(&mut self, f: F) -> R {
+        let b = mem::replace(&mut self.rfc1214, true);
+        let r = f(self);
+        self.rfc1214 = b;
+        r
+    }
+
+    fn cause(&mut self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> {
+        if !self.rfc1214 {
+            traits::ObligationCause::new(self.span, self.body_id, code)
+        } else {
+            let code = traits::ObligationCauseCode::RFC1214(Rc::new(code));
+            traits::ObligationCause::new(self.span, self.body_id, code)
+        }
+    }
+
+    fn normalize(&mut self) -> Vec<traits::PredicateObligation<'tcx>> {
+        let cause = self.cause(traits::MiscObligation);
+        let infcx = &mut self.infcx;
+        self.out.iter()
+                .inspect(|pred| assert!(!pred.has_escaping_regions()))
+                .flat_map(|pred| {
+                    let mut selcx = traits::SelectionContext::new(infcx);
+                    let pred = traits::normalize(&mut selcx, cause.clone(), pred);
+                    once(pred.value).chain(pred.obligations)
+                })
+                .collect()
+    }
+
+    fn compute_rfc1214(&mut self, ty: Ty<'tcx>) {
+        let b = mem::replace(&mut self.rfc1214, true);
+        for subty in ty.walk().skip(1) {
+            self.compute(subty);
+        }
+        self.rfc1214 = b;
+    }
+
+    /// Pushes the obligations required for `trait_ref` to be WF into
+    /// `self.out`.
+    fn compute_trait_ref(&mut self, trait_ref: &ty::TraitRef<'tcx>) {
+        let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.substs);
+        self.out.extend(obligations);
+
+        let cause = self.cause(traits::MiscObligation);
+        self.out.extend(
+            trait_ref.substs.types
+                            .as_slice()
+                            .iter()
+                            .filter(|ty| !ty.has_escaping_regions())
+                            .map(|ty| traits::Obligation::new(cause.clone(),
+                                                              ty::Predicate::WellFormed(ty))));
+    }
+
+    /// Pushes the obligations required for `trait_ref::Item` to be WF
+    /// into `self.out`.
+    fn compute_projection(&mut self, data: ty::ProjectionTy<'tcx>) {
+        // A projection is well-formed if (a) the trait ref itself is
+        // WF WF and (b) the trait-ref holds.  (It may also be
+        // normalizable and be WF that way.)
+
+        self.compute_trait_ref(&data.trait_ref);
+
+        if !data.has_escaping_regions() {
+            let predicate = data.trait_ref.to_predicate();
+            let cause = self.cause(traits::ProjectionWf(data));
+            self.out.push(traits::Obligation::new(cause, predicate));
+        }
+    }
+
+    /// Push new obligations into `out`. Returns true if it was able
+    /// to generate all the predicates needed to validate that `ty0`
+    /// is WF. Returns false if `ty0` is an unresolved type variable,
+    /// in which case we are not able to simplify at all.
+    fn compute(&mut self, ty0: Ty<'tcx>) -> bool {
+        let mut subtys = ty0.walk();
+        while let Some(ty) = subtys.next() {
+            match ty.sty {
+                ty::TyBool |
+                ty::TyChar |
+                ty::TyInt(..) |
+                ty::TyUint(..) |
+                ty::TyFloat(..) |
+                ty::TyError |
+                ty::TyStr |
+                ty::TyParam(_) => {
+                    // WfScalar, WfParameter, etc
+                }
+
+                ty::TySlice(subty) |
+                ty::TyArray(subty, _) => {
+                    self.rfc1214(|this| {
+                        if !subty.has_escaping_regions() {
+                            let cause = this.cause(traits::SliceOrArrayElem);
+                            match traits::trait_ref_for_builtin_bound(this.infcx.tcx,
+                                                                      ty::BoundSized,
+                                                                      subty) {
+                                Ok(trait_ref) => {
+                                    this.out.push(
+                                        traits::Obligation::new(cause,
+                                                                trait_ref.to_predicate()));
+                                }
+                                Err(ErrorReported) => { }
+                            }
+                        }
+                    })
+                }
+
+                ty::TyBox(_) |
+                ty::TyTuple(_) |
+                ty::TyRawPtr(_) => {
+                    // simple cases that are WF if their type args are WF
+                }
+
+                ty::TyProjection(data) => {
+                    subtys.skip_current_subtree(); // subtree handled by compute_projection
+                    self.compute_projection(data);
+                }
+
+                ty::TyEnum(def, substs) |
+                ty::TyStruct(def, substs) => {
+                    // WfNominalType
+                    let obligations = self.nominal_obligations(def.did, substs);
+                    self.out.extend(obligations);
+                }
+
+                ty::TyRef(r, mt) => {
+                    // WfReference
+                    if !r.has_escaping_regions() && !mt.ty.has_escaping_regions() {
+                        let cause = self.cause(traits::ReferenceOutlivesReferent(ty));
+                        self.out.push(
+                            traits::Obligation::new(
+                                cause,
+                                ty::Predicate::TypeOutlives(
+                                    ty::Binder(
+                                        ty::OutlivesPredicate(mt.ty, *r)))));
+                    }
+                }
+
+                ty::TyClosure(..) => {
+                    // the types in a closure are always the types of
+                    // local variables (or possibly references to local
+                    // variables), which are separately checked w/r/t
+                    // WFedness.
+                }
+
+                ty::TyBareFn(..) => {
+                    // process the bound types; because the old implicator
+                    // did not do this, go into RFC1214 mode.
+                    subtys.skip_current_subtree();
+                    self.compute_rfc1214(ty);
+                }
+
+                ty::TyTrait(ref data) => {
+                    // WfObject
+                    //
+                    // Here, we defer WF checking due to higher-ranked
+                    // regions. This is perhaps not ideal.
+                    self.from_object_ty(ty, data);
+
+                    // FIXME(#27579) RFC also considers adding trait
+                    // obligations that don't refer to Self and
+                    // checking those
+
+                    let cause = self.cause(traits::MiscObligation);
+                    self.out.push(
+                        traits::Obligation::new(
+                            cause,
+                            ty::Predicate::ObjectSafe(data.principal_def_id())));
+
+                    // process the bound types; because the old implicator
+                    // did not do this, go into RFC1214 mode.
+                    subtys.skip_current_subtree();
+                    self.compute_rfc1214(ty);
+                }
+
+                // Inference variables are the complicated case, since we don't
+                // know what type they are. We do two things:
+                //
+                // 1. Check if they have been resolved, and if so proceed with
+                //    THAT type.
+                // 2. If not, check whether this is the type that we
+                //    started with (ty0). In that case, we've made no
+                //    progress at all, so return false. Otherwise,
+                //    we've at least simplified things (i.e., we went
+                //    from `Vec<$0>: WF` to `$0: WF`, so we can
+                //    register a pending obligation and keep
+                //    moving. (Goal is that an "inductive hypothesis"
+                //    is satisfied to ensure termination.)
+                ty::TyInfer(_) => {
+                    let ty = self.infcx.shallow_resolve(ty);
+                    if let ty::TyInfer(_) = ty.sty { // not yet resolved...
+                        if ty == ty0 { // ...this is the type we started from! no progress.
+                            return false;
+                        }
+
+                        let cause = self.cause(traits::MiscObligation);
+                        self.out.push( // ...not the type we started from, so we made progress.
+                            traits::Obligation::new(cause, ty::Predicate::WellFormed(ty)));
+                    } else {
+                        // Yes, resolved, proceed with the
+                        // result. Should never return false because
+                        // `ty` is not a TyInfer.
+                        assert!(self.compute(ty));
+                    }
+                }
+            }
+        }
+
+        // if we made it through that loop above, we made progress!
+        return true;
+    }
+
+    fn nominal_obligations(&mut self,
+                           def_id: ast::DefId,
+                           substs: &Substs<'tcx>)
+                           -> Vec<traits::PredicateObligation<'tcx>>
+    {
+        let predicates =
+            self.infcx.tcx.lookup_predicates(def_id)
+                          .instantiate(self.infcx.tcx, substs);
+        let cause = self.cause(traits::ItemObligation(def_id));
+        predicates.predicates
+                  .into_iter()
+                  .map(|pred| traits::Obligation::new(cause.clone(), pred))
+                  .filter(|pred| !pred.has_escaping_regions())
+                  .collect()
+    }
+
+    fn from_object_ty(&mut self, ty: Ty<'tcx>, data: &ty::TraitTy<'tcx>) {
+        // Imagine a type like this:
+        //
+        //     trait Foo { }
+        //     trait Bar<'c> : 'c { }
+        //
+        //     &'b (Foo+'c+Bar<'d>)
+        //         ^
+        //
+        // In this case, the following relationships must hold:
+        //
+        //     'b <= 'c
+        //     'd <= 'c
+        //
+        // The first conditions is due to the normal region pointer
+        // rules, which say that a reference cannot outlive its
+        // referent.
+        //
+        // The final condition may be a bit surprising. In particular,
+        // you may expect that it would have been `'c <= 'd`, since
+        // usually lifetimes of outer things are conservative
+        // approximations for inner things. However, it works somewhat
+        // differently with trait objects: here the idea is that if the
+        // user specifies a region bound (`'c`, in this case) it is the
+        // "master bound" that *implies* that bounds from other traits are
+        // all met. (Remember that *all bounds* in a type like
+        // `Foo+Bar+Zed` must be met, not just one, hence if we write
+        // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and
+        // 'y.)
+        //
+        // Note: in fact we only permit builtin traits, not `Bar<'d>`, I
+        // am looking forward to the future here.
+
+        if !data.has_escaping_regions() {
+            let implicit_bounds =
+                object_region_bounds(self.infcx.tcx,
+                                     &data.principal,
+                                     data.bounds.builtin_bounds);
+
+            let explicit_bound = data.bounds.region_bound;
+
+            for implicit_bound in implicit_bounds {
+                let cause = self.cause(traits::ReferenceOutlivesReferent(ty));
+                let outlives = ty::Binder(ty::OutlivesPredicate(explicit_bound, implicit_bound));
+                self.out.push(traits::Obligation::new(cause, outlives.to_predicate()));
+            }
+        }
+    }
+}
+
+/// Given an object type like `SomeTrait+Send`, computes the lifetime
+/// bounds that must hold on the elided self type. These are derived
+/// from the declarations of `SomeTrait`, `Send`, and friends -- if
+/// they declare `trait SomeTrait : 'static`, for example, then
+/// `'static` would appear in the list. The hard work is done by
+/// `ty::required_region_bounds`, see that for more information.
+pub fn object_region_bounds<'tcx>(
+    tcx: &ty::ctxt<'tcx>,
+    principal: &ty::PolyTraitRef<'tcx>,
+    others: ty::BuiltinBounds)
+    -> Vec<ty::Region>
+{
+    // Since we don't actually *know* the self type for an object,
+    // this "open(err)" serves as a kind of dummy standin -- basically
+    // a skolemized type.
+    let open_ty = tcx.mk_infer(ty::FreshTy(0));
+
+    // Note that we preserve the overall binding levels here.
+    assert!(!open_ty.has_escaping_regions());
+    let substs = tcx.mk_substs(principal.0.substs.with_self_ty(open_ty));
+    let trait_refs = vec!(ty::Binder(ty::TraitRef::new(principal.0.def_id, substs)));
+
+    let mut predicates = others.to_predicates(tcx, open_ty);
+    predicates.extend(trait_refs.iter().map(|t| t.to_predicate()));
+
+    tcx.required_region_bounds(open_ty, predicates)
+}
+
index 99f375c32868e295398c9b453ebc7b507ae569fe..64cde0fccabaad9f49070d454260428e2b842916 100644 (file)
@@ -52,7 +52,7 @@
 use middle::const_eval::{self, ConstVal};
 use middle::const_eval::EvalHint::UncheckedExprHint;
 use middle::def;
-use middle::implicator::object_region_bounds;
+use middle::wf::object_region_bounds;
 use middle::resolve_lifetime as rl;
 use middle::privacy::{AllPublic, LastMod};
 use middle::subst::{FnSpace, TypeSpace, SelfSpace, Subst, Substs, ParamSpace};