diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 6b53f835be5..f48a28965c6 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -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; } diff --git a/src/librustc/middle/free_region.rs b/src/librustc/middle/free_region.rs index 102cd001a29..9a5f0367231 100644 --- a/src/librustc/middle/free_region.rs +++ b/src/librustc/middle/free_region.rs @@ -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 @@ impl 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(..) => { } } } diff --git a/src/librustc/middle/implicator.rs b/src/librustc/middle/implicator.rs index 84fc2f7b2e5..21f09574a3f 100644 --- a/src/librustc/middle/implicator.rs +++ b/src/librustc/middle/implicator.rs @@ -278,9 +278,7 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> { 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 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> { } } - /// Given that there is a requirement that `Foo : 'a`, where - /// `Foo` is declared like `struct Foo where T : SomeTrait`, - /// this code finds all the associated types defined in - /// `SomeTrait` (and supertraits) and adds a requirement that `::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 index 00000000000..fa2b78b330d --- /dev/null +++ b/src/librustc/middle/outlives.rs @@ -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 or the MIT license +// , 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< >::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>), + + RFC1214(Vec>), +} + +/// 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> { + 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>) { + // 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) { + // } + // ``` + // + // 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 `>::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> { + 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>, regions: Vec) { + for r in regions { + if !r.is_bound() { + out.push(Component::Region(r)); + } + } +} + diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 2fe1f14d521..7d807bf2431 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -1670,6 +1670,13 @@ impl Region { } } + 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 @@ impl<'tcx> PolyProjectionPredicate<'tcx> { /// Represents the projection of an associated type. In explicit UFCS /// form this would be written `>::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 @@ impl<'tcx> TyS<'tcx> { } } + /// 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 { + 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 @@ impl<'tcx> RegionEscape for Ty<'tcx> { } } +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 index 00000000000..d7808599895 --- /dev/null +++ b/src/librustc/middle/wf.rs @@ -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 or the MIT license +// , 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>> +{ + 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`, then the trait reference `Foo: Set` 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> +{ + 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> +{ + 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> +{ + // 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>) + -> Vec> +{ + 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>, + rfc1214: bool +} + +impl<'a,'tcx> WfPredicates<'a,'tcx> { + fn rfc1214) -> 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> { + 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> + { + 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 +{ + // 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) +} + diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 99f375c3286..64cde0fccab 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -52,7 +52,7 @@ use middle::astconv_util::{prim_ty_to_ty, check_path_args, NO_TPS, NO_REGIONS}; 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};