about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/infer/error_reporting.rs245
-rw-r--r--src/librustc/infer/region_inference/mod.rs36
-rw-r--r--src/test/compile-fail/issue-17728.rs5
-rw-r--r--src/test/run-pass/const-enum-vec-index.rs2
-rw-r--r--src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.rs15
-rw-r--r--src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr25
-rw-r--r--src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.rs15
-rw-r--r--src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.stderr10
-rw-r--r--src/test/ui/lifetime-errors/ex2a-push-one-existing-name.rs19
-rw-r--r--src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr27
-rw-r--r--src/test/ui/lifetime-errors/ex2b-push-no-existing-names.rs19
-rw-r--r--src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr27
-rw-r--r--src/test/ui/lifetime-errors/ex2c-push-inference-variable.rs20
-rw-r--r--src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr37
-rw-r--r--src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.rs21
-rw-r--r--src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.stderr39
-rw-r--r--src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.rs21
-rw-r--r--src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.stderr39
18 files changed, 362 insertions, 260 deletions
diff --git a/src/librustc/infer/error_reporting.rs b/src/librustc/infer/error_reporting.rs
index 939f214407e..9295fb2ee32 100644
--- a/src/librustc/infer/error_reporting.rs
+++ b/src/librustc/infer/error_reporting.rs
@@ -65,9 +65,6 @@ use super::region_inference::ConcreteFailure;
 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 hir::map as hir_map;
 use hir;
@@ -77,11 +74,10 @@ use infer;
 use middle::region;
 use traits::{ObligationCause, ObligationCauseCode};
 use ty::{self, TyCtxt, TypeFoldable};
-use ty::{Region, ReFree, Issue32330};
+use ty::{Region, Issue32330};
 use ty::error::TypeError;
 
 use std::fmt;
-use syntax::ast;
 use syntax_pos::{Pos, Span};
 use errors::DiagnosticBuilder;
 
@@ -255,8 +251,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
         // 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);
+        let errors = self.process_errors(errors);
 
         debug!("report_region_errors: {} errors after preprocessing", errors.len());
 
@@ -278,13 +273,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                                                  sub_origin, sub_r,
                                                  sup_origin, sup_r);
                 }
-
-                ProcessedErrors(ref origins,
-                                ref same_regions) => {
-                    if !same_regions.is_empty() {
-                        self.report_processed_errors(origins);
-                    }
-                }
             }
         }
     }
@@ -300,202 +288,31 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     // 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>>)
-                      -> Option<Vec<RegionResolutionError<'tcx>>> {
+                      -> Vec<RegionResolutionError<'tcx>> {
         debug!("process_errors()");
-        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();
-
-        // 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 {
-            // Check whether we can process this error into some other
-            // form; if not, fall through.
-            match *error {
-                ConcreteFailure(ref origin, sub, sup) => {
-                    debug!("processing ConcreteFailure");
-                    if let SubregionOrigin::CompareImplMethodObligation { .. } = *origin {
-                        // When comparing an impl method against a
-                        // trait method, it is not helpful to suggest
-                        // changes to the impl method.  This is
-                        // because the impl method signature is being
-                        // checked using the trait's environment, so
-                        // usually the changes we suggest would
-                        // actually have to be applied to the *trait*
-                        // method (and it's not clear that the trait
-                        // method is even under the user's control).
-                    } else if let Some(same_frs) = free_regions_from_same_fn(self.tcx, sub, sup) {
-                        origins.push(
-                            ProcessedErrorOrigin::ConcreteFailure(
-                                origin.clone(),
-                                sub,
-                                sup));
-                        append_to_same_regions(&mut same_regions, &same_frs);
-                        continue;
-                    }
-                }
-                SubSupConflict(ref var_origin, ref sub_origin, sub, ref sup_origin, sup) => {
-                    debug!("processing SubSupConflict sub: {:?} sup: {:?}", sub, sup);
-                    match (sub_origin, sup_origin) {
-                        (&SubregionOrigin::CompareImplMethodObligation { .. }, _) => {
-                            // As above, when comparing an impl method
-                            // against a trait method, it is not helpful
-                            // to suggest changes to the impl method.
-                        }
-                        (_, &SubregionOrigin::CompareImplMethodObligation { .. }) => {
-                            // See above.
-                        }
-                        _ => {
-                            if let Some(same_frs) = free_regions_from_same_fn(self.tcx, sub, sup) {
-                                origins.push(
-                                    ProcessedErrorOrigin::VariableFailure(
-                                        var_origin.clone()));
-                                append_to_same_regions(&mut same_regions, &same_frs);
-                                continue;
-                            }
-                        }
-                    }
-                }
-                GenericBoundFailure(ref origin, ref kind, region) => {
-                    bound_failures.push((origin.clone(), kind.clone(), region));
-                    continue;
-                }
-                ProcessedErrors(..) => {
-                    bug!("should not encounter a `ProcessedErrors` yet: {:?}", error)
-                }
-            }
-
-            // No changes to this error.
-            other_errors.push(error.clone());
-        }
-
-        // 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 {
-                // Since ProcessedErrors is used to reconstruct the function
-                // declaration, we want to make sure that they are, in fact,
-                // from the same scope
-                if sr.scope_id != common_scope_id {
-                    debug!("returning empty result from process_errors because
-                            {} != {}", sr.scope_id, common_scope_id);
-                    return None;
-                }
-            }
-            assert!(origins.len() > 0);
-            let pe = ProcessedErrors(origins, same_regions);
-            debug!("errors processed: {:?}", pe);
-            processed_errors.push(pe);
-        }
-
-        // 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,
-            scope_id: ast::NodeId
-        }
-
-        impl FreeRegionsFromSameFn {
-            fn new(sub_fr: ty::FreeRegion,
-                   sup_fr: ty::FreeRegion,
-                   scope_id: ast::NodeId)
-                   -> FreeRegionsFromSameFn {
-                FreeRegionsFromSameFn {
-                    sub_fr: sub_fr,
-                    sup_fr: sup_fr,
-                    scope_id: scope_id
-                }
-            }
-        }
-
-        fn free_regions_from_same_fn<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
-                                                     sub: &'tcx Region,
-                                                     sup: &'tcx Region)
-                                                     -> Option<FreeRegionsFromSameFn> {
-            debug!("free_regions_from_same_fn(sub={:?}, sup={:?})", sub, sup);
-            let (scope_id, fr1, fr2) = match (sub, sup) {
-                (&ReFree(fr1), &ReFree(fr2)) => {
-                    if fr1.scope != fr2.scope {
-                        return None
-                    }
-                    assert!(fr1.scope == fr2.scope);
-                    (fr1.scope.node_id(&tcx.region_maps), fr1, fr2)
-                },
-                _ => return None
-            };
-            let parent = tcx.hir.get_parent(scope_id);
-            let parent_node = tcx.hir.find(parent);
-            match parent_node {
-                Some(node) => match node {
-                    hir_map::NodeItem(item) => match item.node {
-                        hir::ItemFn(..) => {
-                            Some(FreeRegionsFromSameFn::new(fr1, fr2, scope_id))
-                        },
-                        _ => None
-                    },
-                    hir_map::NodeImplItem(..) |
-                    hir_map::NodeTraitItem(..) => {
-                        Some(FreeRegionsFromSameFn::new(fr1, fr2, scope_id))
-                    },
-                    _ => None
-                },
-                None => {
-                    debug!("no parent node of scope_id {}", scope_id);
-                    None
-                }
-            }
-        }
+        // We want to avoid reporting generic-bound failures if we can
+        // avoid it: 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.
+        //
+        // Therefore, we filter them out of the list unless they are
+        // the only thing in the list.
+
+        let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e {
+            ConcreteFailure(..) => false,
+            SubSupConflict(..) => false,
+            GenericBoundFailure(..) => true,
+        };
 
-        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 same_regions.iter_mut() {
-                if sr.contains(&sup_fr.bound_region) && scope_id == sr.scope_id {
-                    sr.push(sub_fr.bound_region);
-                    return
-                }
-            }
-            same_regions.push(SameRegions {
-                scope_id: scope_id,
-                regions: vec![sub_fr.bound_region, sup_fr.bound_region]
-            })
+        if errors.iter().all(|e| is_bound_failure(e)) {
+            errors.clone()
+        } else {
+            errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect()
         }
     }
 
@@ -1072,20 +889,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         self.note_region_origin(&mut err, &sub_origin);
         err.emit();
     }
-
-    fn report_processed_errors(&self,
-                               origins: &[ProcessedErrorOrigin<'tcx>]) {
-        for origin in origins.iter() {
-            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),
-            };
-
-            err.emit();
-        }
-    }
 }
 
 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
diff --git a/src/librustc/infer/region_inference/mod.rs b/src/librustc/infer/region_inference/mod.rs
index af6f2c50e72..0bb9e2c7fa1 100644
--- a/src/librustc/infer/region_inference/mod.rs
+++ b/src/librustc/infer/region_inference/mod.rs
@@ -24,7 +24,7 @@ use rustc_data_structures::graph::{self, Direction, NodeIndex, OUTGOING};
 use rustc_data_structures::unify::{self, UnificationTable};
 use middle::free_region::FreeRegionMap;
 use ty::{self, Ty, TyCtxt};
-use ty::{BoundRegion, Region, RegionVid};
+use ty::{Region, RegionVid};
 use ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound, ReErased};
 use ty::{ReLateBound, ReScope, ReVar, ReSkolemized, BrFresh};
 
@@ -171,13 +171,6 @@ pub enum RegionResolutionError<'tcx> {
                    &'tcx Region,
                    SubregionOrigin<'tcx>,
                    &'tcx Region),
-
-    /// For subsets of `ConcreteFailure` and `SubSupConflict`, we can derive
-    /// 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<ProcessedErrorOrigin<'tcx>>,
-                    Vec<SameRegions>),
 }
 
 #[derive(Clone, Debug)]
@@ -186,33 +179,6 @@ pub enum ProcessedErrorOrigin<'tcx> {
     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
-/// ```
-/// struct Foo { bar: i32 }
-/// fn foo2<'a, 'b>(x: &'a Foo) -> &'b i32 {
-///    &x.bar
-/// }
-/// ```
-/// would report an error because we expect 'a and 'b to match, and so we group
-/// 'a and 'b together inside a SameRegions struct
-#[derive(Clone, Debug)]
-pub struct SameRegions {
-    pub scope_id: ast::NodeId,
-    pub regions: Vec<BoundRegion>,
-}
-
-impl SameRegions {
-    pub fn contains(&self, other: &BoundRegion) -> bool {
-        self.regions.contains(other)
-    }
-
-    pub fn push(&mut self, other: BoundRegion) {
-        self.regions.push(other);
-    }
-}
-
 pub type CombineMap<'tcx> = FxHashMap<TwoRegions<'tcx>, RegionVid>;
 
 pub struct RegionVarBindings<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
diff --git a/src/test/compile-fail/issue-17728.rs b/src/test/compile-fail/issue-17728.rs
index f508d7123d8..9724d17bef1 100644
--- a/src/test/compile-fail/issue-17728.rs
+++ b/src/test/compile-fail/issue-17728.rs
@@ -108,9 +108,6 @@ impl Debug for Player {
 
 fn str_to_direction(to_parse: &str) -> RoomDirection {
     match to_parse { //~ ERROR match arms have incompatible types
-    //~^ expected enum `RoomDirection`, found enum `std::option::Option`
-    //~| expected type `RoomDirection`
-    //~| found type `std::option::Option<_>`
         "w" | "west" => RoomDirection::West,
         "e" | "east" => RoomDirection::East,
         "n" | "north" => RoomDirection::North,
@@ -119,7 +116,7 @@ fn str_to_direction(to_parse: &str) -> RoomDirection {
         "out" => RoomDirection::Out,
         "up" => RoomDirection::Up,
         "down" => RoomDirection::Down,
-        _ => None //~ NOTE match arm with an incompatible type
+        _ => None
     }
 }
 
diff --git a/src/test/run-pass/const-enum-vec-index.rs b/src/test/run-pass/const-enum-vec-index.rs
index 4af6a6d10d5..9c1a4dbdffa 100644
--- a/src/test/run-pass/const-enum-vec-index.rs
+++ b/src/test/run-pass/const-enum-vec-index.rs
@@ -8,7 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[derive(Copy, Clone)]
 enum E { V1(isize), V0 }
+
 const C: &'static [E] = &[E::V0, E::V1(0xDEADBEE)];
 static C0: E = C[0];
 static C1: E = C[1];
diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.rs
new file mode 100644
index 00000000000..30239f4c094
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.rs
@@ -0,0 +1,15 @@
+// Copyright 2016 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.
+
+fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 {
+    if x > y { x } else { y }
+}
+
+fn main() { }
diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr
new file mode 100644
index 00000000000..85e05422ab3
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr
@@ -0,0 +1,25 @@
+error[E0312]: lifetime of reference outlives lifetime of borrowed content...
+  --> $DIR/ex1-return-one-existing-name-if-else.rs:12:27
+   |
+12 |     if x > y { x } else { y }
+   |                           ^
+   |
+note: ...the reference is valid for the lifetime 'a as defined on the body at 11:43...
+  --> $DIR/ex1-return-one-existing-name-if-else.rs:11:44
+   |
+11 |   fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 {
+   |  ____________________________________________^ starting here...
+12 | |     if x > y { x } else { y }
+13 | | }
+   | |_^ ...ending here
+note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the body at 11:43
+  --> $DIR/ex1-return-one-existing-name-if-else.rs:11:44
+   |
+11 |   fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 {
+   |  ____________________________________________^ starting here...
+12 | |     if x > y { x } else { y }
+13 | | }
+   | |_^ ...ending here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.rs b/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.rs
new file mode 100644
index 00000000000..098950e13b3
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.rs
@@ -0,0 +1,15 @@
+// Copyright 2016 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.
+
+fn foo(x: &i32, y: &i32) -> &i32 {
+    if x > y { x } else { y }
+}
+
+fn main() { }
diff --git a/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.stderr b/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.stderr
new file mode 100644
index 00000000000..fccc44caac8
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex1b-return-no-names-if-else.stderr
@@ -0,0 +1,10 @@
+error[E0106]: missing lifetime specifier
+  --> $DIR/ex1b-return-no-names-if-else.rs:11:29
+   |
+11 | fn foo(x: &i32, y: &i32) -> &i32 {
+   |                             ^ expected lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.rs b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.rs
new file mode 100644
index 00000000000..71a1c865e09
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.rs
@@ -0,0 +1,19 @@
+// Copyright 2016 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.
+
+struct Ref<'a, T: 'a> {
+    data: &'a T
+}
+
+fn foo<'a>(x: &mut Vec<Ref<'a, i32>>, y: Ref<i32>) {
+    x.push(y);
+}
+
+fn main() { }
diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr
new file mode 100644
index 00000000000..6f42a9f679a
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr
@@ -0,0 +1,27 @@
+error[E0308]: mismatched types
+  --> $DIR/ex2a-push-one-existing-name.rs:16:12
+   |
+16 |     x.push(y);
+   |            ^ lifetime mismatch
+   |
+   = note: expected type `Ref<'a, i32>`
+              found type `Ref<'_, i32>`
+note: the anonymous lifetime #2 defined on the body at 15:51...
+  --> $DIR/ex2a-push-one-existing-name.rs:15:52
+   |
+15 |   fn foo<'a>(x: &mut Vec<Ref<'a, i32>>, y: Ref<i32>) {
+   |  ____________________________________________________^ starting here...
+16 | |     x.push(y);
+17 | | }
+   | |_^ ...ending here
+note: ...does not necessarily outlive the lifetime 'a as defined on the body at 15:51
+  --> $DIR/ex2a-push-one-existing-name.rs:15:52
+   |
+15 |   fn foo<'a>(x: &mut Vec<Ref<'a, i32>>, y: Ref<i32>) {
+   |  ____________________________________________________^ starting here...
+16 | |     x.push(y);
+17 | | }
+   | |_^ ...ending here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.rs b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.rs
new file mode 100644
index 00000000000..09038d8ce90
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.rs
@@ -0,0 +1,19 @@
+// Copyright 2016 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.
+
+struct Ref<'a, T: 'a> {
+    data: &'a T
+}
+
+fn foo(x: &mut Vec<Ref<i32>>, y: Ref<i32>) {
+    x.push(y);
+}
+
+fn main() { }
diff --git a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr
new file mode 100644
index 00000000000..edc1c2362de
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr
@@ -0,0 +1,27 @@
+error[E0308]: mismatched types
+  --> $DIR/ex2b-push-no-existing-names.rs:16:12
+   |
+16 |     x.push(y);
+   |            ^ lifetime mismatch
+   |
+   = note: expected type `Ref<'_, i32>`
+              found type `Ref<'_, i32>`
+note: the anonymous lifetime #3 defined on the body at 15:43...
+  --> $DIR/ex2b-push-no-existing-names.rs:15:44
+   |
+15 |   fn foo(x: &mut Vec<Ref<i32>>, y: Ref<i32>) {
+   |  ____________________________________________^ starting here...
+16 | |     x.push(y);
+17 | | }
+   | |_^ ...ending here
+note: ...does not necessarily outlive the anonymous lifetime #2 defined on the body at 15:43
+  --> $DIR/ex2b-push-no-existing-names.rs:15:44
+   |
+15 |   fn foo(x: &mut Vec<Ref<i32>>, y: Ref<i32>) {
+   |  ____________________________________________^ starting here...
+16 | |     x.push(y);
+17 | | }
+   | |_^ ...ending here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.rs b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.rs
new file mode 100644
index 00000000000..cb083f778de
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.rs
@@ -0,0 +1,20 @@
+// Copyright 2016 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.
+
+struct Ref<'a, T: 'a> {
+    data: &'a T
+}
+
+fn foo<'a, 'b, 'c>(x: &'a mut Vec<Ref<'b, i32>>, y: Ref<'c, i32>) {
+    let z = Ref { data: y.data };
+    x.push(z);
+}
+
+fn main() { }
diff --git a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr
new file mode 100644
index 00000000000..755b71d4a1d
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr
@@ -0,0 +1,37 @@
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
+  --> $DIR/ex2c-push-inference-variable.rs:16:13
+   |
+16 |     let z = Ref { data: y.data };
+   |             ^^^
+   |
+note: first, the lifetime cannot outlive the lifetime 'c as defined on the body at 15:66...
+  --> $DIR/ex2c-push-inference-variable.rs:15:67
+   |
+15 |   fn foo<'a, 'b, 'c>(x: &'a mut Vec<Ref<'b, i32>>, y: Ref<'c, i32>) {
+   |  ___________________________________________________________________^ starting here...
+16 | |     let z = Ref { data: y.data };
+17 | |     x.push(z);
+18 | | }
+   | |_^ ...ending here
+note: ...so that reference does not outlive borrowed content
+  --> $DIR/ex2c-push-inference-variable.rs:16:25
+   |
+16 |     let z = Ref { data: y.data };
+   |                         ^^^^^^
+note: but, the lifetime must be valid for the lifetime 'b as defined on the body at 15:66...
+  --> $DIR/ex2c-push-inference-variable.rs:15:67
+   |
+15 |   fn foo<'a, 'b, 'c>(x: &'a mut Vec<Ref<'b, i32>>, y: Ref<'c, i32>) {
+   |  ___________________________________________________________________^ starting here...
+16 | |     let z = Ref { data: y.data };
+17 | |     x.push(z);
+18 | | }
+   | |_^ ...ending here
+note: ...so that expression is assignable (expected Ref<'b, i32>, found Ref<'_, i32>)
+  --> $DIR/ex2c-push-inference-variable.rs:17:12
+   |
+17 |     x.push(z);
+   |            ^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.rs b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.rs
new file mode 100644
index 00000000000..bcb7583beef
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.rs
@@ -0,0 +1,21 @@
+// Copyright 2016 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.
+
+struct Ref<'a, T: 'a> {
+    data: &'a T
+}
+
+fn foo<'a, 'b, 'c>(x: &'a mut Vec<Ref<'b, i32>>, y: Ref<'c, i32>) {
+    let a: &mut Vec<Ref<i32>> = x;
+    let b = Ref { data: y.data };
+    a.push(b);
+}
+
+fn main() { }
diff --git a/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.stderr b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.stderr
new file mode 100644
index 00000000000..daa6ea2d91a
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex2d-push-inference-variable-2.stderr
@@ -0,0 +1,39 @@
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
+  --> $DIR/ex2d-push-inference-variable-2.rs:17:13
+   |
+17 |     let b = Ref { data: y.data };
+   |             ^^^
+   |
+note: first, the lifetime cannot outlive the lifetime 'c as defined on the body at 15:66...
+  --> $DIR/ex2d-push-inference-variable-2.rs:15:67
+   |
+15 |   fn foo<'a, 'b, 'c>(x: &'a mut Vec<Ref<'b, i32>>, y: Ref<'c, i32>) {
+   |  ___________________________________________________________________^ starting here...
+16 | |     let a: &mut Vec<Ref<i32>> = x;
+17 | |     let b = Ref { data: y.data };
+18 | |     a.push(b);
+19 | | }
+   | |_^ ...ending here
+note: ...so that reference does not outlive borrowed content
+  --> $DIR/ex2d-push-inference-variable-2.rs:17:25
+   |
+17 |     let b = Ref { data: y.data };
+   |                         ^^^^^^
+note: but, the lifetime must be valid for the lifetime 'b as defined on the body at 15:66...
+  --> $DIR/ex2d-push-inference-variable-2.rs:15:67
+   |
+15 |   fn foo<'a, 'b, 'c>(x: &'a mut Vec<Ref<'b, i32>>, y: Ref<'c, i32>) {
+   |  ___________________________________________________________________^ starting here...
+16 | |     let a: &mut Vec<Ref<i32>> = x;
+17 | |     let b = Ref { data: y.data };
+18 | |     a.push(b);
+19 | | }
+   | |_^ ...ending here
+note: ...so that expression is assignable (expected &mut std::vec::Vec<Ref<'_, i32>>, found &mut std::vec::Vec<Ref<'b, i32>>)
+  --> $DIR/ex2d-push-inference-variable-2.rs:16:33
+   |
+16 |     let a: &mut Vec<Ref<i32>> = x;
+   |                                 ^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.rs b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.rs
new file mode 100644
index 00000000000..2d05adb7ecd
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.rs
@@ -0,0 +1,21 @@
+// Copyright 2016 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.
+
+struct Ref<'a, T: 'a> {
+    data: &'a T
+}
+
+fn foo<'a, 'b, 'c>(x: &'a mut Vec<Ref<'b, i32>>, y: Ref<'c, i32>) {
+    let a: &mut Vec<Ref<i32>> = x;
+    let b = Ref { data: y.data };
+    Vec::push(a, b);
+}
+
+fn main() { }
diff --git a/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.stderr b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.stderr
new file mode 100644
index 00000000000..b679532a4d9
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex2e-push-inference-variable-3.stderr
@@ -0,0 +1,39 @@
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
+  --> $DIR/ex2e-push-inference-variable-3.rs:17:13
+   |
+17 |     let b = Ref { data: y.data };
+   |             ^^^
+   |
+note: first, the lifetime cannot outlive the lifetime 'c as defined on the body at 15:66...
+  --> $DIR/ex2e-push-inference-variable-3.rs:15:67
+   |
+15 |   fn foo<'a, 'b, 'c>(x: &'a mut Vec<Ref<'b, i32>>, y: Ref<'c, i32>) {
+   |  ___________________________________________________________________^ starting here...
+16 | |     let a: &mut Vec<Ref<i32>> = x;
+17 | |     let b = Ref { data: y.data };
+18 | |     Vec::push(a, b);
+19 | | }
+   | |_^ ...ending here
+note: ...so that reference does not outlive borrowed content
+  --> $DIR/ex2e-push-inference-variable-3.rs:17:25
+   |
+17 |     let b = Ref { data: y.data };
+   |                         ^^^^^^
+note: but, the lifetime must be valid for the lifetime 'b as defined on the body at 15:66...
+  --> $DIR/ex2e-push-inference-variable-3.rs:15:67
+   |
+15 |   fn foo<'a, 'b, 'c>(x: &'a mut Vec<Ref<'b, i32>>, y: Ref<'c, i32>) {
+   |  ___________________________________________________________________^ starting here...
+16 | |     let a: &mut Vec<Ref<i32>> = x;
+17 | |     let b = Ref { data: y.data };
+18 | |     Vec::push(a, b);
+19 | | }
+   | |_^ ...ending here
+note: ...so that expression is assignable (expected &mut std::vec::Vec<Ref<'_, i32>>, found &mut std::vec::Vec<Ref<'b, i32>>)
+  --> $DIR/ex2e-push-inference-variable-3.rs:16:33
+   |
+16 |     let a: &mut Vec<Ref<i32>> = x;
+   |                                 ^
+
+error: aborting due to previous error
+