From 9c0413be68f8b3c4197ee4f4fe9f4ca9e6ce2224 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 29 May 2015 11:04:04 -0400 Subject: [PATCH] Modify implicator to take a "syntactic" approach, ensuring that fns and object types impose minimal constraints on their arguments. Also modify regionck to consider `>::Foo: 'r` to hold if `Pi: 'r` (for all `i`). This is a fallback, because in some cases we can impose looser requirements. Currently the inference is sound but not precise. Fixes #24622. Adapted cherry-pick of 45f93290969f19d8217a2e4dff7b12dc81957eb6. --- src/librustc/middle/expr_use_visitor.rs | 2 +- src/librustc/middle/implicator.rs | 41 +- src/librustc/middle/infer/error_reporting.rs | 10 +- src/librustc/middle/infer/mod.rs | 8 +- .../middle/infer/region_inference/mod.rs | 152 +++++-- src/librustc/middle/ty.rs | 57 ++- src/librustc_typeck/check/dropck.rs | 17 +- src/librustc_typeck/check/regionck.rs | 388 ++++++++++++++++-- .../compile-fail/associated-types-outlives.rs | 36 ++ ...ions-assoc-type-outlives-container-hrtb.rs | 4 +- .../regions-assoc-type-outlives-container.rs | 5 +- ...gions-close-associated-type-into-object.rs | 9 +- .../regions-close-object-into-object-1.rs | 2 +- .../regions-close-object-into-object-2.rs | 2 +- .../regions-close-object-into-object-3.rs | 2 +- .../regions-close-object-into-object-4.rs | 2 +- .../regions-close-object-into-object-5.rs | 13 +- .../compile-fail/variance-regions-direct.rs | 2 +- src/test/run-pass/issue-5708.rs | 4 +- .../regions-early-bound-trait-param.rs | 2 +- 20 files changed, 651 insertions(+), 107 deletions(-) create mode 100644 src/test/compile-fail/associated-types-outlives.rs diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 0d204a823af04..f58d2885671fb 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -239,7 +239,7 @@ impl OverloadedCallType { // supplies types from the tree. After type checking is complete, you // can just use the tcx as the typer. -pub struct ExprUseVisitor<'d,'t,'a: 't, 'tcx:'a> { +pub struct ExprUseVisitor<'d, 't, 'a: 't, 'tcx:'a+'d> { typer: &'t infer::InferCtxt<'a, 'tcx>, mc: mc::MemCategorizationContext<'t, 'a, 'tcx>, delegate: &'d mut (Delegate<'tcx>+'d), diff --git a/src/librustc/middle/implicator.rs b/src/librustc/middle/implicator.rs index faa1f7232b574..2ff9f84d12043 100644 --- a/src/librustc/middle/implicator.rs +++ b/src/librustc/middle/implicator.rs @@ -41,8 +41,9 @@ struct Implicator<'a, 'tcx: 'a> { visited: FnvHashSet>, } -/// This routine computes the well-formedness constraints that must hold for the type `ty` to -/// appear in a context with lifetime `outer_region` +/// 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 implications<'a,'tcx>( infcx: &'a InferCtxt<'a,'tcx>, body_id: ast::NodeId, @@ -90,12 +91,15 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> { ty::TyInt(..) | ty::TyUint(..) | ty::TyFloat(..) | - ty::TyBareFn(..) | ty::TyError | ty::TyStr => { // No borrowed content reachable here. } + ty::TyBareFn(..) => { + self.accumulate_referenced_regions_and_types(ty); + } + ty::TyClosure(def_id, substs) => { let &(r_a, opt_ty) = self.stack.last().unwrap(); self.out.push(Implication::RegionSubClosure(opt_ty, r_a, def_id, substs)); @@ -382,6 +386,37 @@ impl<'a, 'tcx> Implicator<'a, 'tcx> { // constraint above self.out.push(Implication::RegionSubRegion(Some(ty), r_d, r_c)); } + + self.accumulate_referenced_regions_and_types(ty); + } + + fn accumulate_referenced_regions_and_types(&mut self, + iface_ty: Ty<'tcx>) + { + // for now, `iface_ty` represents some type that is a fn or + // trait object argument, and because those appear underneath + // forall binders, we do not enforce the full WF requirements, + // just the lifetime "outlives" requirements. + for ty in iface_ty.walk() { + match ty.sty { + ty::TyParam(p) => { + self.push_param_constraint_from_top(p); + } + + _ => { } + } + + for r in ty.regions() { + match r { + ty::ReLateBound(..) => { + // skip late-bound regions + } + _ => { + self.push_region_constraint_from_top(r); + } + } + } + } } fn fully_normalize(&self, value: &T) -> Result diff --git a/src/librustc/middle/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs index 8d226739e1688..33b7d0c33fbdd 100644 --- a/src/librustc/middle/infer/error_reporting.rs +++ b/src/librustc/middle/infer/error_reporting.rs @@ -241,8 +241,7 @@ pub trait ErrorReporting<'tcx> { fn report_generic_bound_failure(&self, origin: SubregionOrigin<'tcx>, kind: GenericKind<'tcx>, - sub: Region, - sups: Vec); + sub: Region); fn report_sub_sup_conflict(&self, var_origin: RegionVariableOrigin, @@ -294,8 +293,8 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { self.report_concrete_failure(origin, sub, sup); } - GenericBoundFailure(kind, param_ty, sub, sups) => { - self.report_generic_bound_failure(kind, param_ty, sub, sups); + GenericBoundFailure(kind, param_ty, sub) => { + self.report_generic_bound_failure(kind, param_ty, sub); } SubSupConflict(var_origin, @@ -529,8 +528,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { fn report_generic_bound_failure(&self, origin: SubregionOrigin<'tcx>, bound_kind: GenericKind<'tcx>, - sub: Region, - _sups: Vec) + sub: Region) { // FIXME: it would be better to report the first error message // with the span of the parameter itself, rather than the span diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index 63f31921ec2ec..8a5a6b9596492 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -20,7 +20,7 @@ pub use self::ValuePairs::*; pub use self::fixup_err::*; pub use middle::ty::IntVarValue; pub use self::freshen::TypeFreshener; -pub use self::region_inference::GenericKind; +pub use self::region_inference::{GenericKind, VerifyBound}; use middle::free_region::FreeRegionMap; use middle::mem_categorization as mc; @@ -1284,13 +1284,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { origin: SubregionOrigin<'tcx>, kind: GenericKind<'tcx>, a: ty::Region, - bs: Vec) { + bound: VerifyBound) { debug!("verify_generic_bound({:?}, {:?} <: {:?})", kind, a, - bs); + bound); - self.region_vars.verify_generic_bound(origin, kind, a, bs); + self.region_vars.verify_generic_bound(origin, kind, a, bound); } pub fn can_equate<'b,T>(&'b self, a: &T, b: &T) -> UnitResult<'tcx> diff --git a/src/librustc/middle/infer/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs index 891a39e723a62..7a60c909907ff 100644 --- a/src/librustc/middle/infer/region_inference/mod.rs +++ b/src/librustc/middle/infer/region_inference/mod.rs @@ -65,15 +65,41 @@ pub enum Verify<'tcx> { // outlive `RS`. Therefore verify that `R <= RS[i]` for some // `i`. Inference variables may be involved (but this verification // step doesn't influence inference). - VerifyGenericBound(GenericKind<'tcx>, SubregionOrigin<'tcx>, Region, Vec), + VerifyGenericBound(GenericKind<'tcx>, SubregionOrigin<'tcx>, Region, VerifyBound), } -#[derive(Clone, PartialEq, Eq)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum GenericKind<'tcx> { Param(ty::ParamTy), Projection(ty::ProjectionTy<'tcx>), } +// When we introduce a verification step, we wish to test that a +// particular region (let's call it `'min`) meets some bound. +// The bound is described the by the following grammar: +#[derive(Debug)] +pub enum VerifyBound { + // B = exists {R} --> some 'r in {R} must outlive 'min + // + // Put another way, the subject value is known to outlive all + // regions in {R}, so if any of those outlives 'min, then the + // bound is met. + AnyRegion(Vec), + + // B = forall {R} --> all 'r in {R} must outlive 'min + // + // Put another way, the subject value is known to outlive some + // region in {R}, so if all of those outlives 'min, then the bound + // is met. + AllRegions(Vec), + + // B = exists {B} --> 'min must meet some bound b in {B} + AnyBound(Vec), + + // B = forall {B} --> 'min must meet all bounds b in {B} + AllBounds(Vec), +} + #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct TwoRegions { a: Region, @@ -103,12 +129,11 @@ pub enum RegionResolutionError<'tcx> { /// `o` requires that `a <= b`, but this does not hold ConcreteFailure(SubregionOrigin<'tcx>, Region, Region), - /// `GenericBoundFailure(p, s, a, bs) + /// `GenericBoundFailure(p, s, a) /// /// The parameter/associated-type `p` must be known to outlive the lifetime - /// `a`, but it is only known to outlive `bs` (and none of the - /// regions in `bs` outlive `a`). - GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region, Vec), + /// `a` (but none of the known bounds are sufficient). + GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region), /// `SubSupConflict(v, sub_origin, sub_r, sup_origin, sup_r)`: /// @@ -409,6 +434,14 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { debug!("RegionVarBindings: add_verify({:?})", verify); + // skip no-op cases known to be satisfied + match verify { + VerifyGenericBound(_, _, _, VerifyBound::AllBounds(ref bs)) if bs.len() == 0 => { + return; + } + _ => { } + } + let mut verifys = self.verifys.borrow_mut(); let index = verifys.len(); verifys.push(verify); @@ -498,8 +531,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { origin: SubregionOrigin<'tcx>, kind: GenericKind<'tcx>, sub: Region, - sups: Vec) { - self.add_verify(VerifyGenericBound(kind, origin, sub, sups)); + bound: VerifyBound) { + self.add_verify(VerifyGenericBound(kind, origin, sub, bound)); } pub fn lub_regions(&self, @@ -664,12 +697,11 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { &mut result_set, r, a, b); } - VerifyGenericBound(_, _, a, ref bs) => { - for &b in bs { - consider_adding_bidirectional_edges( - &mut result_set, r, - a, b); - } + VerifyGenericBound(_, _, a, ref bound) => { + bound.for_each_region(&mut |b| { + consider_adding_bidirectional_edges(&mut result_set, r, + a, b) + }); } } } @@ -1264,20 +1296,13 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { errors.push(ConcreteFailure((*origin).clone(), sub, sup)); } - VerifyGenericBound(ref kind, ref origin, sub, ref sups) => { + VerifyGenericBound(ref kind, ref origin, sub, ref bound) => { let sub = normalize(values, sub); - if sups.iter() - .map(|&sup| normalize(values, sup)) - .any(|sup| free_regions.is_subregion_of(self.tcx, sub, sup)) - { + if bound.is_met(self.tcx, free_regions, values, sub) { continue; } - let sups = sups.iter().map(|&sup| normalize(values, sup)) - .collect(); - errors.push( - GenericBoundFailure( - (*origin).clone(), kind.clone(), sub, sups)); + errors.push(GenericBoundFailure((*origin).clone(), kind.clone(), sub)); } } } @@ -1723,3 +1748,82 @@ impl<'tcx> GenericKind<'tcx> { } } } + +impl VerifyBound { + fn for_each_region(&self, f: &mut FnMut(ty::Region)) { + match self { + &VerifyBound::AnyRegion(ref rs) | + &VerifyBound::AllRegions(ref rs) => + for &r in rs { f(r); }, + + &VerifyBound::AnyBound(ref bs) | + &VerifyBound::AllBounds(ref bs) => + for b in bs { b.for_each_region(f); }, + } + } + + pub fn must_hold(&self) -> bool { + match self { + &VerifyBound::AnyRegion(ref bs) => bs.contains(&ty::ReStatic), + &VerifyBound::AllRegions(ref bs) => bs.is_empty(), + &VerifyBound::AnyBound(ref bs) => bs.iter().any(|b| b.must_hold()), + &VerifyBound::AllBounds(ref bs) => bs.iter().all(|b| b.must_hold()), + } + } + + pub fn cannot_hold(&self) -> bool { + match self { + &VerifyBound::AnyRegion(ref bs) => bs.is_empty(), + &VerifyBound::AllRegions(ref bs) => bs.contains(&ty::ReEmpty), + &VerifyBound::AnyBound(ref bs) => bs.iter().all(|b| b.cannot_hold()), + &VerifyBound::AllBounds(ref bs) => bs.iter().any(|b| b.cannot_hold()), + } + } + + pub fn or(self, vb: VerifyBound) -> VerifyBound { + if self.must_hold() || vb.cannot_hold() { + self + } else if self.cannot_hold() || vb.must_hold() { + vb + } else { + VerifyBound::AnyBound(vec![self, vb]) + } + } + + pub fn and(self, vb: VerifyBound) -> VerifyBound { + if self.must_hold() && vb.must_hold() { + self + } else if self.cannot_hold() && vb.cannot_hold() { + self + } else { + VerifyBound::AllBounds(vec![self, vb]) + } + } + + fn is_met<'tcx>(&self, + tcx: &ty::ctxt<'tcx>, + free_regions: &FreeRegionMap, + var_values: &Vec, + min: ty::Region) + -> bool { + match self { + &VerifyBound::AnyRegion(ref rs) => + rs.iter() + .map(|&r| normalize(var_values, r)) + .any(|r| free_regions.is_subregion_of(tcx, min, r)), + + &VerifyBound::AllRegions(ref rs) => + rs.iter() + .map(|&r| normalize(var_values, r)) + .all(|r| free_regions.is_subregion_of(tcx, min, r)), + + &VerifyBound::AnyBound(ref bs) => + bs.iter() + .any(|b| b.is_met(tcx, free_regions, var_values, min)), + + &VerifyBound::AllBounds(ref bs) => + bs.iter() + .all(|b| b.is_met(tcx, free_regions, var_values, min)), + } + } +} diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 452f16d492ef6..08411ad430557 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -1635,6 +1635,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, @@ -2456,7 +2463,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>, @@ -3735,6 +3742,13 @@ impl<'tcx> TyS<'tcx> { } } + pub fn is_any_param(&self) -> bool { + match self.sty { + ty::TyParam(_) => true, + _ => false, + } + } + pub fn is_param(&self, space: ParamSpace, index: u32) -> bool { match self.sty { ty::TyParam(ref data) => data.space == space && data.idx == index, @@ -3742,6 +3756,47 @@ 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(_, substs) => { + substs.regions().as_slice().to_vec() + } + TyBareFn(..) | + TyBool | + TyChar | + TyInt(_) | + TyUint(_) | + TyFloat(_) | + TyBox(_) | + TyStr | + TyArray(_, _) | + TySlice(_) | + TyRawPtr(_) | + TyTuple(_) | + TyProjection(_) | + 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. diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs index 7d911cf8b03bc..65a5536c23ba3 100644 --- a/src/librustc_typeck/check/dropck.rs +++ b/src/librustc_typeck/check/dropck.rs @@ -253,9 +253,9 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( /// This function is meant to by applied to the type for every /// expression in the program. pub fn check_safety_of_destructor_if_necessary<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, - typ: ty::Ty<'tcx>, - span: Span, - scope: region::CodeExtent) { + typ: ty::Ty<'tcx>, + span: Span, + scope: region::CodeExtent) { debug!("check_safety_of_destructor_if_necessary typ: {:?} scope: {:?}", typ, scope); @@ -448,16 +448,7 @@ fn iterate_over_potentially_unsafe_regions_in_type<'a, 'tcx>( // borrowed data is torn down in between the end of // `scope` and when the destructor itself actually runs.) - let parent_region = - match rcx.tcx().region_maps.opt_encl_scope(scope) { - Some(parent_scope) => ty::ReScope(parent_scope), - None => rcx.tcx().sess.span_bug( - span, &format!("no enclosing scope found for scope: {:?}", - scope)), - }; - - regionck::type_must_outlive(rcx, origin(), typ, parent_region); - + regionck::type_strictly_outlives(rcx, origin(), typ, scope); } else { // Okay, `typ` itself is itself not reachable by a // destructor; but it may contain substructure that has a diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index bb3c9f9fb5425..9727dadb93f7f 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -92,7 +92,7 @@ use middle::region::CodeExtent; use middle::subst::Substs; use middle::traits; use middle::ty::{self, ReScope, Ty, MethodCall, HasTypeFlags}; -use middle::infer::{self, GenericKind}; +use middle::infer::{self, GenericKind, InferCtxt, VerifyBound}; use middle::pat_util; use std::mem; @@ -214,6 +214,10 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> { self.fcx.ccx.tcx } + pub fn infcx(&self) -> &InferCtxt<'a,'tcx> { + self.fcx.infcx() + } + fn set_body_id(&mut self, body_id: ast::NodeId) -> ast::NodeId { mem::replace(&mut self.body_id, body_id) } @@ -1398,11 +1402,13 @@ fn link_reborrowed_region<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, } } -/// Ensures that all borrowed data reachable via `ty` outlives `region`. +/// Ensures that type is well-formed in `region`, which implies (among +/// other things) that all borrowed data reachable via `ty` outlives +/// `region`. pub fn type_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, - origin: infer::SubregionOrigin<'tcx>, - ty: Ty<'tcx>, - region: ty::Region) + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region) { debug!("type_must_outlive(ty={:?}, region={:?})", ty, @@ -1410,6 +1416,138 @@ pub fn type_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, let implications = implicator::implications(rcx.fcx.infcx(), rcx.body_id, ty, region, origin.span()); + check_implications(rcx, origin, implications); +} + +/// Checks that all data reachable via `ty` *strictly* outlives `scope`. +/// This is used by dropck. +/// +/// CAUTION: It is mostly but not entirely equivalent to `T:'parent` +/// where `'parent` is the parent of `scope`. The difference is subtle +/// and has to do with trait objects, primarily. In particular, if you +/// have `Foo<'y>+'z`, then we require that `'z:'parent` but not +/// `'y:'parent` (same with lifetimes appearing in fn arguments). This +/// is because there is no actual reference to the trait object that +/// outlives `scope`, so we don't need to require that the type could +/// be named outside `scope`. Because trait objects are always +/// considered "suspicious" by dropck, if we don't add this special +/// case, you wind up with some kind of annoying and odd limitations +/// that crop up +/// `src/test/compile-fail/regions-early-bound-trait-param.rs`. +/// Basically there we have `&'foo Trait<'foo>+'bar`, and thus forcing +/// `'foo` to outlive `'parent` also forces the borrow to outlive +/// `'parent`, which is longer than should be necessary. The existence +/// of this "the same but different" predicate is somewhat bothersome +/// and questionable. +pub fn type_strictly_outlives<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + scope: CodeExtent) +{ + debug!("type_strictly_outlives(ty={:?}, scope={:?})", + ty, scope); + + let span = origin.span(); + + let parent_region = + match rcx.tcx().region_maps.opt_encl_scope(scope) { + Some(parent_scope) => ty::ReScope(parent_scope), + None => rcx.tcx().sess.span_bug( + span, &format!("no enclosing scope found for scope: {:?}", + scope)), + }; + + helper(rcx, origin, ty, parent_region); + + // TODO this is bad because it is a copy-and-paste of implicator, + // which in turn highlights that this is NOT QUITE `Foo:'r`. + fn helper<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + parent_region: ty::Region) + { + debug!("type_strictly_outlives::helper: ty={:?} parent_region={:?}", + ty, parent_region); + + match ty.sty { + ty::TyBool | + ty::TyChar | + ty::TyInt(..) | + ty::TyUint(..) | + ty::TyFloat(..) | + ty::TyError | + ty::TyStr => { + // No borrowed content reachable here. + } + + ty::TyBareFn(..) => { + // TODO bare fn bounds? + } + + ty::TyClosure(def_id, substs) => { + closure_must_outlive(rcx, origin, parent_region, def_id, substs); + } + + ty::TyTrait(ref t) => { + rcx.fcx.mk_subr(origin, parent_region, t.bounds.region_bound); + } + + ty::TyEnum(_, substs) | + ty::TyStruct(_, substs) => { + for &ty in substs.types.iter() { + helper(rcx, origin.clone(), ty, parent_region); + } + + for &r in substs.regions().iter() { + rcx.fcx.mk_subr(origin.clone(), parent_region, r); + } + } + + ty::TyArray(t, _) | + ty::TySlice(t) | + ty::TyRawPtr(ty::mt { ty: t, .. }) | + ty::TyBox(t) => { + helper(rcx, origin, t, parent_region); + } + + ty::TyRef(&r_b, _) => { + rcx.fcx.mk_subr(origin, parent_region, r_b); + } + + ty::TyParam(p) => { + generic_must_outlive(rcx, origin.clone(), + parent_region, GenericKind::Param(p)); + } + + ty::TyProjection(data) => { + // `>::Name` + generic_must_outlive(rcx, origin.clone(), + parent_region, GenericKind::Projection(data)); + } + + ty::TyTuple(ref tuptys) => { + for &tupty in tuptys { + helper(rcx, origin.clone(), tupty, parent_region); + } + } + + ty::TyInfer(_) => { + // This should not happen, BUT: + // + // Currently we uncover region relationships on + // entering the fn check. We should do this after + // the fn check, then we can call this case a bug(). + } + } + } +} + +fn check_implications<'a,'tcx>(rcx: &mut Rcx<'a,'tcx>, + origin: infer::SubregionOrigin<'tcx>, + implications: Vec>) +{ + debug!("check_implications"); + for implication in implications { debug!("implication: {:?}", implication); match implication { @@ -1420,10 +1558,10 @@ pub fn type_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, let o1 = infer::ReferenceOutlivesReferent(ty, origin.span()); rcx.fcx.mk_subr(o1, r_a, r_b); } - implicator::Implication::RegionSubGeneric(None, r_a, ref generic_b) => { + implicator::Implication::RegionSubGeneric(None, r_a, generic_b) => { generic_must_outlive(rcx, origin.clone(), r_a, generic_b); } - implicator::Implication::RegionSubGeneric(Some(ty), r_a, ref generic_b) => { + implicator::Implication::RegionSubGeneric(Some(ty), r_a, generic_b) => { let o1 = infer::ReferenceOutlivesReferent(ty, origin.span()); generic_must_outlive(rcx, o1, r_a, generic_b); } @@ -1458,34 +1596,219 @@ fn closure_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, } } -fn generic_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, +fn generic_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, origin: infer::SubregionOrigin<'tcx>, region: ty::Region, - generic: &GenericKind<'tcx>) { - let param_env = &rcx.fcx.inh.infcx.parameter_environment; + generic: GenericKind<'tcx>) { + match generic { + GenericKind::Param(param_ty) => { + param_ty_must_outlive(rcx, origin, region, generic, param_ty); + } + GenericKind::Projection(data) => { + projection_must_outlive(rcx, origin, region, generic, data); + } + } +} - debug!("param_must_outlive(region={:?}, generic={:?})", - region, - generic); +fn param_ty_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region, + generic: GenericKind<'tcx>, + param_ty: ty::ParamTy) { + debug!("param_ty_must_outlive(region={:?}, param_ty={:?})", + region, param_ty); - // To start, collect bounds from user: - let mut param_bounds = rcx.tcx().required_region_bounds(generic.to_ty(rcx.tcx()), - param_env.caller_bounds.clone()); + let verify_bound = param_bound(rcx, param_ty); + rcx.fcx.infcx().verify_generic_bound(origin, generic, region, verify_bound); +} - // In the case of a projection T::Foo, we may be able to extract bounds from the trait def: - match *generic { - GenericKind::Param(..) => { } - GenericKind::Projection(ref projection_ty) => { - param_bounds.push_all( - &projection_bounds(rcx, origin.span(), projection_ty)); +fn projection_must_outlive<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region, + generic: GenericKind<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>) { + debug!("projection_must_outlive(region={:?}, generic={:?})", + region, generic); + + // This is a particularly thorny situation for inference, and for + // now we don't have a complete solution, we just do the best we + // can. The problem is that there are multiple ways for `>::Foo: 'r` to be satisfied: + // + // 1. If `Pi: 'r` forall i, it is satisfied. + // 2. If there is a suitable where-clause, it can be satisfied. + // 3. The trait declaration may declare `'static` bounds on `Foo` as well. + // + // The fact that there are so many options here makes this thorny. + // In the case of parameter relations like `T: 'r`, it's somewhat + // simpler, because checking such a relation does not affect + // inference. This is true because the region bounds we can + // derive for `T` never involve region variables -- they are + // always free regions. The only place a region variable can come + // is on the RHS, and in that case, the smaller the region, the + // better. This means that our current inference, which always + // infers the smallest region it can, can just be used, and we'll + // know what the smallest value for `'r` is when it's done. We can + // then compare that to the regions in the LHS, which are already + // as big as possible, and we're all done. + // + // Projections can in fact be this simple as well. In particular, + // if the parameters `P0..Pn` do not involve any region variables, + // that's the same situation. + // + // Where things get throny is when region variables are involved, + // because in that case relating `Pi: 'r` may influence the + // inference process, since it could cause `'r` to be inferred to + // a larger value. But the problem is that if we add that as a + // constraint into our dataflow graph, we've essentially committed + // to using option 1 (above) to show that `>::Foo: 'r` is satisfied, and it may be that + // Option 1 does not apply, but Option 2 or 3 does. But we can't + // know that now. + // + // For now we choose to accept this. It's a conservative choice, + // so we can move to a more sophisticated inference model later. + // And it's sometimes possible to workaround by introducing + // explicit type parameters or type annotations. But it ain't + // great! + + let declared_bounds = projection_declared_bounds(rcx, origin.span(), projection_ty); + + debug!("projection_must_outlive: declared_bounds={:?}", + declared_bounds); + + // If we know that the projection outlives 'static, then we're done here. + if declared_bounds.contains(&ty::ReStatic) { + return; + } + + // Otherwise, as explained above, if inference variables are + // involved, fallback to rule #1. + if + projection_ty.trait_ref.substs.types.iter().any(|t| t.needs_infer()) || + projection_ty.trait_ref.substs.regions().iter().any(|r| r.needs_infer()) + { + debug!("projection_must_outlive: fallback to rule #1"); + + for &component_ty in &projection_ty.trait_ref.substs.types { + type_must_outlive(rcx, origin.clone(), component_ty, region); } + + for &r in projection_ty.trait_ref.substs.regions() { + rcx.fcx.mk_subr(origin.clone(), region, r); + } + + return; } + // Inform region inference that this generic must be properly + // bounded. + let verify_bound = projection_bound(rcx, origin.span(), declared_bounds, projection_ty); + rcx.fcx.infcx().verify_generic_bound(origin, generic.clone(), region, verify_bound); +} + +fn type_bound<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, span: Span, ty: Ty<'tcx>) -> VerifyBound { + match ty.sty { + ty::TyParam(p) => { + param_bound(rcx, p) + } + ty::TyProjection(data) => { + let declared_bounds = projection_declared_bounds(rcx, span, data); + projection_bound(rcx, span, declared_bounds, data) + } + _ => { + recursive_type_bound(rcx, span, ty) + } + } +} + +fn param_bound<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, param_ty: ty::ParamTy) -> VerifyBound { + let param_env = &rcx.infcx().parameter_environment; + + debug!("param_bound(param_ty={:?})", + param_ty); + + let mut param_bounds = declared_generic_bounds_from_env(rcx, GenericKind::Param(param_ty)); + // Add in the default bound of fn body that applies to all in // scope type parameters: param_bounds.push(param_env.implicit_region_bound); - // Finally, collect regions we scraped from the well-formedness + VerifyBound::AnyRegion(param_bounds) +} + +fn projection_declared_bounds<'a, 'tcx>(rcx: &mut Rcx<'a,'tcx>, + span: Span, + projection_ty: ty::ProjectionTy<'tcx>) + -> Vec +{ + // First assemble bounds from where clauses and traits. + + let mut declared_bounds = + declared_generic_bounds_from_env(rcx, GenericKind::Projection(projection_ty)); + + declared_bounds.push_all( + &declared_projection_bounds_from_trait(rcx, span, projection_ty)); + + declared_bounds +} + +fn projection_bound<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, + span: Span, + declared_bounds: Vec, + projection_ty: ty::ProjectionTy<'tcx>) + -> VerifyBound { + debug!("projection_bound(declared_bounds={:?}, projection_ty={:?})", + declared_bounds, projection_ty); + + // see the extensive comment in projection_must_outlive + + // this routine is not invoked in this case + assert!( + !projection_ty.trait_ref.substs.types.iter().any(|t| t.needs_infer()) && + !projection_ty.trait_ref.substs.regions().iter().any(|r| r.needs_infer())); + + let ty = rcx.tcx().mk_projection(projection_ty.trait_ref, projection_ty.item_name); + let recursive_bound = recursive_type_bound(rcx, span, ty); + + VerifyBound::AnyRegion(declared_bounds).or(recursive_bound) +} + +fn recursive_type_bound<'a, 'tcx>(rcx: &mut Rcx<'a, 'tcx>, + span: Span, + ty: Ty<'tcx>) + -> VerifyBound { + let mut bounds = vec![]; + + for subty in ty.walk_shallow() { + bounds.push(type_bound(rcx, span, subty)); + } + + let mut regions = ty.regions(); + regions.retain(|r| !r.is_bound()); // ignore late-bound regions + bounds.push(VerifyBound::AllRegions(ty.regions())); + + // remove bounds that must hold, since they are not interesting + bounds.retain(|b| !b.must_hold()); + + if bounds.len() == 1 { + bounds.pop().unwrap() + } else { + VerifyBound::AllBounds(bounds) + } +} + +fn declared_generic_bounds_from_env<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, + generic: GenericKind<'tcx>) + -> Vec +{ + let param_env = &rcx.infcx().parameter_environment; + + // To start, collect bounds from user: + let mut param_bounds = rcx.tcx().required_region_bounds(generic.to_ty(rcx.tcx()), + param_env.caller_bounds.clone()); + + // Next, collect regions we scraped from the well-formedness // constraints in the fn signature. To do that, we walk the list // of known relations from the fn ctxt. // @@ -1496,27 +1819,22 @@ fn generic_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>, // The problem is that the type of `x` is `&'a A`. To be // well-formed, then, A must be lower-generic by `'a`, but we // don't know that this holds from first principles. - for &(ref r, ref p) in &rcx.region_bound_pairs { + for &(r, p) in &rcx.region_bound_pairs { debug!("generic={:?} p={:?}", generic, p); if generic == p { - param_bounds.push(*r); + param_bounds.push(r); } } - // Inform region inference that this generic must be properly - // bounded. - rcx.fcx.infcx().verify_generic_bound(origin, - generic.clone(), - region, - param_bounds); + param_bounds } -fn projection_bounds<'a,'tcx>(rcx: &Rcx<'a, 'tcx>, - span: Span, - projection_ty: &ty::ProjectionTy<'tcx>) - -> Vec +fn declared_projection_bounds_from_trait<'a,'tcx>(rcx: &Rcx<'a, 'tcx>, + span: Span, + projection_ty: ty::ProjectionTy<'tcx>) + -> Vec { let fcx = rcx.fcx; let tcx = fcx.tcx(); diff --git a/src/test/compile-fail/associated-types-outlives.rs b/src/test/compile-fail/associated-types-outlives.rs new file mode 100644 index 0000000000000..be52a059808f3 --- /dev/null +++ b/src/test/compile-fail/associated-types-outlives.rs @@ -0,0 +1,36 @@ +// Copyright 2015 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. + +// Regression test for issue #24622. The older associated types code +// was erroneously assuming that all projections outlived the current +// fn body, causing this (invalid) code to be accepted. + +pub trait Foo<'a> { + type Bar; +} + +impl<'a, T> Foo<'a> for T { type Bar = &'a T; } + +fn denormalise<'a, T>(t: &'a T) -> >::Bar { + t +} + +pub fn free_and_use Foo<'a>, + F: for<'a> FnOnce(>::Bar)>(x: T, f: F) { + let y; + 'body: loop { // lifetime annotations added for clarity + 's: loop { y = denormalise(&x); break } + drop(x); //~ ERROR cannot move out of `x` because it is borrowed + return f(y); + } +} + +pub fn main() { +} diff --git a/src/test/compile-fail/regions-assoc-type-outlives-container-hrtb.rs b/src/test/compile-fail/regions-assoc-type-outlives-container-hrtb.rs index ae76b62f06e12..2a15d75b31150 100644 --- a/src/test/compile-fail/regions-assoc-type-outlives-container-hrtb.rs +++ b/src/test/compile-fail/regions-assoc-type-outlives-container-hrtb.rs @@ -56,8 +56,8 @@ pub struct WithHrAssocSub } fn with_assoc_sub<'a,'b>() { - // Same here, because although the where clause is not HR, it - // extends a trait in a HR way. + // The error here is just because `'a:'b` must hold for the type + // below to be well-formed, it is not related to the HR relation. let _: &'a WithHrAssocSub> = loop { }; //~^ ERROR cannot infer diff --git a/src/test/compile-fail/regions-assoc-type-outlives-container.rs b/src/test/compile-fail/regions-assoc-type-outlives-container.rs index 79a90e9ca5a1d..958eb144f56c6 100644 --- a/src/test/compile-fail/regions-assoc-type-outlives-container.rs +++ b/src/test/compile-fail/regions-assoc-type-outlives-container.rs @@ -59,9 +59,8 @@ fn with_assoc1<'a,'b>() where 'b : 'a { } fn without_assoc<'a,'b>() { - // Here there are no associated types and the `'b` appearing in - // `TheType<'b>` is purely covariant, so there is no requirement - // that `'b:'a` holds. + // Here there are no associated types but there is a requirement + // that `'b:'a` holds because the `'b` appears in `TheType<'b>`. let _: &'a WithoutAssoc> = loop { }; //~^ ERROR cannot infer diff --git a/src/test/compile-fail/regions-close-associated-type-into-object.rs b/src/test/compile-fail/regions-close-associated-type-into-object.rs index fdc97ecaf21e2..61897aac18769 100644 --- a/src/test/compile-fail/regions-close-associated-type-into-object.rs +++ b/src/test/compile-fail/regions-close-associated-type-into-object.rs @@ -72,13 +72,12 @@ fn meh1<'a, T: Iter>(v: &'a T) -> Box where T::Item : Clone { // This case is kind of interesting. It's the same as `ok3` but - // without the explicit declaration. In principle, it seems like - // we ought to be able to infer that `T::Item : 'a` because we - // invoked `v.as_self()` which yielded a value of type `&'a - // T::Item`. But we're not that smart at present. + // without the explicit declaration. This is valid because `T: 'a + // => T::Item: 'a`, and the former we can deduce from our argument + // of type `&'a T`. let item = Clone::clone(v.as_item()); - Box::new(item) //~ ERROR associated type `::Item` may not live + Box::new(item) } fn main() {} diff --git a/src/test/compile-fail/regions-close-object-into-object-1.rs b/src/test/compile-fail/regions-close-object-into-object-1.rs index 5472e09ba4be4..5d9818d624b7e 100644 --- a/src/test/compile-fail/regions-close-object-into-object-1.rs +++ b/src/test/compile-fail/regions-close-object-into-object-1.rs @@ -12,7 +12,7 @@ #![allow(warnings)] trait A { } -struct B<'a, T>(&'a (A+'a)); +struct B<'a, T:'a>(&'a (A+'a)); trait X { } diff --git a/src/test/compile-fail/regions-close-object-into-object-2.rs b/src/test/compile-fail/regions-close-object-into-object-2.rs index 1ef000852d561..6cef995665517 100644 --- a/src/test/compile-fail/regions-close-object-into-object-2.rs +++ b/src/test/compile-fail/regions-close-object-into-object-2.rs @@ -11,7 +11,7 @@ #![feature(box_syntax)] trait A { } -struct B<'a, T>(&'a (A+'a)); +struct B<'a, T:'a>(&'a (A+'a)); trait X { } impl<'a, T> X for B<'a, T> {} diff --git a/src/test/compile-fail/regions-close-object-into-object-3.rs b/src/test/compile-fail/regions-close-object-into-object-3.rs index b7dc759b2717f..3004245b15a24 100644 --- a/src/test/compile-fail/regions-close-object-into-object-3.rs +++ b/src/test/compile-fail/regions-close-object-into-object-3.rs @@ -12,7 +12,7 @@ #![allow(warnings)] trait A { } -struct B<'a, T>(&'a (A+'a)); +struct B<'a, T:'a>(&'a (A+'a)); trait X { } impl<'a, T> X for B<'a, T> {} diff --git a/src/test/compile-fail/regions-close-object-into-object-4.rs b/src/test/compile-fail/regions-close-object-into-object-4.rs index 247578d253ea0..bc5b7b7cf7874 100644 --- a/src/test/compile-fail/regions-close-object-into-object-4.rs +++ b/src/test/compile-fail/regions-close-object-into-object-4.rs @@ -11,7 +11,7 @@ #![feature(box_syntax)] trait A { } -struct B<'a, T>(&'a (A+'a)); +struct B<'a, T:'a>(&'a (A+'a)); trait X { } impl<'a, T> X for B<'a, T> {} diff --git a/src/test/compile-fail/regions-close-object-into-object-5.rs b/src/test/compile-fail/regions-close-object-into-object-5.rs index 253132e5f07d0..d89ff48f34f92 100644 --- a/src/test/compile-fail/regions-close-object-into-object-5.rs +++ b/src/test/compile-fail/regions-close-object-into-object-5.rs @@ -16,15 +16,24 @@ trait A fn get(&self) -> T { panic!() } } -struct B<'a, T>(&'a (A+'a)); +struct B<'a, T:'a>(&'a (A+'a)); trait X { fn foo(&self) {} } impl<'a, T> X for B<'a, T> {} fn f<'a, T, U>(v: Box+'static>) -> Box { - box B(&*v) as Box //~ ERROR the parameter type `T` may not live long enough + // oh dear! + box B(&*v) as Box //~^ ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough + //~| ERROR the parameter type `T` may not live long enough } fn main() {} diff --git a/src/test/compile-fail/variance-regions-direct.rs b/src/test/compile-fail/variance-regions-direct.rs index c4fd10830156e..04338a1203c23 100644 --- a/src/test/compile-fail/variance-regions-direct.rs +++ b/src/test/compile-fail/variance-regions-direct.rs @@ -52,7 +52,7 @@ struct Test5<'a, 'b> { //~ ERROR regions=[[I, I];[];[]] // argument list occurs in an invariant context. #[rustc_variance] -struct Test6<'a, 'b> { //~ ERROR regions=[[V, I];[];[]] +struct Test6<'a, 'b:'a> { //~ ERROR regions=[[V, I];[];[]] x: &'a mut extern "Rust" fn(&'b isize), } diff --git a/src/test/run-pass/issue-5708.rs b/src/test/run-pass/issue-5708.rs index dfb560db10067..5471e381a8ded 100644 --- a/src/test/run-pass/issue-5708.rs +++ b/src/test/run-pass/issue-5708.rs @@ -33,7 +33,7 @@ struct Outer<'a> { } impl<'a> Outer<'a> { - fn new(inner: &Inner) -> Outer { + fn new<'b>(inner: &'b Inner) -> Outer<'b> { Outer { inner: inner } @@ -52,7 +52,7 @@ pub trait MyTrait { fn dummy(&self, t: T) -> T { panic!() } } -pub struct MyContainer<'a, T> { +pub struct MyContainer<'a, T:'a> { foos: Vec<&'a (MyTrait+'a)> , } diff --git a/src/test/run-pass/regions-early-bound-trait-param.rs b/src/test/run-pass/regions-early-bound-trait-param.rs index 33889b27a872b..f13bfbb770a84 100644 --- a/src/test/run-pass/regions-early-bound-trait-param.rs +++ b/src/test/run-pass/regions-early-bound-trait-param.rs @@ -42,7 +42,7 @@ fn field_invoke1<'f, 'g>(x: &'g Struct1<'f>) -> (isize,isize) { (l,s) } -struct Struct2<'h, 'i> { +struct Struct2<'h, 'i:'h> { f: &'h (Trait<'i>+'h) }