From ef85338175cb322fa07846d20eec91c2800a98e6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 17 Jun 2015 10:02:32 -0400 Subject: [PATCH 1/6] Code up the new lifetime default rules, but leave them disabled for now. --- src/librustc/metadata/tydecode.rs | 8 ++-- src/librustc/metadata/tyencode.rs | 8 ++-- src/librustc/middle/ty.rs | 6 ++- src/librustc/middle/ty_fold.rs | 3 ++ src/librustc_typeck/check/mod.rs | 6 ++- src/librustc_typeck/collect.rs | 16 +++---- src/librustc_typeck/rscope.rs | 79 ++++++++++++++++++++++++------- 7 files changed, 90 insertions(+), 36 deletions(-) diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index a5b9e40593a83..99d27c1ba3190 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") } diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index c078b62dd2d8a..d80316b8f489f 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -414,12 +414,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/ty.rs b/src/librustc/middle/ty.rs index 91a038813b868..d5372d77005d3 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -2245,6 +2245,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 +2259,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 +7331,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 => format!("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..6bae1b68ed421 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)), } 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..89f118a717977 100644 --- a/src/librustc_typeck/rscope.rs +++ b/src/librustc_typeck/rscope.rs @@ -45,6 +45,13 @@ 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; } // A scope in which all regions must be explicitly named. This is used @@ -53,16 +60,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 +86,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 +93,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 +118,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 +159,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 +182,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 +200,25 @@ 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) + } } } @@ -210,6 +249,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) From 909957793e7008e602079e8fd4c74c9bbda37341 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 17 Jun 2015 09:11:34 -0400 Subject: [PATCH 2/6] Add a boolean flag to ExistentialBounds tracking whether the region-bound is expected to change in Rust 1.3, but don't use it for anything in this commit. Note that this is not a "significant" part of the type (it's not part of the formal model) so we have to normalize this away or trans starts to get confused because two equal types wind up with distinct LLVM types. --- src/librustc/metadata/tydecode.rs | 9 +++- src/librustc/metadata/tyencode.rs | 2 + src/librustc/middle/infer/bivariate.rs | 5 ++ src/librustc/middle/infer/combine.rs | 1 + src/librustc/middle/infer/equate.rs | 5 ++ src/librustc/middle/infer/error_reporting.rs | 6 ++- src/librustc/middle/infer/glb.rs | 10 ++++ src/librustc/middle/infer/lub.rs | 5 ++ src/librustc/middle/infer/mod.rs | 7 ++- src/librustc/middle/infer/sub.rs | 39 +++++++++++--- src/librustc/middle/traits/select.rs | 1 + src/librustc/middle/ty.rs | 5 ++ src/librustc/middle/ty_fold.rs | 1 + src/librustc/middle/ty_match.rs | 5 ++ src/librustc/middle/ty_relate/mod.rs | 33 ++++++++++-- src/librustc/util/ppaux.rs | 4 ++ src/librustc_trans/trans/common.rs | 10 ++++ src/librustc_typeck/astconv.rs | 56 ++++++++++++-------- src/librustc_typeck/rscope.rs | 37 +++++++++++++ 19 files changed, 202 insertions(+), 39 deletions(-) diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index 99d27c1ba3190..61fce699dd555 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 d80316b8f489f..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, 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/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 d5372d77005d3..f8b00ba153fcf 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 6bae1b68ed421..5e88a0aefd3ab 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 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..c4fe16805fa56 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 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/rscope.rs b/src/librustc_typeck/rscope.rs index 89f118a717977..d2c982b3099e7 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(), } } From 1e7a6b880c84176a11525e7be5b5a9b15f8143b1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 17 Jun 2015 10:08:26 -0400 Subject: [PATCH 3/6] After inferring regions, scan for any bounds that are due to a lifetime bound that is likely to change. In that case, it will change to 'static, so then scan down the graph to see whether there are any hard constraints that would prevent 'static from being a valid value here. Report a warning. --- src/librustc/diagnostics.rs | 36 +++++++++++ .../middle/infer/region_inference/mod.rs | 47 ++++++++++++++ .../lifetime_bound_will_change_warning_lib.rs | 21 ++++++ .../lifetime-bound-will-change-warning.rs | 64 +++++++++++++++++++ 4 files changed, 168 insertions(+) create mode 100644 src/test/auxiliary/lifetime_bound_will_change_warning_lib.rs create mode 100644 src/test/compile-fail/lifetime-bound-will-change-warning.rs 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/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/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/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 +} From 18e9007ac249c542aa3b73b5aa275e52825ca940 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 17 Jun 2015 10:12:18 -0400 Subject: [PATCH 4/6] Adjust tests to silence warnings (or record them, as appropriate). --- src/test/compile-fail/issue-24446.rs | 2 +- src/test/compile-fail/object-lifetime-default-elision.rs | 1 + src/test/compile-fail/object-lifetime-default-mybox.rs | 1 + src/test/compile-fail/object-lifetime-default.rs | 4 ++-- src/test/compile-fail/trait-bounds-cant-coerce.rs | 4 ++-- src/test/run-pass/issue-21058.rs | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) 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/object-lifetime-default-elision.rs b/src/test/compile-fail/object-lifetime-default-elision.rs index 75ee0bdc9c7ae..371acd24da065 100644 --- a/src/test/compile-fail/object-lifetime-default-elision.rs +++ b/src/test/compile-fail/object-lifetime-default-elision.rs @@ -41,6 +41,7 @@ fn load0<'a>(ss: &'a Box) -> Box { deref(ss) //~^ ERROR cannot infer + //~| WARNING E0398 } fn load1(ss: &SomeTrait) -> &SomeTrait { diff --git a/src/test/compile-fail/object-lifetime-default-mybox.rs b/src/test/compile-fail/object-lifetime-default-mybox.rs index 23ddea4499a72..c8fe197b92c08 100644 --- a/src/test/compile-fail/object-lifetime-default-mybox.rs +++ b/src/test/compile-fail/object-lifetime-default-mybox.rs @@ -28,6 +28,7 @@ fn deref(ss: &T) -> T { fn load0(ss: &MyBox) -> MyBox { deref(ss) //~ ERROR cannot infer + //~^ WARNING E0398 } fn load1<'a,'b>(a: &'a MyBox, diff --git a/src/test/compile-fail/object-lifetime-default.rs b/src/test/compile-fail/object-lifetime-default.rs index b71eadd6d08af..ff4d5c487450a 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 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")); } From 65a456df430260cc4c0a8ef189b764c34153d435 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 17 Jun 2015 14:11:58 -0400 Subject: [PATCH 5/6] Now that I made the warning smarter, these tests no longer require modification --- src/test/compile-fail/object-lifetime-default-elision.rs | 1 - src/test/compile-fail/object-lifetime-default-mybox.rs | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/compile-fail/object-lifetime-default-elision.rs b/src/test/compile-fail/object-lifetime-default-elision.rs index 371acd24da065..75ee0bdc9c7ae 100644 --- a/src/test/compile-fail/object-lifetime-default-elision.rs +++ b/src/test/compile-fail/object-lifetime-default-elision.rs @@ -41,7 +41,6 @@ fn load0<'a>(ss: &'a Box) -> Box { deref(ss) //~^ ERROR cannot infer - //~| WARNING E0398 } fn load1(ss: &SomeTrait) -> &SomeTrait { diff --git a/src/test/compile-fail/object-lifetime-default-mybox.rs b/src/test/compile-fail/object-lifetime-default-mybox.rs index c8fe197b92c08..b5c4e0c767b00 100644 --- a/src/test/compile-fail/object-lifetime-default-mybox.rs +++ b/src/test/compile-fail/object-lifetime-default-mybox.rs @@ -28,7 +28,6 @@ fn deref(ss: &T) -> T { fn load0(ss: &MyBox) -> MyBox { deref(ss) //~ ERROR cannot infer - //~^ WARNING E0398 } fn load1<'a,'b>(a: &'a MyBox, @@ -40,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() { } From db5f3bc65c5f67806df531eadafdc86f8bf7f5e0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 24 Jun 2015 10:17:31 -0400 Subject: [PATCH 6/6] Fix some merge conflicts --- src/librustc/middle/ty.rs | 2 +- src/librustc/util/ppaux.rs | 19 ++++++++++--------- .../compile-fail/object-lifetime-default.rs | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index f8b00ba153fcf..544e594dd21b0 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -7336,7 +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 => format!("BaseDefault"), + ObjectLifetimeDefault::BaseDefault => write!(f, "BaseDefault"), ObjectLifetimeDefault::Specific(ref r) => write!(f, "{:?}", r), } } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index c4fe16805fa56..44bea1cb621ef 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -290,17 +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 && tcx.sess.verbose() { - components.push(format!("WILL-CHANGE")); + if bounds.region_bound_will_change && verbose() { + try!(write!(f, " [WILL-CHANGE]")); } Ok(()) diff --git a/src/test/compile-fail/object-lifetime-default.rs b/src/test/compile-fail/object-lifetime-default.rs index ff4d5c487450a..104e10f3207db 100644 --- a/src/test/compile-fail/object-lifetime-default.rs +++ b/src/test/compile-fail/object-lifetime-default.rs @@ -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() { }