Skip to content

Commit de959af

Browse files
committed
Handle anon lifetime arg being returned with named lifetime return type
When there's a lifetime mismatch between an argument with an anonymous lifetime being returned in a method with a return type that has a named lifetime, show specialized lifetime error pointing at argument with a hint to give it an explicit lifetime matching the return type. ``` error[E0621]: explicit lifetime required in the type of `other` --> file2.rs:21:21 | 17 | fn bar(&self, other: Foo) -> Foo<'a> { | ----- consider changing the type of `other` to `Foo<'a>` ... 21 | other | ^^^^^ lifetime `'a` required ``` Follow up to #44124 and #42669.
1 parent 5ce3d48 commit de959af

File tree

7 files changed

+87
-34
lines changed

7 files changed

+87
-34
lines changed

src/librustc/infer/error_reporting/different_lifetimes.rs

+36-19
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,42 @@ use hir::intravisit::{self, Visitor, NestedVisitorMap};
2121
use infer::error_reporting::util::AnonymousArgInfo;
2222

2323
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
24-
// This method prints the error message for lifetime errors when both the concerned regions
25-
// are anonymous.
26-
// Consider a case where we have
27-
// fn foo(x: &mut Vec<&u8>, y: &u8)
28-
// { x.push(y); }.
29-
// The example gives
30-
// fn foo(x: &mut Vec<&u8>, y: &u8) {
31-
// --- --- these references are declared with different lifetimes...
32-
// x.push(y);
33-
// ^ ...but data from `y` flows into `x` here
34-
// It has been extended for the case of structs too.
35-
// Consider the example
36-
// struct Ref<'a> { x: &'a u32 }
37-
// fn foo(mut x: Vec<Ref>, y: Ref) {
38-
// --- --- these structs are declared with different lifetimes...
39-
// x.push(y);
40-
// ^ ...but data from `y` flows into `x` here
41-
// }
42-
// It will later be extended to trait objects.
24+
/// Print the error message for lifetime errors when both the concerned regions are anonymous.
25+
///
26+
/// Consider a case where we have
27+
///
28+
/// ```no_run
29+
/// fn foo(x: &mut Vec<&u8>, y: &u8) {
30+
/// x.push(y);
31+
/// }
32+
/// ```
33+
///
34+
/// The example gives
35+
///
36+
/// ```text
37+
/// fn foo(x: &mut Vec<&u8>, y: &u8) {
38+
/// --- --- these references are declared with different lifetimes...
39+
/// x.push(y);
40+
/// ^ ...but data from `y` flows into `x` here
41+
/// ```
42+
///
43+
/// It has been extended for the case of structs too.
44+
///
45+
/// Consider the example
46+
///
47+
/// ```no_run
48+
/// struct Ref<'a> { x: &'a u32 }
49+
/// ```
50+
///
51+
/// ```text
52+
/// fn foo(mut x: Vec<Ref>, y: Ref) {
53+
/// --- --- these structs are declared with different lifetimes...
54+
/// x.push(y);
55+
/// ^ ...but data from `y` flows into `x` here
56+
/// }
57+
/// ````
58+
///
59+
/// It will later be extended to trait objects.
4360
pub fn try_report_anon_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool {
4461
let (span, sub, sup) = match *error {
4562
ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup),

src/librustc/infer/error_reporting/named_anon_conflict.rs

+5-11
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,15 @@ use infer::region_inference::RegionResolutionError;
1616
use ty;
1717

1818
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
19-
// This method generates the error message for the case when
20-
// the function arguments consist of a named region and an anonymous
21-
// region and corresponds to `ConcreteFailure(..)`
19+
/// Generate an error message for when the function arguments consist of a named region and
20+
/// an anonymous region and corresponds to `ConcreteFailure(..)`
2221
pub fn try_report_named_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool {
2322
let (span, sub, sup) = match *error {
2423
ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup),
2524
_ => return false, // inapplicable
2625
};
2726

28-
debug!("try_report_named_anon_conflict(sub={:?}, sup={:?})",
29-
sub,
30-
sup);
27+
debug!("try_report_named_anon_conflict(sub={:?}, sup={:?})", sub, sup);
3128

3229
// Determine whether the sub and sup consist of one named region ('a)
3330
// and one anonymous (elided) region. If so, find the parameter arg
@@ -53,10 +50,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
5350
};
5451

5552
debug!("try_report_named_anon_conflict: named = {:?}", named);
56-
debug!("try_report_named_anon_conflict: anon_arg_info = {:?}",
57-
anon_arg_info);
58-
debug!("try_report_named_anon_conflict: region_info = {:?}",
59-
region_info);
53+
debug!("try_report_named_anon_conflict: anon_arg_info = {:?}", anon_arg_info);
54+
debug!("try_report_named_anon_conflict: region_info = {:?}", region_info);
6055

6156
let (arg, new_ty, br, is_first, scope_def_id, is_impl_item) = (anon_arg_info.arg,
6257
anon_arg_info.arg_ty,
@@ -101,6 +96,5 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
10196
.span_label(span, format!("lifetime `{}` required", named))
10297
.emit();
10398
return true;
104-
10599
}
106100
}

src/librustc/infer/error_reporting/util.rs

+1
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
221221
_ => false,
222222
}
223223
}
224+
ty::ReEarlyBound(_) => true,
224225
_ => false,
225226
}
226227
}

src/test/compile-fail/regions-infer-at-fn-not-param.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct not_parameterized2 {
2121
}
2222

2323
fn take1<'a>(p: parameterized1) -> parameterized1<'a> { p }
24-
//~^ ERROR mismatched types
24+
//~^ ERROR explicit lifetime required in the type of `p`
2525

2626
fn take3(p: not_parameterized1) -> not_parameterized1 { p }
2727
fn take4(p: not_parameterized2) -> not_parameterized2 { p }
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
error[E0623]: lifetime mismatch
1+
error[E0621]: explicit lifetime required in the type of `y`
22
--> $DIR/ex3-both-anon-regions-earlybound-regions.rs:17:12
33
|
44
13 | fn baz<'a, 'b, T>(x: &mut Vec<&'a T>, y: &T)
5-
| ----- -- these two types are declared with different lifetimes...
5+
| - consider changing the type of `y` to `&'a T`
66
...
77
17 | x.push(y);
8-
| ^ ...but data from `y` flows into `x` here
8+
| ^ lifetime `'a` required
99

1010
error: aborting due to previous error
1111

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2017 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+
#[derive(Clone)]
12+
enum Foo<'a> {
13+
Bar(&'a str),
14+
}
15+
16+
impl<'a> Foo<'a> {
17+
fn bar(&self, other: Foo) -> Foo<'a> {
18+
match *self {
19+
Foo::Bar(s) => {
20+
if s == "test" {
21+
other
22+
} else {
23+
self.clone()
24+
}
25+
}
26+
}
27+
}
28+
}
29+
30+
fn main() { }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0621]: explicit lifetime required in the type of `other`
2+
--> $DIR/ex4-anon-named-regions.rs:21:21
3+
|
4+
17 | fn bar(&self, other: Foo) -> Foo<'a> {
5+
| ----- consider changing the type of `other` to `Foo<'a>`
6+
...
7+
21 | other
8+
| ^^^^^ lifetime `'a` required
9+
10+
error: aborting due to previous error
11+

0 commit comments

Comments
 (0)