Skip to content

Commit 0743060

Browse files
committed
Adding diagnostic code 0611 for lifetime errors with one named, one anonymous lifetime parameter
1 parent 787d9da commit 0743060

15 files changed

+365
-54
lines changed

src/librustc/diagnostics.rs

+25
Original file line numberDiff line numberDiff line change
@@ -1913,6 +1913,31 @@ Maybe you just misspelled the lint name or the lint doesn't exist anymore.
19131913
Either way, try to update/remove it in order to fix the error.
19141914
"##,
19151915

1916+
E0611: r##"
1917+
Lifetime parameter is missing in one of the function argument. Erroneous
1918+
code example:
1919+
1920+
```compile_fail,E0611
1921+
fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 { // explicit lifetime required
1922+
// in the type of `y`
1923+
if x > y { x } else { y }
1924+
}
1925+
1926+
fn main () { }
1927+
```
1928+
1929+
Please add the missing lifetime parameter to remove this error. Example:
1930+
1931+
```
1932+
fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 {
1933+
if x > y { x } else { y }
1934+
}
1935+
1936+
fn main() {
1937+
}
1938+
```
1939+
"##,
1940+
19161941
}
19171942

19181943

src/librustc/infer/error_reporting/mod.rs

+26-15
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,17 @@ use hir::def_id::DefId;
6767
use middle::region;
6868
use traits::{ObligationCause, ObligationCauseCode};
6969
use ty::{self, TyCtxt, TypeFoldable};
70-
use ty::{Region, Issue32330};
70+
use ty::{Region, Issue32330 };
7171
use ty::error::TypeError;
7272
use syntax::ast::DUMMY_NODE_ID;
7373
use syntax_pos::{Pos, Span};
7474
use errors::{DiagnosticBuilder, DiagnosticStyledString};
75-
7675
mod note;
76+
7777
mod need_type_info;
78+
mod named_anon_conflict;
79+
mod util;
80+
7881

7982
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
8083
pub fn note_and_explain_region(self,
@@ -255,34 +258,42 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
255258
}
256259

257260
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
258-
pub fn report_region_errors(&self,
259-
errors: &Vec<RegionResolutionError<'tcx>>) {
261+
262+
pub fn report_region_errors(&self, errors: &Vec<RegionResolutionError<'tcx>>) {
260263
debug!("report_region_errors(): {} errors to start", errors.len());
261264

262265
// try to pre-process the errors, which will group some of them
263266
// together into a `ProcessedErrors` group:
264267
let errors = self.process_errors(errors);
265268

266-
debug!("report_region_errors: {} errors after preprocessing", errors.len());
269+
debug!("report_region_errors: {} errors after preprocessing",
270+
errors.len());
267271

268272
for error in errors {
273+
269274
debug!("report_region_errors: error = {:?}", error);
270-
match error.clone() {
271-
ConcreteFailure(origin, sub, sup) => {
272-
self.report_concrete_failure(origin, sub, sup).emit();
273-
}
275+
// If ConcreteFailure does not have an anonymous region
276+
if !self.report_named_anon_conflict(&error){
274277

275-
GenericBoundFailure(kind, param_ty, sub) => {
276-
self.report_generic_bound_failure(kind, param_ty, sub);
277-
}
278+
match error.clone() {
279+
280+
ConcreteFailure(origin, sub, sup) => {
281+
282+
self.report_concrete_failure(origin, sub, sup).emit();
283+
}
278284

279-
SubSupConflict(var_origin,
285+
GenericBoundFailure(kind, param_ty, sub) => {
286+
self.report_generic_bound_failure(kind, param_ty, sub);
287+
}
288+
289+
SubSupConflict(var_origin,
280290
sub_origin, sub_r,
281291
sup_origin, sup_r) => {
282-
self.report_sub_sup_conflict(var_origin,
292+
self.report_sub_sup_conflict(var_origin,
283293
sub_origin, sub_r,
284294
sup_origin, sup_r);
285-
}
295+
}
296+
}
286297
}
287298
}
288299
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! Error Reporting for Anonymous Region Lifetime Errors.
12+
use hir;
13+
use infer::InferCtxt;
14+
use ty::{self, Region};
15+
use infer::region_inference::RegionResolutionError::*;
16+
use infer::region_inference::RegionResolutionError;
17+
18+
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
19+
// This method walks the Type of the function body arguments using
20+
// `fold_regions()` function and returns the
21+
// &hir::Arg of the function argument corresponding to the anonymous
22+
// region and the Ty corresponding to the named region.
23+
// Currently only the case where the function declaration consists of
24+
// one named region and one anonymous region is handled.
25+
// Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
26+
// Here, the `y` and the `Ty` of `y` is returned after being substituted
27+
// by that of the named region.
28+
pub fn find_arg_with_anonymous_region(&self,
29+
anon_region: Region<'tcx>,
30+
named_region: Region<'tcx>)
31+
-> Option<(&hir::Arg, ty::Ty<'tcx>)> {
32+
33+
match *anon_region {
34+
ty::ReFree(ref free_region) => {
35+
36+
let id = free_region.scope;
37+
let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
38+
let body_id = self.tcx.hir.maybe_body_owned_by(node_id).unwrap();
39+
40+
let body = self.tcx.hir.body(body_id);
41+
body.arguments
42+
.iter()
43+
.filter_map(|arg| if let Some(tables) = self.in_progress_tables {
44+
let ty = tables.borrow().node_id_to_type(arg.id);
45+
let mut found_anon_region = false;
46+
let new_arg_ty = self.tcx
47+
.fold_regions(&ty,
48+
&mut false,
49+
|r, _| if *r == *anon_region {
50+
found_anon_region = true;
51+
named_region
52+
} else {
53+
r
54+
});
55+
if found_anon_region {
56+
return Some((arg, new_arg_ty));
57+
} else {
58+
None
59+
}
60+
} else {
61+
None
62+
})
63+
.next()
64+
}
65+
_ => None,
66+
}
67+
68+
}
69+
70+
// This method generates the error message for the case when
71+
// the function arguments consist of a named region and an anonymous
72+
// region and corresponds to `ConcreteFailure(..)`
73+
pub fn report_named_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool {
74+
75+
let (span, sub, sup) = match *error {
76+
ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup),
77+
_ => return false, // inapplicable
78+
};
79+
80+
let (named, (var, new_ty)) =
81+
if self.is_named_region(sub) && self.is_anonymous_region(sup) {
82+
(sub, self.find_arg_with_anonymous_region(sup, sub).unwrap())
83+
} else if self.is_named_region(sup) && self.is_anonymous_region(sub) {
84+
(sup, self.find_arg_with_anonymous_region(sub, sup).unwrap())
85+
} else {
86+
return false; // inapplicable
87+
};
88+
89+
if let Some(simple_name) = var.pat.simple_name() {
90+
struct_span_err!(self.tcx.sess,
91+
var.pat.span,
92+
E0611,
93+
"explicit lifetime required in the type of `{}`",
94+
simple_name)
95+
.span_label(var.pat.span,
96+
format!("consider changing the type of `{}` to `{}`",
97+
simple_name,
98+
new_ty))
99+
.span_label(span, format!("lifetime `{}` required", named))
100+
.emit();
101+
102+
} else {
103+
struct_span_err!(self.tcx.sess,
104+
var.pat.span,
105+
E0611,
106+
"explicit lifetime required in parameter type")
107+
.span_label(var.pat.span,
108+
format!("consider changing type to `{}`", new_ty))
109+
.span_label(span, format!("lifetime `{}` required", named))
110+
.emit();
111+
}
112+
return true;
113+
114+
}
115+
}
+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! Helper for error reporting code for named_anon_conflict
12+
13+
use ty::{self, Region};
14+
use infer::InferCtxt;
15+
use hir::map as hir_map;
16+
17+
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
18+
// This method returns whether the given Region is Named
19+
pub fn is_named_region(&self, region: Region<'tcx>) -> bool {
20+
21+
match *region {
22+
ty::ReFree(ref free_region) => {
23+
match free_region.bound_region {
24+
ty::BrNamed(..) => true,
25+
_ => false,
26+
}
27+
}
28+
_ => false,
29+
}
30+
}
31+
32+
// This method returns whether the given Region is Anonymous
33+
pub fn is_anonymous_region(&self, region: Region<'tcx>) -> bool {
34+
35+
match *region {
36+
ty::ReFree(ref free_region) => {
37+
match free_region.bound_region {
38+
ty::BrAnon(..) => {
39+
let id = free_region.scope;
40+
let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
41+
match self.tcx.hir.find(node_id) {
42+
Some(hir_map::NodeItem(..)) |
43+
Some(hir_map::NodeImplItem(..)) |
44+
Some(hir_map::NodeTraitItem(..)) => { /* proceed ahead */ }
45+
_ => return false, // inapplicable
46+
// we target only top-level functions
47+
}
48+
return true;
49+
}
50+
_ => false,
51+
}
52+
}
53+
_ => false,
54+
}
55+
}
56+
}

src/librustc/infer/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ use errors::DiagnosticBuilder;
3838
use syntax_pos::{self, Span, DUMMY_SP};
3939
use util::nodemap::FxHashMap;
4040
use arena::DroplessArena;
41-
4241
use self::combine::CombineFields;
4342
use self::higher_ranked::HrMatchResult;
4443
use self::region_inference::{RegionVarBindings, RegionSnapshot};
@@ -1077,6 +1076,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
10771076
region_map,
10781077
free_regions);
10791078
let errors = self.region_vars.resolve_regions(&region_rels);
1079+
10801080
if !self.is_tainted_by_errors() {
10811081
// As a heuristic, just skip reporting region errors
10821082
// altogether if other errors have been reported while
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
12+
if x > y { x } else { y }
13+
}
14+
15+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error[E0611]: explicit lifetime required in the type of `x`
2+
--> $DIR/ex1-return-one-existing-name-if-else-2.rs:11:12
3+
|
4+
11 | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
5+
| ^ consider changing the type of `x` to `&'a i32`
6+
12 | if x > y { x } else { y }
7+
| - lifetime `'a` required
8+
9+
error: aborting due to previous error(s)
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn foo<'a>((x, y): (&'a i32, &i32)) -> &'a i32 {
12+
if x > y { x } else { y }
13+
}
14+
15+
fn main () { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error[E0611]: explicit lifetime required in parameter type
2+
--> $DIR/ex1-return-one-existing-name-if-else-3.rs:11:12
3+
|
4+
11 | fn foo<'a>((x, y): (&'a i32, &i32)) -> &'a i32 {
5+
| ^^^^^^ consider changing type to `(&'a i32, &'a i32)`
6+
12 | if x > y { x } else { y }
7+
| - lifetime `'a` required
8+
9+
error: aborting due to previous error(s)
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn invoke<'a, F>(x: &'a i32, f: F) -> &'a i32
12+
where F: FnOnce(&'a i32, &i32) -> &'a i32
13+
{
14+
let y = 22;
15+
f(x, &y)
16+
}
17+
18+
fn foo<'a>(x: &'a i32) {
19+
invoke(&x, |a, b| if a > b { a } else { b });
20+
}
21+
22+
fn main() {
23+
}
24+

0 commit comments

Comments
 (0)