diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 04725916e7a0f..38df0284371d0 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1160,6 +1160,42 @@ static mut FOO: Option> = None; // error: mutable statics are not allowed to have destructors static mut BAR: Option> = None; ``` +"##, + +E0398: r##" +In Rust 1.3, the default object lifetime bounds are expected to +change, as described in RFC #1156 [1]. You are getting a warning +because the compiler thinks it is possible that this change will cause +a compilation error in your code. It is possible, though unlikely, +that this is a false alarm. + +The heart of the change is that where `&'a Box` used to +default to `&'a Box`, it now defaults to `&'a +Box` (here, `SomeTrait` is the name of some trait +type). Note that the only types which are affected are references to +boxes, like `&Box` or `&[Box]`. More common +types like `&SomeTrait` or `Box` are unaffected. + +To silence this warning, edit your code to use an explicit bound. +Most of the time, this means that you will want to change the +signature of a function that you are calling. For example, if +the error is reported on a call like `foo(x)`, and `foo` is +defined as follows: + +``` +fn foo(arg: &Box) { ... } +``` + +you might change it to: + +``` +fn foo<'a>(arg: &Box) { ... } +``` + +This explicitly states that you expect the trait object `SomeTrait` to +contain references (with a maximum lifetime of `'a`). + +[1]: https://github.com/rust-lang/rfcs/pull/1156 "## } diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index a5b9e40593a83..61fce699dd555 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -843,15 +843,15 @@ fn parse_type_param_def_<'a, 'tcx, F>(st: &mut PState<'a, 'tcx>, conv: &mut F) fn parse_object_lifetime_default<'a,'tcx, F>(st: &mut PState<'a,'tcx>, conv: &mut F) - -> Option + -> ty::ObjectLifetimeDefault where F: FnMut(DefIdSource, ast::DefId) -> ast::DefId, { match next(st) { - 'n' => None, - 'a' => Some(ty::ObjectLifetimeDefault::Ambiguous), + 'a' => ty::ObjectLifetimeDefault::Ambiguous, + 'b' => ty::ObjectLifetimeDefault::BaseDefault, 's' => { let region = parse_region_(st, conv); - Some(ty::ObjectLifetimeDefault::Specific(region)) + ty::ObjectLifetimeDefault::Specific(region) } _ => panic!("parse_object_lifetime_default: bad input") } @@ -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 c078b62dd2d8a..441f9f102aea9 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, @@ -414,12 +416,12 @@ pub fn enc_type_param_def<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, fn enc_object_lifetime_default<'a, 'tcx>(w: &mut Encoder, cx: &ctxt<'a, 'tcx>, - default: Option) + default: ty::ObjectLifetimeDefault) { match default { - None => mywrite!(w, "n"), - Some(ty::ObjectLifetimeDefault::Ambiguous) => mywrite!(w, "a"), - Some(ty::ObjectLifetimeDefault::Specific(r)) => { + ty::ObjectLifetimeDefault::Ambiguous => mywrite!(w, "a"), + ty::ObjectLifetimeDefault::BaseDefault => mywrite!(w, "b"), + ty::ObjectLifetimeDefault::Specific(r) => { mywrite!(w, "s"); enc_region(w, cx, r); } diff --git a/src/librustc/middle/infer/bivariate.rs b/src/librustc/middle/infer/bivariate.rs index d2268894b20a1..ad53fb4a8a270 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 13b2214d35328..0d081cfeee041 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 cbbf73d942073..c0dcda1792be5 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 6d5b47d8ed980..8d226739e1688 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 d6b03266b1fe7..adfd1a8a7d794 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 9d993ead5ca20..f10d4adc8e5f2 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 7df37bdae07db..34726436ff755 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/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs index fac7b53ca76aa..891a39e723a62 100644 --- a/src/librustc/middle/infer/region_inference/mod.rs +++ b/src/librustc/middle/infer/region_inference/mod.rs @@ -1358,9 +1358,56 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { } } + // Check for future hostile edges tied to a bad default + self.report_future_hostility(&graph); + (0..self.num_vars() as usize).map(|idx| var_data[idx].value).collect() } + fn report_future_hostility(&self, graph: &RegionGraph) { + let constraints = self.constraints.borrow(); + for edge in graph.all_edges() { + match constraints[&edge.data] { + SubregionOrigin::DefaultExistentialBound(_) => { + // this will become 'static in the future + } + _ => { continue; } + } + + // this constraint will become a 'static constraint in the + // future, so walk outward and see if we have any hard + // bounds that could not be inferred to 'static + for nid in graph.depth_traverse(edge.target()) { + for (_, succ) in graph.outgoing_edges(nid) { + match succ.data { + ConstrainVarSubReg(_, r) => { + match r { + ty::ReStatic | ty::ReInfer(_) => { + /* OK */ + } + ty::ReFree(_) | ty::ReScope(_) | ty::ReEmpty => { + span_warn!( + self.tcx.sess, + constraints[&edge.data].span(), + E0398, + "this code may fail to compile in Rust 1.3 due to \ + the proposed change in object lifetime bound defaults"); + return; // only issue the warning once per fn + } + ty::ReEarlyBound(..) | ty::ReLateBound(..) => { + self.tcx.sess.span_bug( + constraints[&succ.data].span(), + "relation to bound region"); + } + } + } + _ => { } + } + } + } + } + } + fn construct_graph(&self) -> RegionGraph { let num_vars = self.num_vars(); diff --git a/src/librustc/middle/infer/sub.rs b/src/librustc/middle/infer/sub.rs index 2b7f9a74b8bd5..7c40e96a2f7a5 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 3bc4fd0c0a14e..abc300869adca 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 91a038813b868..544e594dd21b0 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)] @@ -2245,6 +2250,9 @@ pub enum ObjectLifetimeDefault { /// `T:'a` constraints are found. Ambiguous, + /// Use the base default, typically 'static, but in a fn body it is a fresh variable + BaseDefault, + /// Use the given region as the default. Specific(Region), } @@ -2256,7 +2264,7 @@ pub struct TypeParameterDef<'tcx> { pub space: subst::ParamSpace, pub index: u32, pub default: Option>, - pub object_lifetime_default: Option, + pub object_lifetime_default: ObjectLifetimeDefault, } #[derive(RustcEncodable, RustcDecodable, Clone, Debug)] @@ -7328,6 +7336,7 @@ impl<'tcx> fmt::Debug for ObjectLifetimeDefault { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { ObjectLifetimeDefault::Ambiguous => write!(f, "Ambiguous"), + ObjectLifetimeDefault::BaseDefault => write!(f, "BaseDefault"), ObjectLifetimeDefault::Specific(ref r) => write!(f, "{:?}", r), } } diff --git a/src/librustc/middle/ty_fold.rs b/src/librustc/middle/ty_fold.rs index 284d26b3cd6d7..5e88a0aefd3ab 100644 --- a/src/librustc/middle/ty_fold.rs +++ b/src/librustc/middle/ty_fold.rs @@ -369,6 +369,9 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ObjectLifetimeDefault { ty::ObjectLifetimeDefault::Ambiguous => ty::ObjectLifetimeDefault::Ambiguous, + ty::ObjectLifetimeDefault::BaseDefault => + ty::ObjectLifetimeDefault::BaseDefault, + ty::ObjectLifetimeDefault::Specific(r) => ty::ObjectLifetimeDefault::Specific(r.fold_with(folder)), } @@ -725,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 135118820a771..5776235780a3a 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 4e88e23377d0c..b8b2469b20667 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 91b547e967952..44bea1cb621ef 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -290,13 +290,18 @@ impl<'tcx> fmt::Display for ty::TraitTy<'tcx> { try!(write!(f, " + {:?}", bound)); } - // Region, if not obviously implied by builtin bounds. - if bounds.region_bound != ty::ReStatic { - // Region bound is implied by builtin bounds: - let bound = bounds.region_bound.to_string(); - if !bound.is_empty() { - try!(write!(f, " + {}", bound)); - } + // FIXME: It'd be nice to compute from context when this bound + // is implied, but that's non-trivial -- we'd perhaps have to + // use thread-local data of some kind? There are also + // advantages to just showing the region, since it makes + // people aware that it's there. + let bound = bounds.region_bound.to_string(); + if !bound.is_empty() { + try!(write!(f, " + {}", bound)); + } + + if bounds.region_bound_will_change && verbose() { + try!(write!(f, " [WILL-CHANGE]")); } Ok(()) diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 96564277cdc1d..ed2d303b5e1b6 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 00b7f42061405..7f29af0d84b2d 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/check/mod.rs b/src/librustc_typeck/check/mod.rs index c6975982c8747..05166fa6134cf 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1779,6 +1779,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { impl<'a, 'tcx> RegionScope for FnCtxt<'a, 'tcx> { fn object_lifetime_default(&self, span: Span) -> Option { + Some(self.base_object_lifetime_default(span)) + } + + fn base_object_lifetime_default(&self, span: Span) -> ty::Region { // RFC #599 specifies that object lifetime defaults take // precedence over other defaults. But within a fn body we // don't have a *default* region, rather we use inference to @@ -1786,7 +1790,7 @@ impl<'a, 'tcx> RegionScope for FnCtxt<'a, 'tcx> { // (and anyway, within a fn body the right region may not even // be something the user can write explicitly, since it might // be some expression). - Some(self.infcx().next_region_var(infer::MiscVariable(span))) + self.infcx().next_region_var(infer::MiscVariable(span)) } fn anon_regions(&self, span: Span, count: usize) diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 5161c84e4b1a6..e170808ad07d6 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1532,8 +1532,7 @@ fn convert_typed_item<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let object_lifetime_default_reprs: String = scheme.generics.types.iter() .map(|t| match t.object_lifetime_default { - Some(ty::ObjectLifetimeDefault::Specific(r)) => - r.to_string(), + ty::ObjectLifetimeDefault::Specific(r) => r.to_string(), d => format!("{:?}", d), }) .collect::>() @@ -1637,7 +1636,7 @@ fn ty_generics_for_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, name: special_idents::type_self.name, def_id: local_def(param_id), default: None, - object_lifetime_default: None, + object_lifetime_default: ty::ObjectLifetimeDefault::BaseDefault, }; ccx.tcx.ty_param_defs.borrow_mut().insert(param_id, def.clone()); @@ -1928,7 +1927,7 @@ fn compute_object_lifetime_default<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, param_id: ast::NodeId, param_bounds: &[ast::TyParamBound], where_clause: &ast::WhereClause) - -> Option + -> ty::ObjectLifetimeDefault { let inline_bounds = from_bounds(ccx, param_bounds); let where_bounds = from_predicates(ccx, param_id, &where_clause.predicates); @@ -1936,11 +1935,12 @@ fn compute_object_lifetime_default<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, .chain(where_bounds) .collect(); return if all_bounds.len() > 1 { - Some(ty::ObjectLifetimeDefault::Ambiguous) + ty::ObjectLifetimeDefault::Ambiguous + } else if all_bounds.len() == 0 { + ty::ObjectLifetimeDefault::BaseDefault } else { - all_bounds.into_iter() - .next() - .map(ty::ObjectLifetimeDefault::Specific) + ty::ObjectLifetimeDefault::Specific( + all_bounds.into_iter().next().unwrap()) }; fn from_bounds<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, diff --git a/src/librustc_typeck/rscope.rs b/src/librustc_typeck/rscope.rs index b416026ba0134..d2c982b3099e7 100644 --- a/src/librustc_typeck/rscope.rs +++ b/src/librustc_typeck/rscope.rs @@ -45,6 +45,26 @@ pub trait RegionScope { /// be derived from the object traits, what should we use? If /// `None` is returned, an explicit annotation is required. fn object_lifetime_default(&self, span: Span) -> Option; + + /// The "base" default is the initial default for a scope. This is + /// 'static except for in fn bodies, where it is a fresh inference + /// variable. You shouldn't call this except for as part of + /// 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 @@ -53,16 +73,20 @@ pub trait RegionScope { pub struct ExplicitRscope; impl RegionScope for ExplicitRscope { - fn object_lifetime_default(&self, _span: Span) -> Option { - Some(ty::ReStatic) - } - fn anon_regions(&self, _span: Span, _count: usize) -> Result, Option>> { Err(None) } + + fn object_lifetime_default(&self, span: Span) -> Option { + Some(self.base_object_lifetime_default(span)) + } + + fn base_object_lifetime_default(&self, _span: Span) -> ty::Region { + ty::ReStatic + } } // Same as `ExplicitRscope`, but provides some extra information for diagnostics @@ -75,10 +99,6 @@ impl UnelidableRscope { } impl RegionScope for UnelidableRscope { - fn object_lifetime_default(&self, _span: Span) -> Option { - Some(ty::ReStatic) - } - fn anon_regions(&self, _span: Span, _count: usize) @@ -86,6 +106,14 @@ impl RegionScope for UnelidableRscope { let UnelidableRscope(ref v) = *self; Err(v.clone()) } + + fn object_lifetime_default(&self, span: Span) -> Option { + Some(self.base_object_lifetime_default(span)) + } + + fn base_object_lifetime_default(&self, _span: Span) -> ty::Region { + ty::ReStatic + } } // A scope in which omitted anonymous region defaults to @@ -103,11 +131,15 @@ impl ElidableRscope { } impl RegionScope for ElidableRscope { - fn object_lifetime_default(&self, _span: Span) -> Option { + fn object_lifetime_default(&self, span: Span) -> Option { // Per RFC #599, object-lifetimes default to 'static unless // overridden by context, and this takes precedence over // lifetime elision. - Some(ty::ReStatic) + Some(self.base_object_lifetime_default(span)) + } + + fn base_object_lifetime_default(&self, _span: Span) -> ty::Region { + ty::ReStatic } fn anon_regions(&self, @@ -140,11 +172,15 @@ impl BindingRscope { } impl RegionScope for BindingRscope { - fn object_lifetime_default(&self, _span: Span) -> Option { + fn object_lifetime_default(&self, span: Span) -> Option { // Per RFC #599, object-lifetimes default to 'static unless // overridden by context, and this takes precedence over the - // binding defaults. - Some(ty::ReStatic) + // binding defaults in a fn signature. + Some(self.base_object_lifetime_default(span)) + } + + fn base_object_lifetime_default(&self, _span: Span) -> ty::Region { + ty::ReStatic } fn anon_regions(&self, @@ -159,12 +195,12 @@ impl RegionScope for BindingRscope { /// A scope which overrides the default object lifetime but has no other effect. pub struct ObjectLifetimeDefaultRscope<'r> { base_scope: &'r (RegionScope+'r), - default: Option, + default: ty::ObjectLifetimeDefault, } impl<'r> ObjectLifetimeDefaultRscope<'r> { pub fn new(base_scope: &'r (RegionScope+'r), - default: Option) + default: ty::ObjectLifetimeDefault) -> ObjectLifetimeDefaultRscope<'r> { ObjectLifetimeDefaultRscope { @@ -177,9 +213,49 @@ impl<'r> ObjectLifetimeDefaultRscope<'r> { impl<'r> RegionScope for ObjectLifetimeDefaultRscope<'r> { fn object_lifetime_default(&self, span: Span) -> Option { match self.default { - None => self.base_scope.object_lifetime_default(span), - Some(ty::ObjectLifetimeDefault::Ambiguous) => None, - Some(ty::ObjectLifetimeDefault::Specific(r)) => Some(r), + ty::ObjectLifetimeDefault::Ambiguous => + None, + + ty::ObjectLifetimeDefault::BaseDefault => + if false { // this will become the behavior in Rust 1.3 + Some(self.base_object_lifetime_default(span)) + } else { + self.base_scope.object_lifetime_default(span) + }, + + ty::ObjectLifetimeDefault::Specific(r) => + Some(r), + } + } + + fn base_object_lifetime_default(&self, span: Span) -> ty::Region { + 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(), } } @@ -210,6 +286,10 @@ impl<'r> RegionScope for ShiftedRscope<'r> { .map(|r| ty_fold::shift_region(r, 1)) } + fn base_object_lifetime_default(&self, span: Span) -> ty::Region { + ty_fold::shift_region(self.base_scope.base_object_lifetime_default(span), 1) + } + fn anon_regions(&self, span: Span, count: usize) diff --git a/src/test/auxiliary/lifetime_bound_will_change_warning_lib.rs b/src/test/auxiliary/lifetime_bound_will_change_warning_lib.rs new file mode 100644 index 0000000000000..95f8b39c48745 --- /dev/null +++ b/src/test/auxiliary/lifetime_bound_will_change_warning_lib.rs @@ -0,0 +1,21 @@ +// 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. + +#![crate_type = "rlib"] + +// Helper for testing that we get suitable warnings when lifetime +// bound change will cause breakage. + +pub fn just_ref(x: &Fn()) { +} + +pub fn ref_obj(x: &Box) { + // this will change to &Box... +} diff --git a/src/test/compile-fail/issue-24446.rs b/src/test/compile-fail/issue-24446.rs index 7ac73a931f113..1fca8eb6ac4f3 100644 --- a/src/test/compile-fail/issue-24446.rs +++ b/src/test/compile-fail/issue-24446.rs @@ -11,7 +11,7 @@ fn main() { static foo: Fn() -> u32 = || -> u32 { //~^ ERROR: mismatched types: - //~| expected `core::ops::Fn() -> u32`, + //~| expected `core::ops::Fn() -> u32 + 'static`, //~| found closure //~| (expected trait core::ops::Fn, //~| found closure) diff --git a/src/test/compile-fail/lifetime-bound-will-change-warning.rs b/src/test/compile-fail/lifetime-bound-will-change-warning.rs new file mode 100644 index 0000000000000..4b575703e7c75 --- /dev/null +++ b/src/test/compile-fail/lifetime-bound-will-change-warning.rs @@ -0,0 +1,64 @@ +// 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. + +// aux-build:lifetime_bound_will_change_warning_lib.rs + +// Test that we get suitable warnings when lifetime bound change will +// cause breakage. + +#![allow(dead_code)] +#![allow(unused_variables)] +#![feature(rustc_attrs)] + +extern crate lifetime_bound_will_change_warning_lib as lib; + +fn just_ref(x: &Fn()) { +} + +fn ref_obj(x: &Box) { + // this will change to &Box... + + // Note: no warning is issued here, because the type of `x` will change to 'static + if false { ref_obj(x); } +} + +fn test1<'a>(x: &'a Box) { + // just_ref will stay the same. + just_ref(&**x) +} + +fn test1cc<'a>(x: &'a Box) { + // same as test1, but cross-crate + lib::just_ref(&**x) +} + +fn test2<'a>(x: &'a Box) { + // but ref_obj will not, so warn. + ref_obj(x) //~ WARNING this code may fail to compile in Rust 1.3 +} + +fn test2cc<'a>(x: &'a Box) { + // same as test2, but cross crate + lib::ref_obj(x) //~ WARNING this code may fail to compile in Rust 1.3 +} + +fn test3<'a>(x: &'a Box) { + // here, we have a 'static bound, so even when ref_obj changes, no error results + ref_obj(x) +} + +fn test3cc<'a>(x: &'a Box) { + // same as test3, but cross crate + lib::ref_obj(x) +} + +#[rustc_error] +fn main() { //~ ERROR compilation successful +} diff --git a/src/test/compile-fail/object-lifetime-default-mybox.rs b/src/test/compile-fail/object-lifetime-default-mybox.rs index 23ddea4499a72..b5c4e0c767b00 100644 --- a/src/test/compile-fail/object-lifetime-default-mybox.rs +++ b/src/test/compile-fail/object-lifetime-default-mybox.rs @@ -39,5 +39,9 @@ fn load1<'a,'b>(a: &'a MyBox, //~| ERROR mismatched types } +fn load2<'a>(ss: &MyBox) -> MyBox { + load0(ss) //~ WARNING E0398 +} + fn main() { } diff --git a/src/test/compile-fail/object-lifetime-default.rs b/src/test/compile-fail/object-lifetime-default.rs index b71eadd6d08af..104e10f3207db 100644 --- a/src/test/compile-fail/object-lifetime-default.rs +++ b/src/test/compile-fail/object-lifetime-default.rs @@ -11,10 +11,10 @@ #![feature(rustc_attrs)] #[rustc_object_lifetime_default] -struct A(T); //~ ERROR None +struct A(T); //~ ERROR BaseDefault #[rustc_object_lifetime_default] -struct B<'a,T>(&'a (), T); //~ ERROR None +struct B<'a,T>(&'a (), T); //~ ERROR BaseDefault #[rustc_object_lifetime_default] struct C<'a,T:'a>(&'a T); //~ ERROR 'a @@ -29,6 +29,6 @@ struct E<'a,'b:'a,T:'b>(&'a T, &'b T); //~ ERROR 'b struct F<'a,'b,T:'a,U:'b>(&'a T, &'b U); //~ ERROR 'a,'b #[rustc_object_lifetime_default] -struct G<'a,'b,T:'a,U:'a+'b>(&'a T, &'b U); //~ ERROR 'a,Some(Ambiguous) +struct G<'a,'b,T:'a,U:'a+'b>(&'a T, &'b U); //~ ERROR 'a,Ambiguous fn main() { } diff --git a/src/test/compile-fail/trait-bounds-cant-coerce.rs b/src/test/compile-fail/trait-bounds-cant-coerce.rs index 3129dceffbb66..836f08d0e78bb 100644 --- a/src/test/compile-fail/trait-bounds-cant-coerce.rs +++ b/src/test/compile-fail/trait-bounds-cant-coerce.rs @@ -22,8 +22,8 @@ fn c(x: Box) { fn d(x: Box) { a(x); //~ ERROR mismatched types - //~| expected `Box` - //~| found `Box` + //~| expected `Box` + //~| found `Box` //~| expected bounds `Send` //~| found no bounds } diff --git a/src/test/run-pass/issue-21058.rs b/src/test/run-pass/issue-21058.rs index 9e8bfc884c984..5fe3434e499e1 100644 --- a/src/test/run-pass/issue-21058.rs +++ b/src/test/run-pass/issue-21058.rs @@ -26,5 +26,5 @@ fn main() { std::intrinsics::type_name::(), // DST std::intrinsics::type_name::() - )}, ("[u8]", "str", "core::marker::Copy", "NT", "DST")); + )}, ("[u8]", "str", "core::marker::Copy + 'static", "NT", "DST")); }