Skip to content

Commit a47e48a

Browse files
Rollup merge of rust-lang#42669 - gaurikholkar:master, r=nikomatsakis
Adding diagnostic code 0611 for lifetime errors with one named, one anonymous lifetime parameter This is a fix for rust-lang#42517 Note that this only handles the above case for **function declarations** and **traits**. `impl items` and `closures` will be handled in a later PR. Example ``` fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 { if x > y { x } else { y } } ``` now displays the following error message. ui tests have been added for the same. ``` error[E0611]: explicit lifetime required in the type of `x` 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 ``` rust-lang#42516 r? @nikomatsakis
2 parents e72580c + 37a88f4 commit a47e48a

24 files changed

+615
-51
lines changed

src/librustc/diagnostics.rs

+38
Original file line numberDiff line numberDiff line change
@@ -1946,6 +1946,44 @@ Maybe you just misspelled the lint name or the lint doesn't exist anymore.
19461946
Either way, try to update/remove it in order to fix the error.
19471947
"##,
19481948

1949+
E0621: r##"
1950+
This error code indicates a mismatch between the function signature (i.e.,
1951+
the parameter types and the return type) and the function body. Most of
1952+
the time, this indicates that the function signature needs to be changed to
1953+
match the body, but it may be that the body needs to be changed to match
1954+
the signature.
1955+
1956+
Specifically, one or more of the parameters contain borrowed data that
1957+
needs to have a named lifetime in order for the body to type-check. Most of
1958+
the time, this is because the borrowed data is being returned from the
1959+
function, as in this example:
1960+
1961+
```compile_fail,E0621
1962+
fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 { // explicit lifetime required
1963+
// in the type of `y`
1964+
if x > y { x } else { y }
1965+
}
1966+
```
1967+
1968+
Here, the function is returning data borrowed from either x or y, but the
1969+
'a annotation indicates that it is returning data only from x. We can make
1970+
the signature match the body by changing the type of y to &'a i32, like so:
1971+
1972+
```
1973+
fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 {
1974+
if x > y { x } else { y }
1975+
}
1976+
```
1977+
1978+
Alternatively, you could change the body not to return data from y:
1979+
1980+
```
1981+
fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 {
1982+
x
1983+
}
1984+
```
1985+
"##,
1986+
19491987
}
19501988

19511989

src/librustc/infer/error_reporting/mod.rs

+30-14
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,11 @@ 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+
7880

7981
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
8082
pub fn note_and_explain_region(self,
@@ -255,34 +257,48 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
255257
}
256258

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

262264
// try to pre-process the errors, which will group some of them
263265
// together into a `ProcessedErrors` group:
264266
let errors = self.process_errors(errors);
265267

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

268271
for error in errors {
272+
269273
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-
}
274274

275-
GenericBoundFailure(kind, param_ty, sub) => {
276-
self.report_generic_bound_failure(kind, param_ty, sub);
277-
}
275+
if !self.try_report_named_anon_conflict(&error){
276+
277+
match error.clone() {
278+
// These errors could indicate all manner of different
279+
// problems with many different solutions. Rather
280+
// than generate a "one size fits all" error, what we
281+
// attempt to do is go through a number of specific
282+
// scenarios and try to find the best way to present
283+
// the error. If all of these fails, we fall back to a rather
284+
// general bit of code that displays the error information
285+
ConcreteFailure(origin, sub, sup) => {
286+
287+
self.report_concrete_failure(origin, sub, sup).emit();
288+
}
278289

279-
SubSupConflict(var_origin,
290+
GenericBoundFailure(kind, param_ty, sub) => {
291+
self.report_generic_bound_failure(kind, param_ty, sub);
292+
}
293+
294+
SubSupConflict(var_origin,
280295
sub_origin, sub_r,
281296
sup_origin, sup_r) => {
282-
self.report_sub_sup_conflict(var_origin,
297+
self.report_sub_sup_conflict(var_origin,
283298
sub_origin, sub_r,
284299
sup_origin, sup_r);
285-
}
300+
}
301+
}
286302
}
287303
}
288304
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
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+
use hir::map as hir_map;
18+
use hir::def_id::DefId;
19+
20+
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
21+
// This method walks the Type of the function body arguments using
22+
// `fold_regions()` function and returns the
23+
// &hir::Arg of the function argument corresponding to the anonymous
24+
// region and the Ty corresponding to the named region.
25+
// Currently only the case where the function declaration consists of
26+
// one named region and one anonymous region is handled.
27+
// Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
28+
// Here, we would return the hir::Arg for y, we return the type &'a
29+
// i32, which is the type of y but with the anonymous region replaced
30+
// with 'a, the corresponding bound region and is_first which is true if
31+
// the hir::Arg is the first argument in the function declaration.
32+
fn find_arg_with_anonymous_region
33+
(&self,
34+
anon_region: Region<'tcx>,
35+
named_region: Region<'tcx>)
36+
-> Option<(&hir::Arg, ty::Ty<'tcx>, ty::BoundRegion, bool)> {
37+
38+
match *anon_region {
39+
ty::ReFree(ref free_region) => {
40+
41+
let id = free_region.scope;
42+
let node_id = self.tcx.hir.as_local_node_id(id).unwrap();
43+
let body_id = self.tcx.hir.maybe_body_owned_by(node_id).unwrap();
44+
let body = self.tcx.hir.body(body_id);
45+
if let Some(tables) = self.in_progress_tables {
46+
body.arguments
47+
.iter()
48+
.enumerate()
49+
.filter_map(|(index, arg)| {
50+
let ty = tables.borrow().node_id_to_type(arg.id);
51+
let mut found_anon_region = false;
52+
let new_arg_ty = self.tcx
53+
.fold_regions(&ty, &mut false, |r, _| if *r == *anon_region {
54+
found_anon_region = true;
55+
named_region
56+
} else {
57+
r
58+
});
59+
if found_anon_region {
60+
let is_first = index == 0;
61+
Some((arg, new_arg_ty, free_region.bound_region, is_first))
62+
} else {
63+
None
64+
}
65+
})
66+
.next()
67+
} else {
68+
None
69+
}
70+
}
71+
_ => None,
72+
73+
}
74+
}
75+
76+
// This method generates the error message for the case when
77+
// the function arguments consist of a named region and an anonymous
78+
// region and corresponds to `ConcreteFailure(..)`
79+
pub fn try_report_named_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool {
80+
81+
let (span, sub, sup) = match *error {
82+
ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup),
83+
_ => return false, // inapplicable
84+
};
85+
86+
// Determine whether the sub and sup consist of one named region ('a)
87+
// and one anonymous (elided) region. If so, find the parameter arg
88+
// where the anonymous region appears (there must always be one; we
89+
// only introduced anonymous regions in parameters) as well as a
90+
// version new_ty of its type where the anonymous region is replaced
91+
// with the named one.
92+
let (named, (arg, new_ty, br, is_first), scope_def_id) =
93+
if sub.is_named_region() && self.is_suitable_anonymous_region(sup).is_some() {
94+
(sub,
95+
self.find_arg_with_anonymous_region(sup, sub).unwrap(),
96+
self.is_suitable_anonymous_region(sup).unwrap())
97+
} else if sup.is_named_region() && self.is_suitable_anonymous_region(sub).is_some() {
98+
(sup,
99+
self.find_arg_with_anonymous_region(sub, sup).unwrap(),
100+
self.is_suitable_anonymous_region(sub).unwrap())
101+
} else {
102+
return false; // inapplicable
103+
};
104+
105+
// Here, we check for the case where the anonymous region
106+
// is in the return type.
107+
// FIXME(#42703) - Need to handle certain cases here.
108+
let ret_ty = self.tcx.type_of(scope_def_id);
109+
match ret_ty.sty {
110+
ty::TyFnDef(_, _) => {
111+
let sig = ret_ty.fn_sig(self.tcx);
112+
let late_bound_regions = self.tcx
113+
.collect_referenced_late_bound_regions(&sig.output());
114+
if late_bound_regions.iter().any(|r| *r == br) {
115+
return false;
116+
} else {
117+
}
118+
}
119+
_ => {}
120+
}
121+
122+
// Here we check for the case where anonymous region
123+
// corresponds to self and if yes, we display E0312.
124+
// FIXME(#42700) - Need to format self properly to
125+
// enable E0621 for it.
126+
if is_first &&
127+
self.tcx
128+
.opt_associated_item(scope_def_id)
129+
.map(|i| i.method_has_self_argument)
130+
.unwrap_or(false) {
131+
return false;
132+
}
133+
134+
let (error_var, span_label_var) = if let Some(simple_name) = arg.pat.simple_name() {
135+
(format!("the type of `{}`", simple_name), format!("the type of `{}`", simple_name))
136+
} else {
137+
(format!("parameter type"), format!("type"))
138+
};
139+
140+
141+
struct_span_err!(self.tcx.sess,
142+
span,
143+
E0621,
144+
"explicit lifetime required in {}",
145+
error_var)
146+
.span_label(arg.pat.span,
147+
format!("consider changing {} to `{}`", span_label_var, new_ty))
148+
.span_label(span, format!("lifetime `{}` required", named))
149+
.emit();
150+
151+
return true;
152+
153+
}
154+
155+
// This method returns whether the given Region is Anonymous
156+
// and returns the DefId corresponding to the region.
157+
pub fn is_suitable_anonymous_region(&self, region: Region<'tcx>) -> Option<DefId> {
158+
159+
match *region {
160+
ty::ReFree(ref free_region) => {
161+
match free_region.bound_region {
162+
ty::BrAnon(..) => {
163+
let anonymous_region_binding_scope = free_region.scope;
164+
let node_id = self.tcx
165+
.hir
166+
.as_local_node_id(anonymous_region_binding_scope)
167+
.unwrap();
168+
match self.tcx.hir.find(node_id) {
169+
Some(hir_map::NodeItem(..)) |
170+
Some(hir_map::NodeTraitItem(..)) => {
171+
// proceed ahead //
172+
}
173+
Some(hir_map::NodeImplItem(..)) => {
174+
let container_id = self.tcx
175+
.associated_item(anonymous_region_binding_scope)
176+
.container
177+
.id();
178+
if self.tcx.impl_trait_ref(container_id).is_some() {
179+
// For now, we do not try to target impls of traits. This is
180+
// because this message is going to suggest that the user
181+
// change the fn signature, but they may not be free to do so,
182+
// since the signature must match the trait.
183+
//
184+
// FIXME(#42706) -- in some cases, we could do better here.
185+
return None;
186+
}
187+
}
188+
_ => return None, // inapplicable
189+
// we target only top-level functions
190+
}
191+
return Some(anonymous_region_binding_scope);
192+
}
193+
_ => None,
194+
}
195+
}
196+
_ => None,
197+
}
198+
}
199+
}

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

src/librustc/ty/sty.rs

+14
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,20 @@ impl RegionKind {
990990

991991
flags
992992
}
993+
994+
// This method returns whether the given Region is Named
995+
pub fn is_named_region(&self) -> bool {
996+
997+
match *self {
998+
ty::ReFree(ref free_region) => {
999+
match free_region.bound_region {
1000+
ty::BrNamed(..) => true,
1001+
_ => false,
1002+
}
1003+
}
1004+
_ => false,
1005+
}
1006+
}
9931007
}
9941008

9951009
/// Type utilities
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
// Test that we give the generic E0495 when one of the free regions is
12+
// bound in a closure (rather than suggesting a change to the signature
13+
// of the closure, which is not specified in `foo` but rather in `invoke`).
14+
15+
// FIXME - This might be better as a UI test, but the finer details
16+
// of the error seem to vary on different machines.
17+
fn invoke<'a, F>(x: &'a i32, f: F) -> &'a i32
18+
where F: FnOnce(&'a i32, &i32) -> &'a i32
19+
{
20+
let y = 22;
21+
f(x, &y)
22+
}
23+
24+
fn foo<'a>(x: &'a i32) {
25+
invoke(&x, |a, b| if a > b { a } else { b }); //~ ERROR E0495
26+
}
27+
28+
fn main() {}

0 commit comments

Comments
 (0)