]> git.lizzy.rs Git - rust.git/commitdiff
Adding diagnostic code 0611 for lifetime errors with one named, one anonymous lifetim...
authorgaurikholkar <f2013002@goa.bits-pilani.ac.in>
Mon, 12 Jun 2017 18:45:19 +0000 (11:45 -0700)
committergaurikholkar <f2013002@goa.bits-pilani.ac.in>
Thu, 29 Jun 2017 13:37:18 +0000 (06:37 -0700)
15 files changed:
src/librustc/diagnostics.rs
src/librustc/infer/error_reporting/mod.rs
src/librustc/infer/error_reporting/named_anon_conflict.rs [new file with mode: 0644]
src/librustc/infer/error_reporting/util.rs [new file with mode: 0644]
src/librustc/infer/mod.rs
src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-2.rs [new file with mode: 0644]
src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-2.stderr [new file with mode: 0644]
src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.rs [new file with mode: 0644]
src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.stderr [new file with mode: 0644]
src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-closure.rs [new file with mode: 0644]
src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-closure.stderr [new file with mode: 0644]
src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else.stderr
src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.rs [new file with mode: 0644]
src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.stderr [new file with mode: 0644]
src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr

index 5e36bd8ec2772abb1db02605c3763adaceb50bfe..1e7f3f9aeb8b9e10db632e1d3db5bfd5d652d819 100644 (file)
@@ -1946,6 +1946,31 @@ fn main() {
 Either way, try to update/remove it in order to fix the error.
 "##,
 
+E0611: r##"
+Lifetime parameter is missing in one of the function argument. Erroneous
+code example:
+
+```compile_fail,E0611
+fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 { // explicit lifetime required
+                                             // in the type of `y`
+    if x > y { x } else { y }
+}
+
+fn main () { }
+```
+
+Please add the missing lifetime parameter to remove this error. Example:
+
+```
+fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 {
+    if x > y { x } else { y }
+}
+
+fn main() {
+}
+```
+"##,
+
 }
 
 
index 11bac21bc429e99b661f56908fc553796f44c16f..1bc01ab858c94a303f2f679fe5e6e6650b8354af 100644 (file)
 use middle::region;
 use traits::{ObligationCause, ObligationCauseCode};
 use ty::{self, TyCtxt, TypeFoldable};
-use ty::{Region, Issue32330};
+use ty::{Region, Issue32330 };
 use ty::error::TypeError;
 use syntax::ast::DUMMY_NODE_ID;
 use syntax_pos::{Pos, Span};
 use errors::{DiagnosticBuilder, DiagnosticStyledString};
-
 mod note;
+
 mod need_type_info;
+mod named_anon_conflict;
+mod util;
+
 
 impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
     pub fn note_and_explain_region(self,
@@ -255,34 +258,42 @@ fn explain_span<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
 }
 
 impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
-    pub fn report_region_errors(&self,
-                                errors: &Vec<RegionResolutionError<'tcx>>) {
+
+    pub fn report_region_errors(&self, errors: &Vec<RegionResolutionError<'tcx>>) {
         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 errors = self.process_errors(errors);
 
-        debug!("report_region_errors: {} errors after preprocessing", errors.len());
+        debug!("report_region_errors: {} errors after preprocessing",
+               errors.len());
 
         for error in errors {
+
             debug!("report_region_errors: error = {:?}", error);
-            match error.clone() {
-                ConcreteFailure(origin, sub, sup) => {
-                    self.report_concrete_failure(origin, sub, sup).emit();
-                }
+        // If ConcreteFailure does not have an anonymous region
+            if !self.report_named_anon_conflict(&error){
 
-                GenericBoundFailure(kind, param_ty, sub) => {
-                    self.report_generic_bound_failure(kind, param_ty, sub);
-                }
+               match error.clone() {
+
+                  ConcreteFailure(origin, sub, sup) => {
+
+                      self.report_concrete_failure(origin, sub, sup).emit();
+                  }
 
-                SubSupConflict(var_origin,
+                  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,
+                      self.report_sub_sup_conflict(var_origin,
                                                  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
new file mode 100644 (file)
index 0000000..d48cb39
--- /dev/null
@@ -0,0 +1,115 @@
+// 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.
+use hir;
+use infer::InferCtxt;
+use ty::{self, Region};
+use infer::region_inference::RegionResolutionError::*;
+use infer::region_inference::RegionResolutionError;
+
+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, the `y` and the `Ty` of `y` is returned after being substituted
+    // by that of the named region.
+    pub fn find_arg_with_anonymous_region(&self,
+                                          anon_region: Region<'tcx>,
+                                          named_region: Region<'tcx>)
+                                          -> Option<(&hir::Arg, ty::Ty<'tcx>)> {
+
+        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);
+                body.arguments
+                    .iter()
+                    .filter_map(|arg| if let Some(tables) = self.in_progress_tables {
+                                    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 {
+                                        return Some((arg, new_arg_ty));
+                                    } else {
+                                        None
+                                    }
+                                } else {
+                                    None
+                                })
+                    .next()
+            }
+            _ => 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(..)`
+    pub fn report_named_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
+        };
+
+        let (named, (var, new_ty)) =
+            if self.is_named_region(sub) && self.is_anonymous_region(sup) {
+                (sub, self.find_arg_with_anonymous_region(sup, sub).unwrap())
+            } else if self.is_named_region(sup) && self.is_anonymous_region(sub) {
+                (sup, self.find_arg_with_anonymous_region(sub, sup).unwrap())
+            } else {
+                return false; // inapplicable
+            };
+
+        if let Some(simple_name) = var.pat.simple_name() {
+            struct_span_err!(self.tcx.sess,
+                             var.pat.span,
+                             E0611,
+                             "explicit lifetime required in the type of `{}`",
+                             simple_name)
+                    .span_label(var.pat.span,
+                                format!("consider changing the type of `{}` to `{}`",
+                                        simple_name,
+                                        new_ty))
+                    .span_label(span, format!("lifetime `{}` required", named))
+                    .emit();
+
+        } else {
+            struct_span_err!(self.tcx.sess,
+                             var.pat.span,
+                             E0611,
+                             "explicit lifetime required in parameter type")
+                    .span_label(var.pat.span,
+                                format!("consider changing type to `{}`", new_ty))
+                    .span_label(span, format!("lifetime `{}` required", named))
+                    .emit();
+        }
+        return true;
+
+    }
+}
diff --git a/src/librustc/infer/error_reporting/util.rs b/src/librustc/infer/error_reporting/util.rs
new file mode 100644 (file)
index 0000000..ea0c706
--- /dev/null
@@ -0,0 +1,56 @@
+// 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 for error reporting code for named_anon_conflict
+
+use ty::{self, Region};
+use infer::InferCtxt;
+use hir::map as hir_map;
+
+impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
+    // This method returns whether the given Region is Named
+    pub fn is_named_region(&self, region: Region<'tcx>) -> bool {
+
+        match *region {
+            ty::ReFree(ref free_region) => {
+                match free_region.bound_region {
+                    ty::BrNamed(..) => true,
+                    _ => false,
+                }
+            }
+            _ => false,
+        }
+    }
+
+    // This method returns whether the given Region is Anonymous
+    pub fn is_anonymous_region(&self, region: Region<'tcx>) -> bool {
+
+        match *region {
+            ty::ReFree(ref free_region) => {
+                match free_region.bound_region {
+                    ty::BrAnon(..) => {
+                        let id = free_region.scope;
+                        let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
+                        match self.tcx.hir.find(node_id) {
+                            Some(hir_map::NodeItem(..)) |
+                            Some(hir_map::NodeImplItem(..)) |
+                            Some(hir_map::NodeTraitItem(..)) => { /* proceed ahead */ }
+                            _ => return false, // inapplicable
+                            // we target only top-level functions
+                        }
+                        return true;
+                    }
+                    _ => false,
+                }
+            }
+            _ => false,
+        }
+    }
+}
index d5020b12ee00e12c5e86295d92ad75a9a8219c03..07de44c92948daead0bb1c501cc18e5104bbfdc3 100644 (file)
@@ -38,7 +38,6 @@
 use syntax_pos::{self, Span, DUMMY_SP};
 use util::nodemap::FxHashMap;
 use arena::DroplessArena;
-
 use self::combine::CombineFields;
 use self::higher_ranked::HrMatchResult;
 use self::region_inference::{RegionVarBindings, RegionSnapshot};
@@ -1077,6 +1076,7 @@ pub fn resolve_regions_and_report_errors(&self,
                                                region_map,
                                                free_regions);
         let errors = self.region_vars.resolve_regions(&region_rels);
+
         if !self.is_tainted_by_errors() {
             // As a heuristic, just skip reporting region errors
             // altogether if other errors have been reported while
diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-2.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-2.rs
new file mode 100644 (file)
index 0000000..a1716c4
--- /dev/null
@@ -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: &i32, y: &'a 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-2.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-2.stderr
new file mode 100644 (file)
index 0000000..a04a946
--- /dev/null
@@ -0,0 +1,10 @@
+error[E0611]: explicit lifetime required in the type of `x`
+  --> $DIR/ex1-return-one-existing-name-if-else-2.rs:11:12
+   |
+11 | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
+   |            ^ consider changing the type of `x` to `&'a i32`
+12 |     if x > y { x } else { y }
+   |                - lifetime `'a` required
+
+error: aborting due to previous error(s)
+
diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.rs
new file mode 100644 (file)
index 0000000..7bd32d8
--- /dev/null
@@ -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, y): (&'a i32, &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-3.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.stderr
new file mode 100644 (file)
index 0000000..143021c
--- /dev/null
@@ -0,0 +1,10 @@
+error[E0611]: explicit lifetime required in parameter type
+  --> $DIR/ex1-return-one-existing-name-if-else-3.rs:11:12
+   |
+11 | fn foo<'a>((x, y): (&'a i32, &i32)) -> &'a i32 {
+   |            ^^^^^^ consider changing type to `(&'a i32, &'a i32)`
+12 |     if x > y { x } else { y }
+   |                           - lifetime `'a` required
+
+error: aborting due to previous error(s)
+
diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-closure.rs b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-closure.rs
new file mode 100644 (file)
index 0000000..faf4fe5
--- /dev/null
@@ -0,0 +1,24 @@
+// 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 invoke<'a, F>(x: &'a i32, f: F) -> &'a i32
+where F: FnOnce(&'a i32, &i32) -> &'a i32
+{
+    let y = 22;
+    f(x, &y)
+}
+
+fn foo<'a>(x: &'a i32) {
+    invoke(&x, |a, b| if a > b { a } else { b });
+}
+
+fn main() {
+}
+
diff --git a/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-closure.stderr b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-closure.stderr
new file mode 100644 (file)
index 0000000..c96ff9b
--- /dev/null
@@ -0,0 +1,29 @@
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
+  --> $DIR/ex1-return-one-existing-name-if-else-using-closure.rs:19:5
+   |
+19 |     invoke(&x, |a, b| if a > b { a } else { b });
+   |     ^^^^^^
+   |
+note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 19:16...
+  --> $DIR/ex1-return-one-existing-name-if-else-using-closure.rs:19:16
+   |
+19 |     invoke(&x, |a, b| if a > b { a } else { b });
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: ...so that reference does not outlive borrowed content
+  --> $DIR/ex1-return-one-existing-name-if-else-using-closure.rs:19:45
+   |
+19 |     invoke(&x, |a, b| if a > b { a } else { b });
+   |                                             ^
+note: but, the lifetime must be valid for the call at 19:5...
+  --> $DIR/ex1-return-one-existing-name-if-else-using-closure.rs:19:5
+   |
+19 |     invoke(&x, |a, b| if a > b { a } else { b });
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: ...so that argument is valid for the call
+  --> $DIR/ex1-return-one-existing-name-if-else-using-closure.rs:19:12
+   |
+19 |     invoke(&x, |a, b| if a > b { a } else { b });
+   |            ^^
+
+error: aborting due to previous error(s)
+
index 0ab24b0b3e6c417bb035f4df923ad9865b523c85..84f166dfa3023df6e4bd329e2f531f545530bb77 100644 (file)
@@ -1,23 +1,10 @@
-error[E0312]: lifetime of reference outlives lifetime of borrowed content...
-  --> $DIR/ex1-return-one-existing-name-if-else.rs:12:27
+error[E0611]: explicit lifetime required in the type of `y`
+  --> $DIR/ex1-return-one-existing-name-if-else.rs:11:24
    |
+11 | fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 {
+   |                        ^ consider changing the type of `y` to `&'a i32`
 12 |     if x > y { x } else { y }
-   |                           ^
-   |
-note: ...the reference is valid for the lifetime 'a as defined on the function body at 11:1...
-  --> $DIR/ex1-return-one-existing-name-if-else.rs:11:1
-   |
-11 | / fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 {
-12 | |     if x > y { x } else { y }
-13 | | }
-   | |_^
-note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the function body at 11:1
-  --> $DIR/ex1-return-one-existing-name-if-else.rs:11:1
-   |
-11 | / fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 {
-12 | |     if x > y { x } else { y }
-13 | | }
-   | |_^
+   |                           - lifetime `'a` required
 
 error: aborting due to previous error(s)
 
diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.rs b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.rs
new file mode 100644 (file)
index 0000000..dd34e1a
--- /dev/null
@@ -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: Ref<i32>, y: &mut Vec<Ref<'a, i32>>) {
+    y.push(x);
+}
+
+fn main() { }
diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.stderr b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.stderr
new file mode 100644 (file)
index 0000000..61e97dc
--- /dev/null
@@ -0,0 +1,10 @@
+error[E0611]: explicit lifetime required in the type of `x`
+  --> $DIR/ex2a-push-one-existing-name-2.rs:15:12
+   |
+15 | fn foo<'a>(x: Ref<i32>, y: &mut Vec<Ref<'a, i32>>) {
+   |            ^ consider changing the type of `x` to `Ref<'a, i32>`
+16 |     y.push(x);
+   |            - lifetime `'a` required
+
+error: aborting due to previous error(s)
+
index 7d0947b364e03ff2318b3aaa4421f460d6667a09..51d86a1f964ca9626f14d155ba0d674140ac8eb2 100644 (file)
@@ -1,25 +1,10 @@
-error[E0308]: mismatched types
-  --> $DIR/ex2a-push-one-existing-name.rs:16:12
+error[E0611]: explicit lifetime required in the type of `y`
+  --> $DIR/ex2a-push-one-existing-name.rs:15:39
    |
+15 | fn foo<'a>(x: &mut Vec<Ref<'a, i32>>, y: Ref<i32>) {
+   |                                       ^ consider changing the type of `y` to `Ref<'a, i32>`
 16 |     x.push(y);
-   |            ^ lifetime mismatch
-   |
-   = note: expected type `Ref<'a, _>`
-              found type `Ref<'_, _>`
-note: the anonymous lifetime #2 defined on the function body at 15:1...
-  --> $DIR/ex2a-push-one-existing-name.rs:15:1
-   |
-15 | / fn foo<'a>(x: &mut Vec<Ref<'a, i32>>, y: Ref<i32>) {
-16 | |     x.push(y);
-17 | | }
-   | |_^
-note: ...does not necessarily outlive the lifetime 'a as defined on the function body at 15:1
-  --> $DIR/ex2a-push-one-existing-name.rs:15:1
-   |
-15 | / fn foo<'a>(x: &mut Vec<Ref<'a, i32>>, y: Ref<i32>) {
-16 | |     x.push(y);
-17 | | }
-   | |_^
+   |            - lifetime `'a` required
 
 error: aborting due to previous error(s)