From 4fb1808ab6e8a232be17ba3e74e27fa08bd4f28d Mon Sep 17 00:00:00 2001
From: gaurikholkar <f2013002@goa.bits-pilani.ac.in>
Date: Thu, 29 Jun 2017 12:40:04 -0700
Subject: [PATCH] Adding E0623, to detect missing lifetimes when both regions
 are anonymous

---
 src/librustc/diagnostics.rs                   |   1 +
 .../error_reporting/anon_anon_conflict.rs     | 200 ++++++++++++++++++
 src/librustc/infer/error_reporting/mod.rs     |  51 +++--
 .../error_reporting/named_anon_conflict.rs    | 107 +---------
 src/librustc/infer/error_reporting/util.rs    | 120 +++++++++++
 .../ex3-both-anon-regions-2.rs                |  15 ++
 .../ex3-both-anon-regions-2.stderr            |  10 +
 .../ex3-both-anon-regions-3.rs                |  15 ++
 .../ex3-both-anon-regions-3.stderr            |  10 +
 .../ex3-both-anon-regions-4.rs                |  13 ++
 .../ex3-both-anon-regions-4.stderr            |  20 ++
 .../lifetime-errors/ex3-both-anon-regions.rs  |  15 ++
 .../ex3-both-anon-regions.stderr              |  10 +
 13 files changed, 462 insertions(+), 125 deletions(-)
 create mode 100644 src/librustc/infer/error_reporting/anon_anon_conflict.rs
 create mode 100644 src/librustc/infer/error_reporting/util.rs
 create mode 100644 src/test/ui/lifetime-errors/ex3-both-anon-regions-2.rs
 create mode 100644 src/test/ui/lifetime-errors/ex3-both-anon-regions-2.stderr
 create mode 100644 src/test/ui/lifetime-errors/ex3-both-anon-regions-3.rs
 create mode 100644 src/test/ui/lifetime-errors/ex3-both-anon-regions-3.stderr
 create mode 100644 src/test/ui/lifetime-errors/ex3-both-anon-regions-4.rs
 create mode 100644 src/test/ui/lifetime-errors/ex3-both-anon-regions-4.stderr
 create mode 100644 src/test/ui/lifetime-errors/ex3-both-anon-regions.rs
 create mode 100644 src/test/ui/lifetime-errors/ex3-both-anon-regions.stderr

diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index 3ce39b23b0f67..522c1531c5938 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -2025,4 +2025,5 @@ register_diagnostics! {
     E0490, // a value of type `..` is borrowed for too long
     E0495, // cannot infer an appropriate lifetime due to conflicting requirements
     E0566, // conflicting representation hints
+    E0623, // lifetime mismatch where both parameters are anonymous regions
 }
diff --git a/src/librustc/infer/error_reporting/anon_anon_conflict.rs b/src/librustc/infer/error_reporting/anon_anon_conflict.rs
new file mode 100644
index 0000000000000..1017f2bd0e6e7
--- /dev/null
+++ b/src/librustc/infer/error_reporting/anon_anon_conflict.rs
@@ -0,0 +1,200 @@
+// Copyright 2012-2013 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.
+
+//! Error Reporting for Anonymous Region Lifetime Errors
+//! where both the regions are anonymous.
+use hir;
+use infer::InferCtxt;
+use ty::{self, Region};
+use infer::region_inference::RegionResolutionError::*;
+use infer::region_inference::RegionResolutionError;
+use hir::map as hir_map;
+use middle::resolve_lifetime as rl;
+use hir::intravisit::{self, Visitor, NestedVisitorMap};
+
+impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
+    // This method prints the error message for lifetime errors when both the concerned regions
+    // are anonymous.
+    // Consider a case where we have
+    // fn foo(x: &mut Vec<&u8>, y: &u8)
+    //    { x.push(y); }.
+    // The example gives
+    // fn foo(x: &mut Vec<&u8>, y: &u8) {
+    //                    ---      --- these references must have the same lifetime
+    //            x.push(y);
+    //            ^ data from `y` flows into `x` here
+    // It will later be extended to trait objects and structs.
+    pub fn try_report_anon_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool {
+
+        let (span, sub, sup) = match *error {
+            ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup),
+            _ => return false, // inapplicable
+        };
+
+        // Determine whether the sub and sup consist of both anonymous (elided) regions.
+        let (ty1, ty2) = if self.is_suitable_anonymous_region(sup).is_some() &&
+                            self.is_suitable_anonymous_region(sub).is_some() {
+            if let (Some(anon_reg1), Some(anon_reg2)) =
+                (self.is_suitable_anonymous_region(sup), self.is_suitable_anonymous_region(sub)) {
+                let ((_, br1), (_, br2)) = (anon_reg1, anon_reg2);
+                if self.find_anon_type(sup, &br1).is_some() &&
+                   self.find_anon_type(sub, &br2).is_some() {
+                    (self.find_anon_type(sup, &br1).unwrap(),
+                     self.find_anon_type(sub, &br2).unwrap())
+                } else {
+                    return false;
+                }
+            } else {
+                return false;
+            }
+        } else {
+            return false; // inapplicable
+        };
+
+        if let (Some(sup_arg), Some(sub_arg)) =
+            (self.find_arg_with_anonymous_region(sup, sup),
+             self.find_arg_with_anonymous_region(sub, sub)) {
+            let ((anon_arg1, _, _, _), (anon_arg2, _, _, _)) = (sup_arg, sub_arg);
+
+            let span_label_var1 = if let Some(simple_name) = anon_arg1.pat.simple_name() {
+                format!(" from `{}` ", simple_name)
+            } else {
+                format!(" ")
+            };
+
+            let span_label_var2 = if let Some(simple_name) = anon_arg2.pat.simple_name() {
+                format!(" into `{}` ", simple_name)
+            } else {
+                format!(" ")
+            };
+
+            struct_span_err!(self.tcx.sess, span, E0623, "lifetime mismatch")
+                .span_label(ty1.span,
+                            format!("these references must have the same lifetime"))
+                .span_label(ty2.span, format!(""))
+                .span_label(span,
+                            format!("data{}flows{}here", span_label_var1, span_label_var2))
+                .emit();
+        } else {
+            return false;
+        }
+
+        return true;
+    }
+
+    /// This function calls the `visit_ty` method for the parameters
+    /// corresponding to the anonymous regions. The `nested_visitor.found_type`
+    /// contains the anonymous type.
+    ///
+    /// # Arguments
+    ///
+    /// region - the anonymous region corresponding to the anon_anon conflict
+    /// br - the bound region corresponding to the above region which is of type `BrAnon(_)`
+    ///
+    /// # Example
+    /// ```
+    /// fn foo(x: &mut Vec<&u8>, y: &u8)
+    ///    { x.push(y); }
+    /// ```
+    /// The function returns the nested type corresponding to the anonymous region
+    /// for e.g. `&u8` and Vec<`&u8`.
+    fn find_anon_type(&self, region: Region<'tcx>, br: &ty::BoundRegion) -> Option<&hir::Ty> {
+        if let Some(anon_reg) = self.is_suitable_anonymous_region(region) {
+            let (def_id, _) = anon_reg;
+            if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
+                let ret_ty = self.tcx.type_of(def_id);
+                if let ty::TyFnDef(_, _) = ret_ty.sty {
+                    if let hir_map::NodeItem(it) = self.tcx.hir.get(node_id) {
+                        if let hir::ItemFn(ref fndecl, _, _, _, _, _) = it.node {
+                            return fndecl
+                                       .inputs
+                                       .iter()
+                                       .filter_map(|arg| {
+                                let mut nested_visitor = FindNestedTypeVisitor {
+                                    infcx: &self,
+                                    hir_map: &self.tcx.hir,
+                                    bound_region: *br,
+                                    found_type: None,
+                                };
+                                nested_visitor.visit_ty(&**arg);
+                                if nested_visitor.found_type.is_some() {
+                                    nested_visitor.found_type
+                                } else {
+                                    None
+                                }
+                            })
+                                       .next();
+                        }
+                    }
+                }
+            }
+        }
+        None
+    }
+}
+
+// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
+// anonymous region. The example above would lead to a conflict between
+// the two anonymous lifetimes for &u8 in x and y respectively. This visitor
+// would be invoked twice, once for each lifetime, and would
+// walk the types like &mut Vec<&u8> and &u8 looking for the HIR
+// where that lifetime appears. This allows us to highlight the
+// specific part of the type in the error message.
+struct FindNestedTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
+    infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+    hir_map: &'a hir::map::Map<'gcx>,
+    // The bound_region corresponding to the Refree(freeregion)
+    // associated with the anonymous region we are looking for.
+    bound_region: ty::BoundRegion,
+    // The type where the anonymous lifetime appears
+    // for e.g. Vec<`&u8`> and <`&u8`>
+    found_type: Option<&'gcx hir::Ty>,
+}
+
+impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
+    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
+        NestedVisitorMap::OnlyBodies(&self.hir_map)
+    }
+
+    fn visit_ty(&mut self, arg: &'gcx hir::Ty) {
+        // Find the index of the anonymous region that was part of the
+        // error. We will then search the function parameters for a bound
+        // region at the right depth with the same index.
+        let br_index = match self.bound_region {
+            ty::BrAnon(index) => index,
+            _ => return,
+        };
+
+        match arg.node {
+            hir::TyRptr(ref lifetime, _) => {
+                match self.infcx.tcx.named_region_map.defs.get(&lifetime.id) {
+                    // the lifetime of the TyRptr
+                    Some(&rl::Region::LateBoundAnon(debuijn_index, anon_index)) => {
+                        if debuijn_index.depth == 1 && anon_index == br_index {
+                            self.found_type = Some(arg);
+                            return; // we can stop visiting now
+                        }
+                    }
+                    Some(&rl::Region::Static) |
+                    Some(&rl::Region::EarlyBound(_, _)) |
+                    Some(&rl::Region::LateBound(_, _)) |
+                    Some(&rl::Region::Free(_, _)) |
+                    None => {
+                        debug!("no arg found");
+                    }
+                }
+            }
+            _ => {}
+        }
+        // walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
+        // go on to visit `&Foo`
+        intravisit::walk_ty(self, arg);
+    }
+}
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 95722a93beda1..77ec866dc8087 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -76,7 +76,9 @@ use errors::{DiagnosticBuilder, DiagnosticStyledString};
 mod note;
 
 mod need_type_info;
+mod util;
 mod named_anon_conflict;
+mod anon_anon_conflict;
 
 impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     pub fn note_and_explain_region(self,
@@ -270,29 +272,34 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         for error in errors {
             debug!("report_region_errors: error = {:?}", error);
 
-            if !self.try_report_named_anon_conflict(&error) {
-                match error.clone() {
-                    // These errors could indicate all manner of different
-                    // problems with many different solutions. Rather
-                    // than generate a "one size fits all" error, what we
-                    // attempt to do is go through a number of specific
-                    // scenarios and try to find the best way to present
-                    // the error. If all of these fails, we fall back to a rather
-                    // general bit of code that displays the error information
-                    ConcreteFailure(origin, sub, sup) => {
-                        self.report_concrete_failure(origin, sub, sup).emit();
-                    }
-                    GenericBoundFailure(kind, param_ty, sub) => {
-                        self.report_generic_bound_failure(kind, param_ty, sub);
-                    }
-                    SubSupConflict(var_origin,
-                                 sub_origin, sub_r,
-                                 sup_origin, sup_r) => {
+            if !self.try_report_named_anon_conflict(&error) &&
+               !self.try_report_anon_anon_conflict(&error) {
+
+               match error.clone() {
+                  // These errors could indicate all manner of different
+                  // problems with many different solutions. Rather
+                  // than generate a "one size fits all" error, what we
+                  // attempt to do is go through a number of specific
+                  // scenarios and try to find the best way to present
+                  // the error. If all of these fails, we fall back to a rather
+                  // general bit of code that displays the error information
+                  ConcreteFailure(origin, sub, sup) => {
+
+                      self.report_concrete_failure(origin, sub, sup).emit();
+                  }
+
+                  GenericBoundFailure(kind, param_ty, sub) => {
+                      self.report_generic_bound_failure(kind, param_ty, sub);
+                  }
+
+                  SubSupConflict(var_origin, sub_origin, sub_r, sup_origin, sup_r) => {
                         self.report_sub_sup_conflict(var_origin,
-                                                     sub_origin, sub_r,
-                                                     sup_origin, sup_r);
-                    }
-                }
+                                                     sub_origin,
+                                                     sub_r,
+                                                     sup_origin,
+                                                     sup_r);
+                  }
+               }
             }
         }
     }
diff --git a/src/librustc/infer/error_reporting/named_anon_conflict.rs b/src/librustc/infer/error_reporting/named_anon_conflict.rs
index 48f39ac5b3e56..491079a1f9259 100644
--- a/src/librustc/infer/error_reporting/named_anon_conflict.rs
+++ b/src/librustc/infer/error_reporting/named_anon_conflict.rs
@@ -8,69 +8,14 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-//! Error Reporting for Anonymous Region Lifetime Errors.
-use hir;
+//! Error Reporting for Anonymous Region Lifetime Errors
+//! where one region is named and the other is anonymous.
 use infer::InferCtxt;
-use ty::{self, Region};
+use ty;
 use infer::region_inference::RegionResolutionError::*;
 use infer::region_inference::RegionResolutionError;
-use hir::map as hir_map;
-use hir::def_id::DefId;
 
 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
-    // This method walks the Type of the function body arguments using
-    // `fold_regions()` function and returns the
-    // &hir::Arg of the function argument corresponding to the anonymous
-    // region and the Ty corresponding to the named region.
-    // Currently only the case where the function declaration consists of
-    // one named region and one anonymous region is handled.
-    // Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
-    // Here, we would return the hir::Arg for y, we return the type &'a
-    // i32, which is the type of y but with the anonymous region replaced
-    // with 'a, the corresponding bound region and is_first which is true if
-    // the hir::Arg is the first argument in the function declaration.
-    fn find_arg_with_anonymous_region
-        (&self,
-         anon_region: Region<'tcx>,
-         named_region: Region<'tcx>)
-         -> Option<(&hir::Arg, ty::Ty<'tcx>, ty::BoundRegion, bool)> {
-
-        match *anon_region {
-            ty::ReFree(ref free_region) => {
-                let id = free_region.scope;
-                let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
-                let body_id = self.tcx.hir.maybe_body_owned_by(node_id).unwrap();
-                let body = self.tcx.hir.body(body_id);
-                if let Some(tables) = self.in_progress_tables {
-                    body.arguments
-                        .iter()
-                        .enumerate()
-                        .filter_map(|(index, arg)| {
-                            let ty = tables.borrow().node_id_to_type(arg.id);
-                            let mut found_anon_region = false;
-                            let new_arg_ty = self.tcx
-                                .fold_regions(&ty, &mut false, |r, _| if *r == *anon_region {
-                                    found_anon_region = true;
-                                    named_region
-                                } else {
-                                    r
-                                });
-                            if found_anon_region {
-                                let is_first = index == 0;
-                                Some((arg, new_arg_ty, free_region.bound_region, is_first))
-                            } else {
-                                None
-                            }
-                        })
-                        .next()
-                } else {
-                    None
-                }
-            }
-            _ => None,
-        }
-    }
-
     // This method generates the error message for the case when
     // the function arguments consist of a named region and an anonymous
     // region and corresponds to `ConcreteFailure(..)`
@@ -86,7 +31,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         // only introduced anonymous regions in parameters) as well as a
         // version new_ty of its type where the anonymous region is replaced
         // with the named one.
-        let (named, (arg, new_ty, br, is_first), scope_def_id) =
+        let (named, (arg, new_ty, br, is_first), (scope_def_id, _)) =
             if sub.is_named_region() && self.is_suitable_anonymous_region(sup).is_some() {
                 (sub,
                  self.find_arg_with_anonymous_region(sup, sub).unwrap(),
@@ -145,48 +90,4 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
         return true;
     }
-
-    // This method returns whether the given Region is Anonymous
-    // and returns the DefId corresponding to the region.
-    pub fn is_suitable_anonymous_region(&self, region: Region<'tcx>) -> Option<DefId> {
-        match *region {
-            ty::ReFree(ref free_region) => {
-                match free_region.bound_region {
-                    ty::BrAnon(..) => {
-                        let anonymous_region_binding_scope = free_region.scope;
-                        let node_id = self.tcx
-                            .hir
-                            .as_local_node_id(anonymous_region_binding_scope)
-                            .unwrap();
-                        match self.tcx.hir.find(node_id) {
-                            Some(hir_map::NodeItem(..)) |
-                            Some(hir_map::NodeTraitItem(..)) => {
-                                // proceed ahead //
-                            }
-                            Some(hir_map::NodeImplItem(..)) => {
-                                let container_id = self.tcx
-                                    .associated_item(anonymous_region_binding_scope)
-                                    .container
-                                    .id();
-                                if self.tcx.impl_trait_ref(container_id).is_some() {
-                                    // For now, we do not try to target impls of traits. This is
-                                    // because this message is going to suggest that the user
-                                    // change the fn signature, but they may not be free to do so,
-                                    // since the signature must match the trait.
-                                    //
-                                    // FIXME(#42706) -- in some cases, we could do better here.
-                                    return None;
-                                }
-                            }
-                            _ => return None, // inapplicable
-                            // we target only top-level functions
-                        }
-                        return Some(anonymous_region_binding_scope);
-                    }
-                    _ => None,
-                }
-            }
-            _ => None,
-        }
-    }
 }
diff --git a/src/librustc/infer/error_reporting/util.rs b/src/librustc/infer/error_reporting/util.rs
new file mode 100644
index 0000000000000..14fe8e699c7f2
--- /dev/null
+++ b/src/librustc/infer/error_reporting/util.rs
@@ -0,0 +1,120 @@
+// Copyright 2012-2013 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.
+
+//! Helper functions corresponding to lifetime errors due to
+//! anonymous regions.
+use hir;
+use infer::InferCtxt;
+use ty::{self, Region};
+use hir::def_id::DefId;
+use hir::map as hir_map;
+
+impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
+    // This method walks the Type of the function body arguments using
+    // `fold_regions()` function and returns the
+    // &hir::Arg of the function argument corresponding to the anonymous
+    // region and the Ty corresponding to the named region.
+    // Currently only the case where the function declaration consists of
+    // one named region and one anonymous region is handled.
+    // Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
+    // Here, we would return the hir::Arg for y, we return the type &'a
+    // i32, which is the type of y but with the anonymous region replaced
+    // with 'a, the corresponding bound region and is_first which is true if
+    // the hir::Arg is the first argument in the function declaration.
+    pub fn find_arg_with_anonymous_region
+        (&self,
+         anon_region: Region<'tcx>,
+         replace_region: Region<'tcx>)
+         -> Option<(&hir::Arg, ty::Ty<'tcx>, ty::BoundRegion, bool)> {
+
+        if let ty::ReFree(ref free_region) = *anon_region {
+
+            let id = free_region.scope;
+            let hir = &self.tcx.hir;
+            if let Some(node_id) = hir.as_local_node_id(id) {
+                if let Some(body_id) = hir.maybe_body_owned_by(node_id) {
+                    let body = hir.body(body_id);
+                    if let Some(tables) = self.in_progress_tables {
+                        body.arguments
+                            .iter()
+                            .enumerate()
+                            .filter_map(|(index, arg)| {
+                                let ty = tables.borrow().node_id_to_type(arg.id);
+                                let mut found_anon_region = false;
+                                let new_arg_ty = self.tcx
+                                    .fold_regions(&ty, &mut false, |r, _| if *r == *anon_region {
+                                        found_anon_region = true;
+                                        replace_region
+                                    } else {
+                                        r
+                                    });
+                                if found_anon_region {
+                                    let is_first = index == 0;
+                                    Some((arg, new_arg_ty, free_region.bound_region, is_first))
+                                } else {
+                                    None
+                                }
+                            })
+                            .next()
+                    } else {
+                        None
+                    }
+                } else {
+                    None
+                }
+            } else {
+                None
+            }
+        } else {
+            None
+        }
+    }
+
+    // This method returns whether the given Region is Anonymous
+    // and returns the DefId and the BoundRegion corresponding to the given region.
+    pub fn is_suitable_anonymous_region(&self,
+                                        region: Region<'tcx>)
+                                        -> Option<(DefId, ty::BoundRegion)> {
+        if let ty::ReFree(ref free_region) = *region {
+            if let ty::BrAnon(..) = free_region.bound_region{
+                    let anonymous_region_binding_scope = free_region.scope;
+                    let node_id = self.tcx
+                        .hir
+                        .as_local_node_id(anonymous_region_binding_scope)
+                        .unwrap();
+                    match self.tcx.hir.find(node_id) {
+                        Some(hir_map::NodeItem(..)) |
+                        Some(hir_map::NodeTraitItem(..)) => {
+                            // Success -- proceed to return Some below
+                        }
+                        Some(hir_map::NodeImplItem(..)) => {
+                            let container_id = self.tcx
+                                .associated_item(anonymous_region_binding_scope)
+                                .container
+                                .id();
+                            if self.tcx.impl_trait_ref(container_id).is_some() {
+                                // For now, we do not try to target impls of traits. This is
+                                // because this message is going to suggest that the user
+                                // change the fn signature, but they may not be free to do so,
+                                // since the signature must match the trait.
+                                //
+                                // FIXME(#42706) -- in some cases, we could do better here.
+                                return None;
+                            }
+                        }
+                        _ => return None, // inapplicable
+                        // we target only top-level functions
+                    }
+                    return Some((anonymous_region_binding_scope, free_region.bound_region));
+                }
+            }
+            None
+        }
+}
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.rs
new file mode 100644
index 0000000000000..905eae18d1807
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.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((v, w): (&u8, &u8), x: &u8) {
+    v = x;
+}
+
+fn main() { }
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.stderr
new file mode 100644
index 0000000000000..8dd906afdc4e7
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-2.stderr
@@ -0,0 +1,10 @@
+error[E0623]: lifetime mismatch
+  --> $DIR/ex3-both-anon-regions-2.rs:12:9
+   |
+11 | fn foo((v, w): (&u8, &u8), x: &u8) {
+   |                 ---           --- these references must have the same lifetime
+12 |     v = x;
+   |         ^ data from `x` flows here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.rs
new file mode 100644
index 0000000000000..7bd5ebf805f1e
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.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((v, w): (&u8, &u8), (x, y): (&u8, &u8)) {
+    v = x;
+}
+
+fn main() { }
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.stderr
new file mode 100644
index 0000000000000..66c3ca45499b6
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-3.stderr
@@ -0,0 +1,10 @@
+error[E0623]: lifetime mismatch
+  --> $DIR/ex3-both-anon-regions-3.rs:12:9
+   |
+11 | fn foo((v, w): (&u8, &u8), (x, y): (&u8, &u8)) {
+   |                 ---                 --- these references must have the same lifetime
+12 |     v = x;
+   |         ^ data flows here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-4.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions-4.rs
new file mode 100644
index 0000000000000..fdb010a04f4c6
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-4.rs
@@ -0,0 +1,13 @@
+// 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(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) {
+    z.push((x,y));
+}
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions-4.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions-4.stderr
new file mode 100644
index 0000000000000..b969797b37440
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions-4.stderr
@@ -0,0 +1,20 @@
+error[E0601]: main function not found
+
+error[E0623]: lifetime mismatch
+  --> $DIR/ex3-both-anon-regions-4.rs:12:13
+   |
+11 | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) {
+   |                     ---                 --- these references must have the same lifetime
+12 |     z.push((x,y));
+   |             ^ data flows into `z` here
+
+error[E0623]: lifetime mismatch
+  --> $DIR/ex3-both-anon-regions-4.rs:12:15
+   |
+11 | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) {
+   |                         ---                  --- these references must have the same lifetime
+12 |     z.push((x,y));
+   |               ^ data flows into `z` here
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions.rs b/src/test/ui/lifetime-errors/ex3-both-anon-regions.rs
new file mode 100644
index 0000000000000..9ebff5118768d
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions.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: &mut Vec<&u8>, y: &u8) {
+    x.push(y);
+}
+
+fn main() { }
diff --git a/src/test/ui/lifetime-errors/ex3-both-anon-regions.stderr b/src/test/ui/lifetime-errors/ex3-both-anon-regions.stderr
new file mode 100644
index 0000000000000..e38e2ef07ad87
--- /dev/null
+++ b/src/test/ui/lifetime-errors/ex3-both-anon-regions.stderr
@@ -0,0 +1,10 @@
+error[E0623]: lifetime mismatch
+  --> $DIR/ex3-both-anon-regions.rs:12:12
+   |
+11 | fn foo(x: &mut Vec<&u8>, y: &u8) {
+   |                    ---      --- these references must have the same lifetime
+12 |     x.push(y);
+   |            ^ data from `y` flows into `x` here
+
+error: aborting due to previous error
+