about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2015-06-17 09:11:34 -0400
committerNiko Matsakis <niko@alum.mit.edu>2015-07-03 19:42:35 -0400
commit909957793e7008e602079e8fd4c74c9bbda37341 (patch)
treec0b0e7aac707de36b1764ea47f84293099bee7f4
parentef85338175cb322fa07846d20eec91c2800a98e6 (diff)
downloadrust-909957793e7008e602079e8fd4c74c9bbda37341.tar.gz
rust-909957793e7008e602079e8fd4c74c9bbda37341.zip
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.
-rw-r--r--src/librustc/metadata/tydecode.rs9
-rw-r--r--src/librustc/metadata/tyencode.rs2
-rw-r--r--src/librustc/middle/infer/bivariate.rs5
-rw-r--r--src/librustc/middle/infer/combine.rs1
-rw-r--r--src/librustc/middle/infer/equate.rs5
-rw-r--r--src/librustc/middle/infer/error_reporting.rs6
-rw-r--r--src/librustc/middle/infer/glb.rs10
-rw-r--r--src/librustc/middle/infer/lub.rs5
-rw-r--r--src/librustc/middle/infer/mod.rs7
-rw-r--r--src/librustc/middle/infer/sub.rs39
-rw-r--r--src/librustc/middle/traits/select.rs1
-rw-r--r--src/librustc/middle/ty.rs5
-rw-r--r--src/librustc/middle/ty_fold.rs1
-rw-r--r--src/librustc/middle/ty_match.rs5
-rw-r--r--src/librustc/middle/ty_relate/mod.rs33
-rw-r--r--src/librustc/util/ppaux.rs4
-rw-r--r--src/librustc_trans/trans/common.rs10
-rw-r--r--src/librustc_typeck/astconv.rs56
-rw-r--r--src/librustc_typeck/rscope.rs37
19 files changed, 202 insertions, 39 deletions
diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index 99d27c1ba31..61fce699dd5 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<F>(st: &mut PState, mut _conv: F) -> ty::BuiltinBounds where
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index d80316b8f48..441f9f102ae 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 d2268894b20..ad53fb4a8a2 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<T:Relate<'a,'tcx>>(&mut self,
                                                variance: ty::Variance,
                                                a: &T,
diff --git a/src/librustc/middle/infer/combine.rs b/src/librustc/middle/infer/combine.rs
index 13b2214d353..0d081cfeee0 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<ty_relate::Cause>,
 }
 
 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 cbbf73d9420..c0dcda1792b 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<T:Relate<'a,'tcx>>(&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 6d5b47d8ed9..8d226739e16 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 d6b03266b1f..adfd1a8a7d7 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<T:Relate<'a,'tcx>>(&mut self,
                                                variance: ty::Variance,
                                                a: &T,
diff --git a/src/librustc/middle/infer/lub.rs b/src/librustc/middle/infer/lub.rs
index 9d993ead5ca..f10d4adc8e5 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<T:Relate<'a,'tcx>>(&mut self,
                                                variance: ty::Variance,
                                                a: &T,
diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs
index 7df37bdae07..34726436ff7 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 2b7f9a74b8b..7c40e96a2f7 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<F,R>(&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<T:Relate<'a,'tcx>>(&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 3bc4fd0c0a1..abc300869ad 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 d5372d77005..f8b00ba153f 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<PolyProjectionPredicate<'tcx>>,
+
+    // 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 6bae1b68ed4..5e88a0aefd3 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 135118820a7..5776235780a 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<T:Relate<'a,'tcx>>(&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 4e88e23377d..b8b2469b206 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<T, ty::type_err<'tcx>>;
 
+#[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<F,R>(&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<T:Relate<'a,'tcx>>(&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 91b547e9679..c4fe16805fa 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 96564277cdc..ed2d303b5e1 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<T>(&mut self, t: &ty::Binder<T>) -> ty::Binder<T>
             where T : TypeFoldable<'tcx>
         {
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 00b7f420614..7f29af0d84b 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,
-                                                     &region_bounds,
-                                                     principal_trait_ref,
-                                                     builtin_bounds);
+    let region_bound =
+        compute_object_lifetime_bound(this,
+                                      span,
+                                      &region_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<ty::Region> // 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 89f118a7179..d2c982b3099 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(),
         }
     }