about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-03-19 18:39:50 -0700
committerbors <bors@rust-lang.org>2016-03-19 18:39:50 -0700
commit78e8a00514052bf812af0677dde8710336d77cbb (patch)
treef028367e726ba4fa26b458b5c5e9d1e5c64561d4
parent02310fd313cf3eed11fe5ac11921b73cc3b648d4 (diff)
parent43aed96d0da2cfa6e5da3058c107f3de4a3c009b (diff)
downloadrust-78e8a00514052bf812af0677dde8710336d77cbb.tar.gz
rust-78e8a00514052bf812af0677dde8710336d77cbb.zip
Auto merge of #32306 - nikomatsakis:issue-32278, r=eddyb
create fewer region variables in coercions

Fixes #32278.

r? @eddyb
-rw-r--r--src/librustc/middle/infer/error_reporting.rs214
-rw-r--r--src/librustc/middle/infer/mod.rs10
-rw-r--r--src/librustc/middle/infer/region_inference/mod.rs37
-rw-r--r--src/librustc_typeck/check/coercion.rs199
-rw-r--r--src/test/compile-fail/issue-10291.rs3
-rw-r--r--src/test/compile-fail/issue-13058.rs4
-rw-r--r--src/test/compile-fail/issue-7573.rs4
-rw-r--r--src/test/compile-fail/lifetime-inference-give-expl-lifetime-param-3.rs16
-rw-r--r--src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs12
-rw-r--r--src/test/compile-fail/lub-if.rs4
-rw-r--r--src/test/compile-fail/lub-match.rs6
-rw-r--r--src/test/compile-fail/object-lifetime-default-mybox.rs3
-rw-r--r--src/test/compile-fail/region-lifetime-bounds-on-fns-where-clause.rs2
-rw-r--r--src/test/compile-fail/region-multiple-lifetime-bounds-on-fns-where-clause.rs4
-rw-r--r--src/test/compile-fail/regions-early-bound-error-method.rs3
-rw-r--r--src/test/compile-fail/regions-early-bound-error.rs6
-rw-r--r--src/test/compile-fail/regions-glb-free-free.rs4
-rw-r--r--src/test/compile-fail/regions-lifetime-bounds-on-fns.rs4
-rw-r--r--src/test/compile-fail/regions-nested-fns.rs6
-rw-r--r--src/test/compile-fail/regions-static-bound.rs2
-rw-r--r--src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.rs2
-rw-r--r--src/test/compile-fail/wf-static-method.rs4
-rw-r--r--src/test/run-pass/regions-lub-ref-ref-rc.rs36
23 files changed, 396 insertions, 189 deletions
diff --git a/src/librustc/middle/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs
index 2f1af4184e5..089eae89d04 100644
--- a/src/librustc/middle/infer/error_reporting.rs
+++ b/src/librustc/middle/infer/error_reporting.rs
@@ -68,6 +68,7 @@ use super::region_inference::SubSupConflict;
 use super::region_inference::GenericBoundFailure;
 use super::region_inference::GenericKind;
 use super::region_inference::ProcessedErrors;
+use super::region_inference::ProcessedErrorOrigin;
 use super::region_inference::SameRegions;
 
 use std::collections::HashSet;
@@ -232,7 +233,7 @@ pub trait ErrorReporting<'tcx> {
                             errors: &Vec<RegionResolutionError<'tcx>>);
 
     fn process_errors(&self, errors: &Vec<RegionResolutionError<'tcx>>)
-                      -> Vec<RegionResolutionError<'tcx>>;
+                      -> Option<Vec<RegionResolutionError<'tcx>>>;
 
     fn report_type_error(&self,
                          trace: TypeTrace<'tcx>,
@@ -246,7 +247,8 @@ pub trait ErrorReporting<'tcx> {
 
     fn report_and_explain_type_error(&self,
                                      trace: TypeTrace<'tcx>,
-                                     terr: &TypeError<'tcx>);
+                                     terr: &TypeError<'tcx>)
+                                     -> DiagnosticBuilder<'tcx>;
 
     fn values_str(&self, values: &ValuePairs<'tcx>) -> Option<String>;
 
@@ -258,7 +260,8 @@ pub trait ErrorReporting<'tcx> {
     fn report_concrete_failure(&self,
                                origin: SubregionOrigin<'tcx>,
                                sub: Region,
-                               sup: Region);
+                               sup: Region)
+                                -> DiagnosticBuilder<'tcx>;
 
     fn report_generic_bound_failure(&self,
                                     origin: SubregionOrigin<'tcx>,
@@ -273,8 +276,7 @@ pub trait ErrorReporting<'tcx> {
                                sup_region: Region);
 
     fn report_processed_errors(&self,
-                               var_origin: &[RegionVariableOrigin],
-                               trace_origin: &[(TypeTrace<'tcx>, TypeError<'tcx>)],
+                               origins: &[ProcessedErrorOrigin<'tcx>],
                                same_regions: &[SameRegions]);
 
     fn give_suggestion(&self, err: &mut DiagnosticBuilder, same_regions: &[SameRegions]);
@@ -303,12 +305,19 @@ trait ErrorReportingHelpers<'tcx> {
 impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
     fn report_region_errors(&self,
                             errors: &Vec<RegionResolutionError<'tcx>>) {
-        let p_errors = self.process_errors(errors);
-        let errors = if p_errors.is_empty() { errors } else { &p_errors };
+        debug!("report_region_errors(): {} errors to start", errors.len());
+
+        // try to pre-process the errors, which will group some of them
+        // together into a `ProcessedErrors` group:
+        let processed_errors = self.process_errors(errors);
+        let errors = processed_errors.as_ref().unwrap_or(errors);
+
+        debug!("report_region_errors: {} errors after preprocessing", errors.len());
+
         for error in errors {
             match error.clone() {
                 ConcreteFailure(origin, sub, sup) => {
-                    self.report_concrete_failure(origin, sub, sup);
+                    self.report_concrete_failure(origin, sub, sup).emit();
                 }
 
                 GenericBoundFailure(kind, param_ty, sub) => {
@@ -323,13 +332,10 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                                                  sup_origin, sup_r);
                 }
 
-                ProcessedErrors(ref var_origins,
-                                ref trace_origins,
+                ProcessedErrors(ref origins,
                                 ref same_regions) => {
                     if !same_regions.is_empty() {
-                        self.report_processed_errors(&var_origins[..],
-                                                     &trace_origins[..],
-                                                     &same_regions[..]);
+                        self.report_processed_errors(origins, same_regions);
                     }
                 }
             }
@@ -341,46 +347,73 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
     // parameters to the user. This is done so that we can have a more
     // complete view of what lifetimes should be the same.
     // If the return value is an empty vector, it means that processing
-    // failed (so the return value of this method should not be used)
+    // failed (so the return value of this method should not be used).
+    //
+    // The method also attempts to weed out messages that seem like
+    // duplicates that will be unhelpful to the end-user. But
+    // obviously it never weeds out ALL errors.
     fn process_errors(&self, errors: &Vec<RegionResolutionError<'tcx>>)
-                      -> Vec<RegionResolutionError<'tcx>> {
+                      -> Option<Vec<RegionResolutionError<'tcx>>> {
         debug!("process_errors()");
-        let mut var_origins = Vec::new();
-        let mut trace_origins = Vec::new();
+        let mut origins = Vec::new();
+
+        // we collect up ConcreteFailures and SubSupConflicts that are
+        // relating free-regions bound on the fn-header and group them
+        // together into this vector
         let mut same_regions = Vec::new();
-        let mut processed_errors = Vec::new();
+
+        // here we put errors that we will not be able to process nicely
+        let mut other_errors = Vec::new();
+
+        // we collect up GenericBoundFailures in here.
+        let mut bound_failures = Vec::new();
+
         for error in errors {
-            match error.clone() {
-                ConcreteFailure(origin, sub, sup) => {
+            match *error {
+                ConcreteFailure(ref origin, sub, sup) => {
                     debug!("processing ConcreteFailure");
-                    let trace = match origin {
-                        infer::Subtype(trace) => Some(trace),
-                        _ => None,
-                    };
                     match free_regions_from_same_fn(self.tcx, sub, sup) {
-                        Some(ref same_frs) if trace.is_some() => {
-                            let trace = trace.unwrap();
-                            let terr = TypeError::RegionsDoesNotOutlive(sup,
-                                                                        sub);
-                            trace_origins.push((trace, terr));
+                        Some(ref same_frs) => {
+                            origins.push(
+                                ProcessedErrorOrigin::ConcreteFailure(
+                                    origin.clone(),
+                                    sub,
+                                    sup));
                             append_to_same_regions(&mut same_regions, same_frs);
                         }
-                        _ => processed_errors.push((*error).clone()),
+                        _ => {
+                            other_errors.push(error.clone());
+                        }
                     }
                 }
-                SubSupConflict(var_origin, _, sub_r, _, sup_r) => {
+                SubSupConflict(ref var_origin, _, sub_r, _, sup_r) => {
                     debug!("processing SubSupConflict sub: {:?} sup: {:?}", sub_r, sup_r);
                     match free_regions_from_same_fn(self.tcx, sub_r, sup_r) {
                         Some(ref same_frs) => {
-                            var_origins.push(var_origin);
+                            origins.push(
+                                ProcessedErrorOrigin::VariableFailure(
+                                    var_origin.clone()));
                             append_to_same_regions(&mut same_regions, same_frs);
                         }
-                        None => processed_errors.push((*error).clone()),
+                        None => {
+                            other_errors.push(error.clone());
+                        }
                     }
                 }
-                _ => ()  // This shouldn't happen
+                GenericBoundFailure(ref origin, ref kind, region) => {
+                    bound_failures.push((origin.clone(), kind.clone(), region));
+                }
+                ProcessedErrors(..) => {
+                    panic!("should not encounter a `ProcessedErrors` yet: {:?}", error)
+                }
             }
         }
+
+        // ok, let's pull together the errors, sorted in an order that
+        // we think will help user the best
+        let mut processed_errors = vec![];
+
+        // first, put the processed errors, if any
         if !same_regions.is_empty() {
             let common_scope_id = same_regions[0].scope_id;
             for sr in &same_regions {
@@ -390,16 +423,39 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                 if sr.scope_id != common_scope_id {
                     debug!("returning empty result from process_errors because
                             {} != {}", sr.scope_id, common_scope_id);
-                    return vec!();
+                    return None;
                 }
             }
-            let pe = ProcessedErrors(var_origins, trace_origins, same_regions);
+            assert!(origins.len() > 0);
+            let pe = ProcessedErrors(origins, same_regions);
             debug!("errors processed: {:?}", pe);
             processed_errors.push(pe);
         }
-        return processed_errors;
 
+        // next, put the other misc errors
+        processed_errors.extend(other_errors);
+
+        // finally, put the `T: 'a` errors, but only if there were no
+        // other errors. otherwise, these have a very high rate of
+        // being unhelpful in practice. This is because they are
+        // basically secondary checks that test the state of the
+        // region graph after the rest of inference is done, and the
+        // other kinds of errors indicate that the region constraint
+        // graph is internally inconsistent, so these test results are
+        // likely to be meaningless.
+        if processed_errors.is_empty() {
+            for (origin, kind, region) in bound_failures {
+                processed_errors.push(GenericBoundFailure(origin, kind, region));
+            }
+        }
+
+        // we should always wind up with SOME errors, unless there were no
+        // errors to start
+        assert!(if errors.len() > 0 {processed_errors.len() > 0} else {true});
 
+        return Some(processed_errors);
+
+        #[derive(Debug)]
         struct FreeRegionsFromSameFn {
             sub_fr: ty::FreeRegion,
             sup_fr: ty::FreeRegion,
@@ -459,11 +515,12 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
 
         fn append_to_same_regions(same_regions: &mut Vec<SameRegions>,
                                   same_frs: &FreeRegionsFromSameFn) {
+            debug!("append_to_same_regions(same_regions={:?}, same_frs={:?})",
+                   same_regions, same_frs);
             let scope_id = same_frs.scope_id;
             let (sub_fr, sup_fr) = (same_frs.sub_fr, same_frs.sup_fr);
-            for sr in &mut *same_regions {
-                if sr.contains(&sup_fr.bound_region)
-                   && scope_id == sr.scope_id {
+            for sr in same_regions.iter_mut() {
+                if sr.contains(&sup_fr.bound_region) && scope_id == sr.scope_id {
                     sr.push(sub_fr.bound_region);
                     return
                 }
@@ -569,11 +626,12 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
 
     fn report_and_explain_type_error(&self,
                                      trace: TypeTrace<'tcx>,
-                                     terr: &TypeError<'tcx>) {
+                                     terr: &TypeError<'tcx>)
+                                     -> DiagnosticBuilder<'tcx> {
         let span = trace.origin.span();
         let mut err = self.report_type_error(trace, terr);
         self.tcx.note_and_explain_type_err(&mut err, terr, span);
-        err.emit();
+        err
     }
 
     /// Returns a string of the form "expected `{}`, found `{}`", or None if this is a derived
@@ -678,11 +736,12 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
     fn report_concrete_failure(&self,
                                origin: SubregionOrigin<'tcx>,
                                sub: Region,
-                               sup: Region) {
+                               sup: Region)
+                                -> DiagnosticBuilder<'tcx> {
         match origin {
             infer::Subtype(trace) => {
                 let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
-                self.report_and_explain_type_error(trace, &terr);
+                self.report_and_explain_type_error(trace, &terr)
             }
             infer::Reborrow(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0312,
@@ -696,7 +755,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "...but the borrowed content is only valid for ",
                     sup,
                     "");
-                err.emit();
+                err
             }
             infer::ReborrowUpvar(span, ref upvar_id) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0313,
@@ -712,7 +771,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                              self.tcx.local_var_name_str(upvar_id.var_id)),
                     sup,
                     "");
-                err.emit();
+                err
             }
             infer::InfStackClosure(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0314,
@@ -725,7 +784,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "...but the closure's stack frame is only valid for ",
                     sup,
                     "");
-                err.emit();
+                err
             }
             infer::InvokeClosure(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0315,
@@ -734,7 +793,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "the closure is only valid for ",
                     sup,
                     "");
-                err.emit();
+                err
             }
             infer::DerefPointer(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0473,
@@ -743,7 +802,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "the reference is only valid for ",
                     sup,
                     "");
-                err.emit();
+                err
             }
             infer::FreeVariable(span, id) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0474,
@@ -757,7 +816,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "closure is valid for ",
                     sub,
                     "");
-                err.emit();
+                err
             }
             infer::IndexSlice(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0475,
@@ -766,7 +825,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "the slice is only valid for ",
                     sup,
                     "");
-                err.emit();
+                err
             }
             infer::RelateObjectBound(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0476,
@@ -780,7 +839,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "source pointer is only valid for ",
                     sup,
                     "");
-                err.emit();
+                err
             }
             infer::RelateParamBound(span, ty) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0477,
@@ -790,7 +849,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                                         "type must outlive ",
                                         sub,
                                         "");
-                err.emit();
+                err
             }
             infer::RelateRegionParamBound(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0478,
@@ -803,7 +862,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "but lifetime parameter must outlive ",
                     sub,
                     "");
-                err.emit();
+                err
             }
             infer::RelateDefaultParamBound(span, ty) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0479,
@@ -814,7 +873,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                                         "type must outlive ",
                                         sub,
                                         "");
-                err.emit();
+                err
             }
             infer::CallRcvr(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0480,
@@ -824,7 +883,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "the receiver is only valid for ",
                     sup,
                     "");
-                err.emit();
+                err
             }
             infer::CallArg(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0481,
@@ -834,7 +893,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "the function argument is only valid for ",
                     sup,
                     "");
-                err.emit();
+                err
             }
             infer::CallReturn(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0482,
@@ -844,7 +903,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "the return value is only valid for ",
                     sup,
                     "");
-                err.emit();
+                err
             }
             infer::Operand(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0483,
@@ -854,7 +913,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "the operand is only valid for ",
                     sup,
                     "");
-                err.emit();
+                err
             }
             infer::AddrOf(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0484,
@@ -863,7 +922,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "the borrow is only valid for ",
                     sup,
                     "");
-                err.emit();
+                err
             }
             infer::AutoBorrow(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0485,
@@ -873,7 +932,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "the automatic borrow is only valid for ",
                     sup,
                     "");
-                err.emit();
+                err
             }
             infer::ExprTypeIsNotInScope(t, span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0486,
@@ -884,7 +943,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "type is only valid for ",
                     sup,
                     "");
-                err.emit();
+                err
             }
             infer::SafeDestructor(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0487,
@@ -899,7 +958,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "subregion: ",
                     sub,
                     "");
-                err.emit();
+                err
             }
             infer::BindingTypeIsNotValidAtDecl(span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0488,
@@ -908,7 +967,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "the variable is only valid for ",
                     sup,
                     "");
-                err.emit();
+                err
             }
             infer::ParameterInScope(_, span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0489,
@@ -917,7 +976,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "the parameter is only valid for ",
                     sub,
                     "");
-                err.emit();
+                err
             }
             infer::DataBorrowed(ty, span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0490,
@@ -925,7 +984,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                           self.ty_to_string(ty));
                 self.tcx.note_and_explain_region(&mut err, "the type is valid for ", sub, "");
                 self.tcx.note_and_explain_region(&mut err, "but the borrow lasts for ", sup, "");
-                err.emit();
+                err
             }
             infer::ReferenceOutlivesReferent(ty, span) => {
                 let mut err = struct_span_err!(self.tcx.sess, span, E0491,
@@ -940,7 +999,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
                     "but the referenced data is only valid for ",
                     sup,
                     "");
-                err.emit();
+                err
             }
         }
     }
@@ -970,19 +1029,22 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
     }
 
     fn report_processed_errors(&self,
-                               var_origins: &[RegionVariableOrigin],
-                               trace_origins: &[(TypeTrace<'tcx>, TypeError<'tcx>)],
+                               origins: &[ProcessedErrorOrigin<'tcx>],
                                same_regions: &[SameRegions]) {
-        for (i, vo) in var_origins.iter().enumerate() {
-            let mut err = self.report_inference_failure(vo.clone());
-            if i == var_origins.len() - 1 {
+        for (i, origin) in origins.iter().enumerate() {
+            let mut err = match *origin {
+                ProcessedErrorOrigin::VariableFailure(ref var_origin) =>
+                    self.report_inference_failure(var_origin.clone()),
+                ProcessedErrorOrigin::ConcreteFailure(ref sr_origin, sub, sup) =>
+                    self.report_concrete_failure(sr_origin.clone(), sub, sup),
+            };
+
+            // attach the suggestion to the last such error
+            if i == origins.len() - 1 {
                 self.give_suggestion(&mut err, same_regions);
             }
-            err.emit();
-        }
 
-        for &(ref trace, ref terr) in trace_origins {
-            self.report_and_explain_type_error(trace.clone(), terr);
+            err.emit();
         }
     }
 
diff --git a/src/librustc/middle/infer/mod.rs b/src/librustc/middle/infer/mod.rs
index 4ff1de42211..290d9afa449 100644
--- a/src/librustc/middle/infer/mod.rs
+++ b/src/librustc/middle/infer/mod.rs
@@ -407,7 +407,7 @@ pub fn common_supertype<'a, 'tcx>(cx: &InferCtxt<'a, 'tcx>,
     match result {
         Ok(t) => t,
         Err(ref err) => {
-            cx.report_and_explain_type_error(trace, err);
+            cx.report_and_explain_type_error(trace, err).emit();
             cx.tcx.types.err
         }
     }
@@ -1396,7 +1396,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 found: actual
             })
         };
-        self.report_and_explain_type_error(trace, &err);
+        self.report_and_explain_type_error(trace, &err).emit();
     }
 
     pub fn report_conflicting_default_types(&self,
@@ -1411,11 +1411,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             })
         };
 
-        self.report_and_explain_type_error(trace,
+        self.report_and_explain_type_error(
+            trace,
             &TypeError::TyParamDefaultMismatch(ExpectedFound {
                 expected: expected,
                 found: actual
-        }));
+            }))
+            .emit();
     }
 
     pub fn replace_late_bound_regions_with_fresh_var<T>(
diff --git a/src/librustc/middle/infer/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs
index 36462b68288..7bba495e467 100644
--- a/src/librustc/middle/infer/region_inference/mod.rs
+++ b/src/librustc/middle/infer/region_inference/mod.rs
@@ -17,7 +17,7 @@ pub use self::CombineMapType::*;
 pub use self::RegionResolutionError::*;
 pub use self::VarValue::*;
 
-use super::{RegionVariableOrigin, SubregionOrigin, TypeTrace, MiscVariable};
+use super::{RegionVariableOrigin, SubregionOrigin, MiscVariable};
 use super::unify_key;
 
 use rustc_data_structures::graph::{self, Direction, NodeIndex};
@@ -27,7 +27,6 @@ use middle::ty::{self, Ty, TyCtxt};
 use middle::ty::{BoundRegion, Region, RegionVid};
 use middle::ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound};
 use middle::ty::{ReLateBound, ReScope, ReVar, ReSkolemized, BrFresh};
-use middle::ty::error::TypeError;
 use util::common::indenter;
 use util::nodemap::{FnvHashMap, FnvHashSet};
 
@@ -152,11 +151,16 @@ pub enum RegionResolutionError<'tcx> {
     /// more specific errors message by suggesting to the user where they
     /// should put a lifetime. In those cases we process and put those errors
     /// into `ProcessedErrors` before we do any reporting.
-    ProcessedErrors(Vec<RegionVariableOrigin>,
-                    Vec<(TypeTrace<'tcx>, TypeError<'tcx>)>,
+    ProcessedErrors(Vec<ProcessedErrorOrigin<'tcx>>,
                     Vec<SameRegions>),
 }
 
+#[derive(Clone, Debug)]
+pub enum ProcessedErrorOrigin<'tcx> {
+    ConcreteFailure(SubregionOrigin<'tcx>, Region, Region),
+    VariableFailure(RegionVariableOrigin),
+}
+
 /// SameRegions is used to group regions that we think are the same and would
 /// like to indicate so to the user.
 /// For example, the following function
@@ -530,16 +534,14 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
         assert!(self.values_are_none());
 
         debug!("RegionVarBindings: lub_regions({:?}, {:?})", a, b);
-        match (a, b) {
-            (ReStatic, _) | (_, ReStatic) => {
-                ReStatic // nothing lives longer than static
-            }
-
-            _ => {
-                self.combine_vars(Lub, a, b, origin.clone(), |this, old_r, new_r| {
-                    this.make_subregion(origin.clone(), old_r, new_r)
-                })
-            }
+        if a == ty::ReStatic || b == ty::ReStatic {
+            ReStatic // nothing lives longer than static
+        } else if a == b {
+            a // LUB(a,a) = a
+        } else {
+            self.combine_vars(Lub, a, b, origin.clone(), |this, old_r, new_r| {
+                this.make_subregion(origin.clone(), old_r, new_r)
+            })
         }
     }
 
@@ -550,8 +552,11 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
         debug!("RegionVarBindings: glb_regions({:?}, {:?})", a, b);
         match (a, b) {
             (ReStatic, r) | (r, ReStatic) => {
-                // static lives longer than everything else
-                r
+                r // static lives longer than everything else
+            }
+
+            _ if a == b => {
+                a // GLB(a,a) = a
             }
 
             _ => {
diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs
index aa359c95e2d..bb27333025e 100644
--- a/src/librustc_typeck/check/coercion.rs
+++ b/src/librustc_typeck/check/coercion.rs
@@ -71,7 +71,7 @@ use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
 use middle::ty::{self, LvaluePreference, TypeAndMut, Ty, TyCtxt};
 use middle::ty::fold::TypeFoldable;
 use middle::ty::error::TypeError;
-use middle::ty::relate::{relate_substs, RelateResult, TypeRelation};
+use middle::ty::relate::{relate_substs, Relate, RelateResult, TypeRelation};
 use util::common::indent;
 
 use std::cell::RefCell;
@@ -112,8 +112,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         self.fcx.tcx()
     }
 
-    /// Unify two types (using sub or lub) and produce a noop coercion.
-    fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
+    fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
         let infcx = self.fcx.infcx();
         infcx.commit_if_ok(|_| {
             let trace = TypeTrace::types(self.origin, false, a, b);
@@ -122,7 +121,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             } else {
                 infcx.sub(false, trace).relate(&a, &b)
             }
-        }).and_then(|ty| self.identity(ty))
+        })
+    }
+
+    /// Unify two types (using sub or lub) and produce a noop coercion.
+    fn unify_and_identity(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> {
+        self.unify(&a, &b).and_then(|ty| self.identity(ty))
     }
 
     /// Synthesize an identity adjustment.
@@ -166,8 +170,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 return self.coerce_unsafe_ptr(a, b, mt_b.mutbl);
             }
 
-            ty::TyRef(_, mt_b) => {
-                return self.coerce_borrowed_pointer(exprs, a, b, mt_b.mutbl);
+            ty::TyRef(r_b, mt_b) => {
+                return self.coerce_borrowed_pointer(exprs, a, b, r_b, mt_b);
             }
 
             _ => {}
@@ -187,7 +191,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             }
             _ => {
                 // Otherwise, just use unification rules.
-                self.unify(a, b)
+                self.unify_and_identity(a, b)
             }
         }
     }
@@ -199,7 +203,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                                          exprs: &E,
                                          a: Ty<'tcx>,
                                          b: Ty<'tcx>,
-                                         mutbl_b: hir::Mutability)
+                                         r_b: &'tcx ty::Region,
+                                         mt_b: TypeAndMut<'tcx>)
                                          -> CoerceResult<'tcx>
         // FIXME(eddyb) use copyable iterators when that becomes ergonomic.
         where E: Fn() -> I,
@@ -213,57 +218,171 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         // to type check, we will construct the type that `&M*expr` would
         // yield.
 
-        match a.sty {
-            ty::TyRef(_, mt_a) => {
-                try!(coerce_mutbls(mt_a.mutbl, mutbl_b));
+        let (r_a, mt_a) = match a.sty {
+            ty::TyRef(r_a, mt_a) => {
+                try!(coerce_mutbls(mt_a.mutbl, mt_b.mutbl));
+                (r_a, mt_a)
             }
-            _ => return self.unify(a, b)
-        }
+            _ => return self.unify_and_identity(a, b)
+        };
 
         let span = self.origin.span();
-        let coercion = Coercion(span);
-        let r_borrow = self.fcx.infcx().next_region_var(coercion);
-        let r_borrow = self.tcx().mk_region(r_borrow);
-        let autoref = Some(AutoPtr(r_borrow, mutbl_b));
 
-        let lvalue_pref = LvaluePreference::from_mutbl(mutbl_b);
+        let lvalue_pref = LvaluePreference::from_mutbl(mt_b.mutbl);
         let mut first_error = None;
+        let mut r_borrow_var = None;
         let (_, autoderefs, success) = autoderef(self.fcx, span, a, exprs,
                                                  UnresolvedTypeAction::Ignore,
                                                  lvalue_pref,
-                                                 |inner_ty, autoderef| {
+                                                 |referent_ty, autoderef|
+        {
             if autoderef == 0 {
                 // Don't let this pass, otherwise it would cause
                 // &T to autoref to &&T.
                 return None;
             }
-            let ty = self.tcx().mk_ref(r_borrow,
-                                        TypeAndMut {ty: inner_ty, mutbl: mutbl_b});
-            match self.unify(ty, b) {
+
+            // At this point, we have deref'd `a` to `referent_ty`.  So
+            // imagine we are coercing from `&'a mut Vec<T>` to `&'b mut [T]`.
+            // In the autoderef loop for `&'a mut Vec<T>`, we would get
+            // three callbacks:
+            //
+            // - `&'a mut Vec<T>` -- 0 derefs, just ignore it
+            // - `Vec<T>` -- 1 deref
+            // - `[T]` -- 2 deref
+            //
+            // At each point after the first callback, we want to
+            // check to see whether this would match out target type
+            // (`&'b mut [T]`) if we autoref'd it. We can't just
+            // compare the referent types, though, because we still
+            // have to consider the mutability. E.g., in the case
+            // we've been considering, we have an `&mut` reference, so
+            // the `T` in `[T]` needs to be unified with equality.
+            //
+            // Therefore, we construct reference types reflecting what
+            // the types will be after we do the final auto-ref and
+            // compare those. Note that this means we use the target
+            // mutability [1], since it may be that we are coercing
+            // from `&mut T` to `&U`.
+            //
+            // One fine point concerns the region that we use. We
+            // choose the region such that the region of the final
+            // type that results from `unify` will be the region we
+            // want for the autoref:
+            //
+            // - if in sub mode, that means we want to use `'b` (the
+            //   region from the target reference) for both
+            //   pointers [2]. This is because sub mode (somewhat
+            //   arbitrarily) returns the subtype region.  In the case
+            //   where we are coercing to a target type, we know we
+            //   want to use that target type region (`'b`) because --
+            //   for the program to type-check -- it must be the
+            //   smaller of the two.
+            //   - One fine point. It may be surprising that we can
+            //     use `'b` without relating `'a` and `'b`. The reason
+            //     that this is ok is that what we produce is
+            //     effectively a `&'b *x` expression (if you could
+            //     annotate the region of a borrow), and regionck has
+            //     code that adds edges from the region of a borrow
+            //     (`'b`, here) into the regions in the borrowed
+            //     expression (`*x`, here).  (Search for "link".)
+            // - if in lub mode, things can get fairly complicated. The
+            //   easiest thing is just to make a fresh
+            //   region variable [4], which effectively means we defer
+            //   the decision to region inference (and regionck, which will add
+            //   some more edges to this variable). However, this can wind up
+            //   creating a crippling number of variables in some cases --
+            //   e.g. #32278 -- so we optimize one particular case [3].
+            //   Let me try to explain with some examples:
+            //   - The "running example" above represents the simple case,
+            //     where we have one `&` reference at the outer level and
+            //     ownership all the rest of the way down. In this case,
+            //     we want `LUB('a, 'b)` as the resulting region.
+            //   - However, if there are nested borrows, that region is
+            //     too strong. Consider a coercion from `&'a &'x Rc<T>` to
+            //     `&'b T`. In this case, `'a` is actually irrelevant.
+            //     The pointer we want is `LUB('x, 'b`). If we choose `LUB('a,'b)`
+            //     we get spurious errors (`run-pass/regions-lub-ref-ref-rc.rs`).
+            //     (The errors actually show up in borrowck, typically, because
+            //     this extra edge causes the region `'a` to be inferred to something
+            //     too big, which then results in borrowck errors.)
+            //   - We could track the innermost shared reference, but there is already
+            //     code in regionck that has the job of creating links between
+            //     the region of a borrow and the regions in the thing being
+            //     borrowed (here, `'a` and `'x`), and it knows how to handle
+            //     all the various cases. So instead we just make a region variable
+            //     and let regionck figure it out.
+            let r = if !self.use_lub {
+                r_b // [2] above
+            } else if autoderef == 1 {
+                r_a // [3] above
+            } else {
+                if r_borrow_var.is_none() { // create var lazilly, at most once
+                    let coercion = Coercion(span);
+                    let r = self.fcx.infcx().next_region_var(coercion);
+                    r_borrow_var = Some(self.tcx().mk_region(r)); // [4] above
+                }
+                r_borrow_var.unwrap()
+            };
+            let derefd_ty_a = self.tcx().mk_ref(r, TypeAndMut {
+                ty: referent_ty,
+                mutbl: mt_b.mutbl // [1] above
+            });
+            match self.unify(derefd_ty_a, b) {
+                Ok(ty) => Some(ty),
                 Err(err) => {
                     if first_error.is_none() {
                         first_error = Some(err);
                     }
                     None
                 }
-                Ok((ty, _)) => Some(ty)
             }
         });
 
-        match success {
-            Some(ty) => {
-                Ok((ty, AdjustDerefRef(AutoDerefRef {
-                    autoderefs: autoderefs,
-                    autoref: autoref,
-                    unsize: None
-                })))
-            }
+        // Extract type or return an error. We return the first error
+        // we got, which should be from relating the "base" type
+        // (e.g., in example above, the failure from relating `Vec<T>`
+        // to the target type), since that should be the least
+        // confusing.
+        let ty = match success {
+            Some(ty) => ty,
             None => {
-                // Return original error as if overloaded deref was never
-                // attempted, to avoid irrelevant/confusing error messages.
-                Err(first_error.expect("coerce_borrowed_pointer failed with no error?"))
+                let err = first_error.expect("coerce_borrowed_pointer had no error");
+                debug!("coerce_borrowed_pointer: failed with err = {:?}", err);
+                return Err(err);
             }
+        };
+
+        // Now apply the autoref. We have to extract the region out of
+        // the final ref type we got.
+        if ty == a && mt_a.mutbl == hir::MutImmutable && autoderefs == 1 {
+            // As a special case, if we would produce `&'a *x`, that's
+            // a total no-op. We end up with the type `&'a T` just as
+            // we started with.  In that case, just skip it
+            // altogether. This is just an optimization.
+            //
+            // Note that for `&mut`, we DO want to reborrow --
+            // otherwise, this would be a move, which might be an
+            // error. For example `foo(self.x)` where `self` and
+            // `self.x` both have `&mut `type would be a move of
+            // `self.x`, but we auto-coerce it to `foo(&mut *self.x)`,
+            // which is a borrow.
+            assert_eq!(mt_b.mutbl, hir::MutImmutable); // can only coerce &T -> &U
+            return self.identity(ty);
         }
+        let r_borrow = match ty.sty {
+            ty::TyRef(r_borrow, _) => r_borrow,
+            _ => self.tcx().sess.span_bug(span,
+                                          &format!("expected a ref type, got {:?}", ty))
+        };
+        let autoref = Some(AutoPtr(r_borrow, mt_b.mutbl));
+        debug!("coerce_borrowed_pointer: succeeded ty={:?} autoderefs={:?} autoref={:?}",
+               ty, autoderefs, autoref);
+        Ok((ty, AdjustDerefRef(AutoDerefRef {
+            autoderefs: autoderefs,
+            autoref: autoref,
+            unsize: None
+        })))
     }
 
 
@@ -392,14 +511,14 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             match (fn_ty_a.unsafety, fn_ty_b.unsafety) {
                 (hir::Unsafety::Normal, hir::Unsafety::Unsafe) => {
                     let unsafe_a = self.tcx().safe_to_unsafe_fn_ty(fn_ty_a);
-                    return self.unify(unsafe_a, b).map(|(ty, _)| {
+                    return self.unify_and_identity(unsafe_a, b).map(|(ty, _)| {
                         (ty, AdjustUnsafeFnPointer)
                     });
                 }
                 _ => {}
             }
         }
-        self.unify(a, b)
+        self.unify_and_identity(a, b)
     }
 
     fn coerce_from_fn_item(&self,
@@ -418,11 +537,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         match b.sty {
             ty::TyFnPtr(_) => {
                 let a_fn_pointer = self.tcx().mk_ty(ty::TyFnPtr(fn_ty_a));
-                self.unify(a_fn_pointer, b).map(|(ty, _)| {
+                self.unify_and_identity(a_fn_pointer, b).map(|(ty, _)| {
                     (ty, AdjustReifyFnPointer)
                 })
             }
-            _ => self.unify(a, b)
+            _ => self.unify_and_identity(a, b)
         }
     }
 
@@ -439,13 +558,13 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             ty::TyRef(_, mt) => (true, mt),
             ty::TyRawPtr(mt) => (false, mt),
             _ => {
-                return self.unify(a, b);
+                return self.unify_and_identity(a, b);
             }
         };
 
         // Check that the types which they point at are compatible.
         let a_unsafe = self.tcx().mk_ptr(ty::TypeAndMut{ mutbl: mutbl_b, ty: mt_a.ty });
-        let (ty, noop) = try!(self.unify(a_unsafe, b));
+        let (ty, noop) = try!(self.unify_and_identity(a_unsafe, b));
         try!(coerce_mutbls(mt_a.mutbl, mutbl_b));
 
         // Although references and unsafe ptrs have the same
diff --git a/src/test/compile-fail/issue-10291.rs b/src/test/compile-fail/issue-10291.rs
index 9711d760ae6..43255db2ff3 100644
--- a/src/test/compile-fail/issue-10291.rs
+++ b/src/test/compile-fail/issue-10291.rs
@@ -11,8 +11,7 @@
 fn test<'x>(x: &'x isize) {
     // FIXME (#22405): Replace `Box::new` with `box` here when/if possible.
     drop::<Box<for<'z> FnMut(&'z isize) -> &'z isize>>(Box::new(|z| {
-        x
-        //~^ ERROR cannot infer an appropriate lifetime
+        x //~ ERROR E0312
     }));
 }
 
diff --git a/src/test/compile-fail/issue-13058.rs b/src/test/compile-fail/issue-13058.rs
index 503ccbd1cab..b552d7678d5 100644
--- a/src/test/compile-fail/issue-13058.rs
+++ b/src/test/compile-fail/issue-13058.rs
@@ -36,9 +36,5 @@ fn check<'r, I: Iterator<Item=usize>, T: Itble<'r, usize, I>>(cont: &T) -> bool
 fn main() {
     check((3, 5));
 //~^ ERROR mismatched types
-//~| expected `&_`
-//~| found `(_, _)`
-//~| expected &-ptr
-//~| found tuple
 //~| HELP run `rustc --explain E0308` to see a detailed explanation
 }
diff --git a/src/test/compile-fail/issue-7573.rs b/src/test/compile-fail/issue-7573.rs
index 2d1cea1d44b..d13da1d9fd9 100644
--- a/src/test/compile-fail/issue-7573.rs
+++ b/src/test/compile-fail/issue-7573.rs
@@ -24,11 +24,9 @@ impl CrateId {
 }
 
 pub fn remove_package_from_database() {
-    let mut lines_to_use: Vec<&CrateId> = Vec::new();
+    let mut lines_to_use: Vec<&CrateId> = Vec::new(); //~ ERROR E0495
     let push_id = |installed_id: &CrateId| {
         lines_to_use.push(installed_id);
-        //~^ ERROR cannot infer an appropriate lifetime for automatic coercion due to
-        // conflicting requirements
     };
     list_database(push_id);
 
diff --git a/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param-3.rs b/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param-3.rs
index 66d8927ee51..e0ea1ed7434 100644
--- a/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param-3.rs
+++ b/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param-3.rs
@@ -14,19 +14,19 @@ use std::marker::PhantomData;
 
 struct Bar<'x, 'y, 'z> { bar: &'y i32, baz: i32, marker: PhantomData<(&'x(),&'y(),&'z())> }
 fn bar1<'a>(x: &Bar) -> (&'a i32, &'a i32, &'a i32) {
-//~^ HELP: consider using an explicit lifetime parameter as shown: fn bar1<'b, 'c, 'a>(x: &'a Bar<'b, 'a, 'c>) -> (&'a i32, &'a i32, &'a i32)
+    //~^ HELP: consider using an explicit lifetime parameter as shown: fn bar1<'a>(x: &'a Bar) -> (&'a i32, &'a i32, &'a i32)
     (x.bar, &x.baz, &x.baz)
-    //~^ ERROR: cannot infer
-    //~^^ ERROR: cannot infer
-    //~^^^ ERROR: cannot infer
+    //~^ ERROR E0312
+    //~| ERROR cannot infer
+    //~| ERROR cannot infer
 }
 
 fn bar2<'a, 'b, 'c>(x: &Bar<'a, 'b, 'c>) -> (&'a i32, &'a i32, &'a i32) {
-//~^ HELP: consider using an explicit lifetime parameter as shown: fn bar2<'a, 'c>(x: &'a Bar<'a, 'a, 'c>) -> (&'a i32, &'a i32, &'a i32)
+    //~^ HELP: consider using an explicit lifetime parameter as shown: fn bar2<'a, 'c>(x: &'a Bar<'a, 'a, 'c>) -> (&'a i32, &'a i32, &'a i32)
     (x.bar, &x.baz, &x.baz)
-    //~^ ERROR: cannot infer
-    //~^^ ERROR: cannot infer
-    //~^^^ ERROR: cannot infer
+    //~^ ERROR E0312
+    //~| ERROR cannot infer
+    //~| ERROR cannot infer
 }
 
 fn main() { }
diff --git a/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs b/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs
index e32ed1c42a0..73d89beb220 100644
--- a/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs
+++ b/src/test/compile-fail/lifetime-inference-give-expl-lifetime-param.rs
@@ -39,8 +39,8 @@ struct Cat<'x, T> { cat: &'x isize, t: T }
 struct Dog<'y> { dog: &'y isize }
 
 fn cat2<'x, 'y>(x: Cat<'x, Dog<'y>>) -> &'x isize {
-//~^ HELP: consider using an explicit lifetime parameter as shown: fn cat2<'x>(x: Cat<'x, Dog<'x>>) -> &'x isize
-    x.t.dog //~ ERROR: cannot infer
+    //~^ HELP consider using an explicit lifetime parameter as shown: fn cat2<'x>(x: Cat<'x, Dog<'x>>) -> &'x isize
+    x.t.dog //~ ERROR E0312
 }
 
 struct Baz<'x> {
@@ -49,11 +49,9 @@ struct Baz<'x> {
 
 impl<'a> Baz<'a> {
     fn baz2<'b>(&self, x: &isize) -> (&'b isize, &'b isize) {
-         //~^ HELP: parameter as shown: fn baz2<'b>(&self, x: &'b isize) -> (&'a isize, &'a isize)
-        // The lifetime that gets assigned to `x` seems somewhat random.
-        // I have disabled this test for the time being. --pcwalton
-        (self.bar, x) //~ ERROR: cannot infer
-        //~^ ERROR: cannot infer
+        //~^ HELP consider using an explicit lifetime parameter as shown: fn baz2<'b>(&self, x: &'b isize) -> (&'a isize, &'a isize)
+        (self.bar, x) //~ ERROR E0312
+        //~^ ERROR E0312
     }
 }
 
diff --git a/src/test/compile-fail/lub-if.rs b/src/test/compile-fail/lub-if.rs
index 06af8ac8719..8d2a0fd07e8 100644
--- a/src/test/compile-fail/lub-if.rs
+++ b/src/test/compile-fail/lub-if.rs
@@ -35,14 +35,14 @@ pub fn opt_str2<'a>(maybestr: &'a Option<String>) -> &'static str {
         "(none)"
     } else {
         let s: &'a str = maybestr.as_ref().unwrap();
-        s  //~ ERROR cannot infer an appropriate lifetime for automatic coercion due to conflicting
+        s  //~ ERROR E0312
     }
 }
 
 pub fn opt_str3<'a>(maybestr: &'a Option<String>) -> &'static str {
     if maybestr.is_some() {
         let s: &'a str = maybestr.as_ref().unwrap();
-        s  //~ ERROR cannot infer an appropriate lifetime for automatic coercion due to conflicting
+        s  //~ ERROR E0312
     } else {
         "(none)"
     }
diff --git a/src/test/compile-fail/lub-match.rs b/src/test/compile-fail/lub-match.rs
index 1b5824964a8..b9423feb5c1 100644
--- a/src/test/compile-fail/lub-match.rs
+++ b/src/test/compile-fail/lub-match.rs
@@ -37,8 +37,7 @@ pub fn opt_str2<'a>(maybestr: &'a Option<String>) -> &'static str {
         None => "(none)",
         Some(ref s) => {
             let s: &'a str = s;
-            s
-            //~^ ERROR cannot infer an appropriate lifetime
+            s //~ ERROR E0312
         }
     }
 }
@@ -47,8 +46,7 @@ pub fn opt_str3<'a>(maybestr: &'a Option<String>) -> &'static str {
     match *maybestr {
         Some(ref s) => {
             let s: &'a str = s;
-            s
-            //~^ ERROR cannot infer an appropriate lifetime
+            s //~ ERROR E0312
         }
         None => "(none)",
     }
diff --git a/src/test/compile-fail/object-lifetime-default-mybox.rs b/src/test/compile-fail/object-lifetime-default-mybox.rs
index 80dbee3c481..014b0c1e80e 100644
--- a/src/test/compile-fail/object-lifetime-default-mybox.rs
+++ b/src/test/compile-fail/object-lifetime-default-mybox.rs
@@ -34,8 +34,7 @@ fn load1<'a,'b>(a: &'a MyBox<SomeTrait>,
                 b: &'b MyBox<SomeTrait>)
                 -> &'b MyBox<SomeTrait>
 {
-    a
-      //~^ ERROR cannot infer
+    a //~ ERROR E0312
 }
 
 fn load2<'a>(ss: &MyBox<SomeTrait+'a>) -> MyBox<SomeTrait+'a> {
diff --git a/src/test/compile-fail/region-lifetime-bounds-on-fns-where-clause.rs b/src/test/compile-fail/region-lifetime-bounds-on-fns-where-clause.rs
index ee05ba676ac..6364db1f4b4 100644
--- a/src/test/compile-fail/region-lifetime-bounds-on-fns-where-clause.rs
+++ b/src/test/compile-fail/region-lifetime-bounds-on-fns-where-clause.rs
@@ -15,7 +15,7 @@ fn a<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) where 'b: 'a {
 
 fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
     // Illegal now because there is no `'b:'a` declaration.
-    *x = *y; //~ ERROR cannot infer
+    *x = *y; //~ ERROR E0312
 }
 
 fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
diff --git a/src/test/compile-fail/region-multiple-lifetime-bounds-on-fns-where-clause.rs b/src/test/compile-fail/region-multiple-lifetime-bounds-on-fns-where-clause.rs
index 30e6a4e1277..154135eba38 100644
--- a/src/test/compile-fail/region-multiple-lifetime-bounds-on-fns-where-clause.rs
+++ b/src/test/compile-fail/region-multiple-lifetime-bounds-on-fns-where-clause.rs
@@ -16,8 +16,8 @@ fn a<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) where
 
 fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
     // Illegal now because there is no `'b:'a` declaration.
-    *x = *y; //~ ERROR cannot infer
-    *z = *y; //~ ERROR cannot infer
+    *x = *y; //~ ERROR E0312
+    *z = *y; //~ ERROR E0312
 }
 
 fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
diff --git a/src/test/compile-fail/regions-early-bound-error-method.rs b/src/test/compile-fail/regions-early-bound-error-method.rs
index 4a3ca01c849..8cc35272282 100644
--- a/src/test/compile-fail/regions-early-bound-error-method.rs
+++ b/src/test/compile-fail/regions-early-bound-error-method.rs
@@ -28,8 +28,7 @@ impl<'a> GetRef<'a> for Box<'a> {
 impl<'a> Box<'a> {
     fn or<'b,G:GetRef<'b>>(&self, g2: G) -> &'a isize {
         g2.get()
-        //~^ ERROR cannot infer an appropriate lifetime for automatic coercion due to
-        //~| ERROR mismatched types
+        //~^ ERROR mismatched types
         //~| expected `&'a isize`
         //~| found `&'b isize`
         //~| lifetime mismatch
diff --git a/src/test/compile-fail/regions-early-bound-error.rs b/src/test/compile-fail/regions-early-bound-error.rs
index 57c8e3f1170..1fc3b4b3c6a 100644
--- a/src/test/compile-fail/regions-early-bound-error.rs
+++ b/src/test/compile-fail/regions-early-bound-error.rs
@@ -27,11 +27,7 @@ impl<'a,T:Clone> GetRef<'a,T> for Box<'a,T> {
 
 fn get<'a,'b,G:GetRef<'a, isize>>(g1: G, b: &'b isize) -> &'b isize {
     g1.get()
-    //~^ ERROR cannot infer an appropriate lifetime for automatic coercion due to
-    //~| ERROR mismatched types
-    //~| expected `&'b isize`
-    //~| found `&'a isize`
-    //~| lifetime mismatch
+    //~^ ERROR mismatched types
 }
 
 fn main() {
diff --git a/src/test/compile-fail/regions-glb-free-free.rs b/src/test/compile-fail/regions-glb-free-free.rs
index 323d5360029..c2e4fbac3c9 100644
--- a/src/test/compile-fail/regions-glb-free-free.rs
+++ b/src/test/compile-fail/regions-glb-free-free.rs
@@ -22,9 +22,9 @@ mod argparse {
 
     impl<'a> Flag<'a> {
         pub fn set_desc(self, s: &str) -> Flag<'a> {
-            Flag {
+            Flag { //~ ERROR cannot infer
                 name: self.name,
-                desc: s, //~ ERROR cannot infer an appropriate lifetime for automatic coercion due t
+                desc: s,
                 max_count: self.max_count,
                 value: self.value
             }
diff --git a/src/test/compile-fail/regions-lifetime-bounds-on-fns.rs b/src/test/compile-fail/regions-lifetime-bounds-on-fns.rs
index 43940d499d2..89254516ac6 100644
--- a/src/test/compile-fail/regions-lifetime-bounds-on-fns.rs
+++ b/src/test/compile-fail/regions-lifetime-bounds-on-fns.rs
@@ -15,13 +15,13 @@ fn a<'a, 'b:'a>(x: &mut &'a isize, y: &mut &'b isize) {
 
 fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
     // Illegal now because there is no `'b:'a` declaration.
-    *x = *y; //~ ERROR cannot infer
+    *x = *y; //~ ERROR E0312
 }
 
 fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
     // Here we try to call `foo` but do not know that `'a` and `'b` are
     // related as required.
-    a(x, y); //~ ERROR cannot infer
+    a(x, y); //~ ERROR E0495
 }
 
 fn d() {
diff --git a/src/test/compile-fail/regions-nested-fns.rs b/src/test/compile-fail/regions-nested-fns.rs
index f114a8bc7ce..5ef2a701a60 100644
--- a/src/test/compile-fail/regions-nested-fns.rs
+++ b/src/test/compile-fail/regions-nested-fns.rs
@@ -14,16 +14,16 @@ fn ignore<T>(t: T) {}
 
 fn nested<'x>(x: &'x isize) {
     let y = 3;
-    let mut ay = &y;
+    let mut ay = &y; //~ ERROR E0495
 
     ignore::<Box<for<'z> FnMut(&'z isize)>>(Box::new(|z| {
-        ay = x; //~ ERROR cannot infer
+        ay = x;
         ay = &y;
         ay = z;
     }));
 
     ignore::< Box<for<'z> FnMut(&'z isize) -> &'z isize>>(Box::new(|z| {
-        if false { return x; }  //~ ERROR cannot infer an appropriate lifetime for automatic
+        if false { return x; } //~ ERROR E0312
         if false { return ay; }
         return z;
     }));
diff --git a/src/test/compile-fail/regions-static-bound.rs b/src/test/compile-fail/regions-static-bound.rs
index 297b6a866da..de695e72d07 100644
--- a/src/test/compile-fail/regions-static-bound.rs
+++ b/src/test/compile-fail/regions-static-bound.rs
@@ -13,7 +13,7 @@ fn static_id<'a,'b>(t: &'a ()) -> &'static ()
 fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static ()
     where 'a: 'b, 'b: 'static { t }
 fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a {
-    t //~ ERROR cannot infer an appropriate lifetime
+    t //~ ERROR E0312
 }
 
 fn error(u: &(), v: &()) {
diff --git a/src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.rs b/src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.rs
index 3dd5779914d..1e2b01856e7 100644
--- a/src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.rs
+++ b/src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.rs
@@ -24,6 +24,6 @@ fn doit<T,F>(val: T, f: &F)
 
 pub fn main() {
     doit(0, &|x, y| {
-        x.set(y); //~ ERROR cannot infer
+        x.set(y); //~ ERROR E0312
     });
 }
diff --git a/src/test/compile-fail/wf-static-method.rs b/src/test/compile-fail/wf-static-method.rs
index 6c6522fe658..e99957c7914 100644
--- a/src/test/compile-fail/wf-static-method.rs
+++ b/src/test/compile-fail/wf-static-method.rs
@@ -24,7 +24,7 @@ struct Evil<'a, 'b: 'a>(Option<&'a &'b ()>);
 impl<'a, 'b> Foo<'a, 'b, Evil<'a, 'b>> for () {
     fn make_me() -> Self { }
     fn static_evil(u: &'b u32) -> &'a u32 {
-        u //~ ERROR cannot infer an appropriate lifetime
+        u //~ ERROR E0312
     }
 }
 
@@ -40,7 +40,7 @@ impl<'a, 'b> Foo<'a, 'b, ()> for IndirectEvil<'a, 'b> {
 
 impl<'a, 'b> Evil<'a, 'b> {
     fn inherent_evil(u: &'b u32) -> &'a u32 {
-        u //~ ERROR cannot infer an appropriate lifetime
+        u //~ ERROR E0312
     }
 }
 
diff --git a/src/test/run-pass/regions-lub-ref-ref-rc.rs b/src/test/run-pass/regions-lub-ref-ref-rc.rs
new file mode 100644
index 00000000000..41c64197acb
--- /dev/null
+++ b/src/test/run-pass/regions-lub-ref-ref-rc.rs
@@ -0,0 +1,36 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test a corner case of LUB coercion. In this case, one arm of the
+// match requires a deref coercion and other other doesn't, and there
+// is an extra `&` on the `rc`. We want to be sure that the lifetime
+// assigned to this `&rc` value is not `'a` but something smaller.  In
+// other words, the type from `rc` is `&'a Rc<String>` and the type
+// from `&rc` should be `&'x &'a Rc<String>`, where `'x` is something
+// small.
+
+use std::rc::Rc;
+
+#[derive(Clone)]
+enum CachedMir<'mir> {
+    Ref(&'mir String),
+    Owned(Rc<String>),
+}
+
+impl<'mir> CachedMir<'mir> {
+    fn get_ref<'a>(&'a self) -> &'a String {
+        match *self {
+            CachedMir::Ref(r) => r,
+            CachedMir::Owned(ref rc) => &rc,
+        }
+    }
+}
+
+fn main() { }