diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index 99d27c1ba319..61fce699dd55 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -887,9 +887,16 @@ fn parse_existential_bounds_<'a,'tcx, F>(st: &mut PState<'a,'tcx>, } } + let region_bound_will_change = match next(st) { + 'y' => true, + 'n' => false, + c => panic!("parse_ty: expected y/n not '{}'", c) + }; + return ty::ExistentialBounds { region_bound: region_bound, builtin_bounds: builtin_bounds, - projection_bounds: projection_bounds }; + projection_bounds: projection_bounds, + region_bound_will_change: region_bound_will_change }; } fn parse_builtin_bounds(st: &mut PState, mut _conv: F) -> ty::BuiltinBounds where diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index d80316b8f489..441f9f102aea 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -390,6 +390,8 @@ pub fn enc_existential_bounds<'a,'tcx>(w: &mut Encoder, } mywrite!(w, "."); + + mywrite!(w, "{}", if bs.region_bound_will_change {'y'} else {'n'}); } pub fn enc_region_bounds<'a, 'tcx>(w: &mut Encoder, diff --git a/src/librustc/middle/infer/bivariate.rs b/src/librustc/middle/infer/bivariate.rs index d2268894b20a..ad53fb4a8a27 100644 --- a/src/librustc/middle/infer/bivariate.rs +++ b/src/librustc/middle/infer/bivariate.rs @@ -49,6 +49,11 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Bivariate<'a, 'tcx> { fn a_is_expected(&self) -> bool { self.fields.a_is_expected } + fn will_change(&mut self, _: bool, _: bool) -> bool { + // since we are not comparing regions, we don't care + false + } + fn relate_with_variance>(&mut self, variance: ty::Variance, a: &T, diff --git a/src/librustc/middle/infer/combine.rs b/src/librustc/middle/infer/combine.rs index 13b2214d3532..0d081cfeee04 100644 --- a/src/librustc/middle/infer/combine.rs +++ b/src/librustc/middle/infer/combine.rs @@ -56,6 +56,7 @@ pub struct CombineFields<'a, 'tcx: 'a> { pub infcx: &'a InferCtxt<'a, 'tcx>, pub a_is_expected: bool, pub trace: TypeTrace<'tcx>, + pub cause: Option, } pub fn super_combine_tys<'a,'tcx:'a,R>(infcx: &InferCtxt<'a, 'tcx>, diff --git a/src/librustc/middle/infer/equate.rs b/src/librustc/middle/infer/equate.rs index cbbf73d94207..c0dcda1792be 100644 --- a/src/librustc/middle/infer/equate.rs +++ b/src/librustc/middle/infer/equate.rs @@ -34,6 +34,11 @@ impl<'a, 'tcx> TypeRelation<'a,'tcx> for Equate<'a, 'tcx> { fn a_is_expected(&self) -> bool { self.fields.a_is_expected } + fn will_change(&mut self, a: bool, b: bool) -> bool { + // if either side changed from what it was, that could cause equality to fail + a || b + } + fn relate_with_variance>(&mut self, _: ty::Variance, a: &T, diff --git a/src/librustc/middle/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs index 6d5b47d8ed98..8d226739e168 100644 --- a/src/librustc/middle/infer/error_reporting.rs +++ b/src/librustc/middle/infer/error_reporting.rs @@ -593,7 +593,8 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { sub: Region, sup: Region) { match origin { - infer::Subtype(trace) => { + infer::Subtype(trace) | + infer::DefaultExistentialBound(trace) => { let terr = ty::terr_regions_does_not_outlive(sup, sub); self.report_and_explain_type_error(trace, &terr); } @@ -1569,7 +1570,8 @@ impl<'a, 'tcx> ErrorReportingHelpers<'tcx> for InferCtxt<'a, 'tcx> { fn note_region_origin(&self, origin: &SubregionOrigin<'tcx>) { match *origin { - infer::Subtype(ref trace) => { + infer::Subtype(ref trace) | + infer::DefaultExistentialBound(ref trace) => { let desc = match trace.origin { infer::Misc(_) => { "types are compatible" diff --git a/src/librustc/middle/infer/glb.rs b/src/librustc/middle/infer/glb.rs index d6b03266b1fe..adfd1a8a7d79 100644 --- a/src/librustc/middle/infer/glb.rs +++ b/src/librustc/middle/infer/glb.rs @@ -35,6 +35,16 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Glb<'a, 'tcx> { fn a_is_expected(&self) -> bool { self.fields.a_is_expected } + fn will_change(&mut self, a: bool, b: bool) -> bool { + // Hmm, so the result of GLB will still be a LB if one or both + // sides change to 'static, but it may no longer be the GLB. + // I'm going to go with `a || b` here to be conservative, + // since the result of this operation may be affected, though + // I think it would mostly be more accepting than before (since the result + // would be a bigger region). + a || b + } + fn relate_with_variance>(&mut self, variance: ty::Variance, a: &T, diff --git a/src/librustc/middle/infer/lub.rs b/src/librustc/middle/infer/lub.rs index 9d993ead5ca2..f10d4adc8e5f 100644 --- a/src/librustc/middle/infer/lub.rs +++ b/src/librustc/middle/infer/lub.rs @@ -35,6 +35,11 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Lub<'a, 'tcx> { fn a_is_expected(&self) -> bool { self.fields.a_is_expected } + fn will_change(&mut self, a: bool, b: bool) -> bool { + // result will be 'static if a || b + a || b + } + fn relate_with_variance>(&mut self, variance: ty::Variance, a: &T, diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs index 7df37bdae07d..34726436ff75 100644 --- a/src/librustc/middle/infer/mod.rs +++ b/src/librustc/middle/infer/mod.rs @@ -194,6 +194,9 @@ pub enum SubregionOrigin<'tcx> { // Arose from a subtyping relation Subtype(TypeTrace<'tcx>), + // Arose from a subtyping relation + DefaultExistentialBound(TypeTrace<'tcx>), + // Stack-allocated closures cannot outlive innermost loop // or function so as to ensure we only require finite stack InfStackClosure(Span), @@ -658,7 +661,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { -> CombineFields<'a, 'tcx> { CombineFields {infcx: self, a_is_expected: a_is_expected, - trace: trace} + trace: trace, + cause: None} } // public so that it can be used from the rustc_driver unit tests @@ -1464,6 +1468,7 @@ impl<'tcx> SubregionOrigin<'tcx> { pub fn span(&self) -> Span { match *self { Subtype(ref a) => a.span(), + DefaultExistentialBound(ref a) => a.span(), InfStackClosure(a) => a, InvokeClosure(a) => a, DerefPointer(a) => a, diff --git a/src/librustc/middle/infer/sub.rs b/src/librustc/middle/infer/sub.rs index 2b7f9a74b8bd..7c40e96a2f7a 100644 --- a/src/librustc/middle/infer/sub.rs +++ b/src/librustc/middle/infer/sub.rs @@ -10,16 +10,17 @@ use super::combine::{self, CombineFields}; use super::higher_ranked::HigherRankedRelations; -use super::Subtype; +use super::SubregionOrigin; use super::type_variable::{SubtypeOf, SupertypeOf}; use middle::ty::{self, Ty}; use middle::ty::TyVar; -use middle::ty_relate::{Relate, RelateResult, TypeRelation}; +use middle::ty_relate::{Cause, Relate, RelateResult, TypeRelation}; +use std::mem; /// "Greatest lower bound" (common subtype) pub struct Sub<'a, 'tcx: 'a> { - fields: CombineFields<'a, 'tcx> + fields: CombineFields<'a, 'tcx>, } impl<'a, 'tcx> Sub<'a, 'tcx> { @@ -33,6 +34,25 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Sub<'a, 'tcx> { fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.fields.infcx.tcx } fn a_is_expected(&self) -> bool { self.fields.a_is_expected } + fn with_cause(&mut self, cause: Cause, f: F) -> R + where F: FnOnce(&mut Self) -> R + { + debug!("sub with_cause={:?}", cause); + let old_cause = mem::replace(&mut self.fields.cause, Some(cause)); + let r = f(self); + debug!("sub old_cause={:?}", old_cause); + self.fields.cause = old_cause; + r + } + + fn will_change(&mut self, a: bool, b: bool) -> bool { + // if we have (Foo+'a) <: (Foo+'b), this requires that 'a:'b. + // So if 'a becomes 'static, no additional errors can occur. + // OTOH, if 'a stays the same, but 'b becomes 'static, we + // could have a problem. + !a && b + } + fn relate_with_variance>(&mut self, variance: ty::Variance, a: &T, @@ -84,11 +104,14 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Sub<'a, 'tcx> { } fn regions(&mut self, a: ty::Region, b: ty::Region) -> RelateResult<'tcx, ty::Region> { - debug!("{}.regions({:?}, {:?})", - self.tag(), - a, - b); - let origin = Subtype(self.fields.trace.clone()); + debug!("{}.regions({:?}, {:?}) self.cause={:?}", + self.tag(), a, b, self.fields.cause); + let origin = match self.fields.cause { + Some(Cause::ExistentialRegionBound(true)) => + SubregionOrigin::DefaultExistentialBound(self.fields.trace.clone()), + _ => + SubregionOrigin::Subtype(self.fields.trace.clone()), + }; self.fields.infcx.region_vars.make_subregion(origin, a, b); Ok(a) } diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 3bc4fd0c0a14..abc300869adc 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -2445,6 +2445,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { region_bound: data_b.bounds.region_bound, builtin_bounds: data_b.bounds.builtin_bounds, projection_bounds: data_a.bounds.projection_bounds.clone(), + region_bound_will_change: data_b.bounds.region_bound_will_change, }; let new_trait = tcx.mk_trait(data_a.principal.clone(), bounds); diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index d5372d77005d..f8b00ba153fc 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -2055,6 +2055,11 @@ pub struct ExistentialBounds<'tcx> { pub region_bound: ty::Region, pub builtin_bounds: BuiltinBounds, pub projection_bounds: Vec>, + + // If true, this TyTrait used a "default bound" in the surface + // syntax. This makes no difference to the type system but is + // handy for error reporting. + pub region_bound_will_change: bool, } #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index 6bae1b68ed42..5e88a0aefd3a 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -728,6 +728,7 @@ pub fn super_fold_existential_bounds<'tcx, T: TypeFolder<'tcx>>( region_bound: bounds.region_bound.fold_with(this), builtin_bounds: bounds.builtin_bounds, projection_bounds: bounds.projection_bounds.fold_with(this), + region_bound_will_change: bounds.region_bound_will_change, } } diff --git a/src/librustc/middle/ty_match.rs b/src/librustc/middle/ty_match.rs index 135118820a77..5776235780a3 100644 --- a/src/librustc/middle/ty_match.rs +++ b/src/librustc/middle/ty_match.rs @@ -42,6 +42,11 @@ impl<'a, 'tcx> TypeRelation<'a, 'tcx> for Match<'a, 'tcx> { fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.tcx } fn a_is_expected(&self) -> bool { true } // irrelevant + fn will_change(&mut self, _: bool, _: bool) -> bool { + // we're ignoring regions in this code + false + } + fn relate_with_variance>(&mut self, _: ty::Variance, a: &T, diff --git a/src/librustc/middle/ty_relate/mod.rs b/src/librustc/middle/ty_relate/mod.rs index 4e88e23377d0..b8b2469b2066 100644 --- a/src/librustc/middle/ty_relate/mod.rs +++ b/src/librustc/middle/ty_relate/mod.rs @@ -22,6 +22,11 @@ use syntax::ast; pub type RelateResult<'tcx, T> = Result>; +#[derive(Clone, Debug)] +pub enum Cause { + ExistentialRegionBound(bool), // if true, this is a default, else explicit +} + pub trait TypeRelation<'a,'tcx> : Sized { fn tcx(&self) -> &'a ty::ctxt<'tcx>; @@ -32,6 +37,19 @@ pub trait TypeRelation<'a,'tcx> : Sized { /// relation. Just affects error messages. fn a_is_expected(&self) -> bool; + fn with_cause(&mut self, _cause: Cause, f: F) -> R + where F: FnOnce(&mut Self) -> R + { + f(self) + } + + /// Hack for deciding whether the lifetime bound defaults change + /// will be a breaking change or not. The bools indicate whether + /// `a`/`b` have a default that will change to `'static`; the + /// result is true if this will potentially affect the affect of + /// relating `a` and `b`. + fn will_change(&mut self, a: bool, b: bool) -> bool; + /// Generic relation routine suitable for most anything. fn relate>(&mut self, a: &T, b: &T) -> RelateResult<'tcx, T> { Relate::relate(self, a, b) @@ -366,14 +384,21 @@ impl<'a,'tcx:'a> Relate<'a,'tcx> for ty::ExistentialBounds<'tcx> { -> RelateResult<'tcx, ty::ExistentialBounds<'tcx>> where R: TypeRelation<'a,'tcx> { - let r = try!(relation.relate_with_variance(ty::Contravariant, - &a.region_bound, - &b.region_bound)); + let will_change = relation.will_change(a.region_bound_will_change, + b.region_bound_will_change); + + let r = + try!(relation.with_cause( + Cause::ExistentialRegionBound(will_change), + |relation| relation.relate_with_variance(ty::Contravariant, + &a.region_bound, + &b.region_bound))); let nb = try!(relation.relate(&a.builtin_bounds, &b.builtin_bounds)); let pb = try!(relation.relate(&a.projection_bounds, &b.projection_bounds)); Ok(ty::ExistentialBounds { region_bound: r, builtin_bounds: nb, - projection_bounds: pb }) + projection_bounds: pb, + region_bound_will_change: will_change }) } } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 91b547e96795..c4fe16805fa5 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -299,6 +299,10 @@ impl<'tcx> fmt::Display for ty::TraitTy<'tcx> { } } + if bounds.region_bound_will_change && tcx.sess.verbose() { + components.push(format!("WILL-CHANGE")); + } + Ok(()) } } diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 96564277cdc1..ed2d303b5e1b 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -82,6 +82,16 @@ pub fn erase_regions<'tcx,T>(cx: &ty::ctxt<'tcx>, value: &T) -> T return t_norm; } + fn fold_existential_bounds(&mut self, s: &ty::ExistentialBounds<'tcx>) + -> ty::ExistentialBounds<'tcx> { + let mut s = ty_fold::super_fold_existential_bounds(self, s); + + // this annoying flag messes up trans normalization + s.region_bound_will_change = false; + + s + } + fn fold_binder(&mut self, t: &ty::Binder) -> ty::Binder where T : TypeFoldable<'tcx> { diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 00b7f4206140..7f29af0d84b2 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -1549,7 +1549,7 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>, let rscope1 = &ObjectLifetimeDefaultRscope::new( rscope, - Some(ty::ObjectLifetimeDefault::Specific(r))); + ty::ObjectLifetimeDefault::Specific(r)); let t = ast_ty_to_ty(this, rscope1, &*mt.ty); tcx.mk_ref(tcx.mk_region(r), ty::mt {ty: t, mutbl: mt.mutbl}) } @@ -2016,12 +2016,30 @@ pub fn conv_existential_bounds_from_partitioned_bounds<'tcx>( "only the builtin traits can be used as closure or object bounds"); } - let region_bound = compute_object_lifetime_bound(this, - rscope, - span, - ®ion_bounds, - principal_trait_ref, - builtin_bounds); + let region_bound = + compute_object_lifetime_bound(this, + span, + ®ion_bounds, + principal_trait_ref, + builtin_bounds); + + let (region_bound, will_change) = match region_bound { + Some(r) => (r, false), + None => { + match rscope.object_lifetime_default(span) { + Some(r) => (r, rscope.object_lifetime_default_will_change_in_1_3()), + None => { + span_err!(this.tcx().sess, span, E0228, + "the lifetime bound for this object type cannot be deduced \ + from context; please supply an explicit bound"); + (ty::ReStatic, false) + } + } + } + }; + + debug!("region_bound: {:?} will_change: {:?}", + region_bound, will_change); ty::sort_bounds_list(&mut projection_bounds); @@ -2029,6 +2047,7 @@ pub fn conv_existential_bounds_from_partitioned_bounds<'tcx>( region_bound: region_bound, builtin_bounds: builtin_bounds, projection_bounds: projection_bounds, + region_bound_will_change: will_change, } } @@ -2038,12 +2057,11 @@ pub fn conv_existential_bounds_from_partitioned_bounds<'tcx>( /// region bounds. It may be that we can derive no bound at all, in which case we return `None`. fn compute_object_lifetime_bound<'tcx>( this: &AstConv<'tcx>, - rscope: &RegionScope, span: Span, explicit_region_bounds: &[&ast::Lifetime], principal_trait_ref: ty::PolyTraitRef<'tcx>, builtin_bounds: ty::BuiltinBounds) - -> ty::Region + -> Option // if None, use the default { let tcx = this.tcx(); @@ -2061,11 +2079,11 @@ fn compute_object_lifetime_bound<'tcx>( if !explicit_region_bounds.is_empty() { // Explicitly specified region bound. Use that. let r = explicit_region_bounds[0]; - return ast_region_to_region(tcx, r); + return Some(ast_region_to_region(tcx, r)); } if let Err(ErrorReported) = this.ensure_super_predicates(span,principal_trait_ref.def_id()) { - return ty::ReStatic; + return Some(ty::ReStatic); } // No explicit region bound specified. Therefore, examine trait @@ -2074,23 +2092,15 @@ fn compute_object_lifetime_bound<'tcx>( object_region_bounds(tcx, &principal_trait_ref, builtin_bounds); // If there are no derived region bounds, then report back that we - // can find no region bound. + // can find no region bound. The caller will use the default. if derived_region_bounds.is_empty() { - match rscope.object_lifetime_default(span) { - Some(r) => { return r; } - None => { - span_err!(this.tcx().sess, span, E0228, - "the lifetime bound for this object type cannot be deduced \ - from context; please supply an explicit bound"); - return ty::ReStatic; - } - } + return None; } // If any of the derived region bounds are 'static, that is always // the best choice. if derived_region_bounds.iter().any(|r| ty::ReStatic == *r) { - return ty::ReStatic; + return Some(ty::ReStatic); } // Determine whether there is exactly one unique region in the set @@ -2101,7 +2111,7 @@ fn compute_object_lifetime_bound<'tcx>( span_err!(tcx.sess, span, E0227, "ambiguous lifetime bound, explicit lifetime bound required"); } - return r; + return Some(r); } pub struct PartitionedBounds<'a> { diff --git a/src/librustc_typeck/rscope.rs b/src/librustc_typeck/rscope.rs index 89f118a71797..d2c982b3099e 100644 --- a/src/librustc_typeck/rscope.rs +++ b/src/librustc_typeck/rscope.rs @@ -52,6 +52,19 @@ pub trait RegionScope { /// computing `object_lifetime_default` (in particular, in legacy /// modes, it may not be relevant). fn base_object_lifetime_default(&self, span: Span) -> ty::Region; + + /// Used to issue warnings in Rust 1.2, not needed after that. + /// True if the result of `object_lifetime_default` will change in 1.3. + fn object_lifetime_default_will_change_in_1_3(&self) -> bool { + false + } + + /// Used to issue warnings in Rust 1.2, not needed after that. + /// True if the result of `base_object_lifetime_default` differs + /// from the result of `object_lifetime_default`. + fn base_object_lifetime_default_differs(&self) -> bool { + false + } } // A scope in which all regions must be explicitly named. This is used @@ -219,6 +232,30 @@ impl<'r> RegionScope for ObjectLifetimeDefaultRscope<'r> { assert!(false, "this code should not execute until Rust 1.3"); self.base_scope.base_object_lifetime_default(span) } + + fn object_lifetime_default_will_change_in_1_3(&self) -> bool { + debug!("object_lifetime_default_will_change_in_1_3: {:?}", self.default); + + match self.default { + ty::ObjectLifetimeDefault::Ambiguous | + ty::ObjectLifetimeDefault::Specific(_) => + false, + + ty::ObjectLifetimeDefault::BaseDefault => + self.base_scope.base_object_lifetime_default_differs() + } + } + + fn base_object_lifetime_default_differs(&self) -> bool { + debug!("base_object_lifetime_default_differs: {:?}", self.default); + + match self.default { + ty::ObjectLifetimeDefault::Ambiguous | + ty::ObjectLifetimeDefault::Specific(_) => + true, + + ty::ObjectLifetimeDefault::BaseDefault => + self.base_scope.base_object_lifetime_default_differs(), } }