diff options
15 files changed, 365 insertions, 54 deletions
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 5e36bd8ec27..1e7f3f9aeb8 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1946,6 +1946,31 @@ Maybe you just misspelled the lint name or the lint doesn't exist anymore. 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() { +} +``` +"##, + } diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs index 11bac21bc42..1bc01ab858c 100644 --- a/src/librustc/infer/error_reporting/mod.rs +++ b/src/librustc/infer/error_reporting/mod.rs @@ -67,14 +67,17 @@ use hir::def_id::DefId; 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 @@ impl<'a, 'gcx, '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 index 00000000000..d48cb398ec2 --- /dev/null +++ b/src/librustc/infer/error_reporting/named_anon_conflict.rs @@ -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 index 00000000000..ea0c706d8aa --- /dev/null +++ b/src/librustc/infer/error_reporting/util.rs @@ -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, + } + } +} diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index d5020b12ee0..07de44c9294 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -38,7 +38,6 @@ use errors::DiagnosticBuilder; 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 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { region_map, free_regions); let errors = self.region_vars.resolve_regions(®ion_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 index 00000000000..a1716c4e797 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-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<'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 index 00000000000..a04a9461eb4 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-2.stderr @@ -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 index 00000000000..7bd32d87617 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-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<'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 index 00000000000..143021cbbdd --- /dev/null +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-3.stderr @@ -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 index 00000000000..faf4fe547bf --- /dev/null +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-closure.rs @@ -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 index 00000000000..c96ff9bc114 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex1-return-one-existing-name-if-else-using-closure.stderr @@ -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) + 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 index 0ab24b0b3e6..84f166dfa30 100644 --- 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 @@ -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 index 00000000000..dd34e1aa6d9 --- /dev/null +++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.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: 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 index 00000000000..61e97dc2bcd --- /dev/null +++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name-2.stderr @@ -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) + 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 index 7d0947b364e..51d86a1f964 100644 --- a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr +++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr @@ -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) |
