Rollup merge of #45751 - estebank:issue-44684, r=nikomatsakis

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. Fix #44684.
This commit is contained in:
kennytm 2017-11-07 15:52:11 +08:00
commit d8238e4314
No known key found for this signature in database
GPG key ID: FEF6C8051D0E013C
9 changed files with 95 additions and 42 deletions

View file

@ -21,25 +21,42 @@ use hir::intravisit::{self, Visitor, NestedVisitorMap};
use infer::error_reporting::util::AnonymousArgInfo;
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// This method prints the error message for lifetime errors when both the concerned regions
// are anonymous.
// Consider a case where we have
// fn foo(x: &mut Vec<&u8>, y: &u8)
// { x.push(y); }.
// The example gives
// fn foo(x: &mut Vec<&u8>, y: &u8) {
// --- --- these references are declared with different lifetimes...
// x.push(y);
// ^ ...but data from `y` flows into `x` here
// It has been extended for the case of structs too.
// Consider the example
// struct Ref<'a> { x: &'a u32 }
// fn foo(mut x: Vec<Ref>, y: Ref) {
// --- --- these structs are declared with different lifetimes...
// x.push(y);
// ^ ...but data from `y` flows into `x` here
// }
// It will later be extended to trait objects.
/// Print the error message for lifetime errors when both the concerned regions are anonymous.
///
/// Consider a case where we have
///
/// ```no_run
/// fn foo(x: &mut Vec<&u8>, y: &u8) {
/// x.push(y);
/// }
/// ```
///
/// The example gives
///
/// ```text
/// fn foo(x: &mut Vec<&u8>, y: &u8) {
/// --- --- these references are declared with different lifetimes...
/// x.push(y);
/// ^ ...but data from `y` flows into `x` here
/// ```
///
/// It has been extended for the case of structs too.
///
/// Consider the example
///
/// ```no_run
/// struct Ref<'a> { x: &'a u32 }
/// ```
///
/// ```text
/// fn foo(mut x: Vec<Ref>, y: Ref) {
/// --- --- these structs are declared with different lifetimes...
/// x.push(y);
/// ^ ...but data from `y` flows into `x` here
/// }
/// ````
///
/// It will later be extended to trait objects.
pub fn try_report_anon_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool {
let (span, sub, sup) = match *error {
ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup),

View file

@ -16,18 +16,15 @@ use infer::region_inference::RegionResolutionError;
use ty;
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// 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(..)`
/// When given a `ConcreteFailure` for a function with arguments containing a named region and
/// an anonymous region, emit an descriptive diagnostic error.
pub fn try_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
};
debug!("try_report_named_anon_conflict(sub={:?}, sup={:?})",
sub,
sup);
debug!("try_report_named_anon_conflict(sub={:?}, sup={:?})", sub, sup);
// Determine whether the sub and sup consist of one named region ('a)
// and one anonymous (elided) region. If so, find the parameter arg
@ -53,10 +50,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
};
debug!("try_report_named_anon_conflict: named = {:?}", named);
debug!("try_report_named_anon_conflict: anon_arg_info = {:?}",
anon_arg_info);
debug!("try_report_named_anon_conflict: region_info = {:?}",
region_info);
debug!("try_report_named_anon_conflict: anon_arg_info = {:?}", anon_arg_info);
debug!("try_report_named_anon_conflict: region_info = {:?}", region_info);
let (arg, new_ty, br, is_first, scope_def_id, is_impl_item) = (anon_arg_info.arg,
anon_arg_info.arg_ty,
@ -101,6 +96,5 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
.span_label(span, format!("lifetime `{}` required", named))
.emit();
return true;
}
}

View file

@ -221,6 +221,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
_ => false,
}
}
ty::ReEarlyBound(_) => true,
_ => false,
}
}

View file

@ -21,7 +21,7 @@ struct not_parameterized2 {
}
fn take1<'a>(p: parameterized1) -> parameterized1<'a> { p }
//~^ ERROR mismatched types
//~^ ERROR explicit lifetime required in the type of `p`
fn take3(p: not_parameterized1) -> not_parameterized1 { p }
fn take4(p: not_parameterized2) -> not_parameterized2 { p }

View file

@ -0,0 +1,30 @@
// Copyright 2017 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.
#[derive(Clone)]
enum Foo<'a> {
Bar(&'a str),
}
impl<'a> Foo<'a> {
fn bar(&self, other: Foo) -> Foo<'a> {
match *self {
Foo::Bar(s) => {
if s == "test" {
other
} else {
self.clone()
}
}
}
}
}
fn main() { }

View file

@ -0,0 +1,11 @@
error[E0621]: explicit lifetime required in the type of `other`
--> $DIR/ex1-return-one-existing-name-early-bound-in-struct.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
error: aborting due to previous error

View file

@ -0,0 +1,11 @@
error[E0621]: explicit lifetime required in the type of `y`
--> $DIR/ex2a-push-one-existing-name-early-bound.rs:17:12
|
13 | fn baz<'a, 'b, T>(x: &mut Vec<&'a T>, y: &T)
| - consider changing the type of `y` to `&'a T`
...
17 | x.push(y);
| ^ lifetime `'a` required
error: aborting due to previous error

View file

@ -1,11 +0,0 @@
error[E0623]: lifetime mismatch
--> $DIR/ex3-both-anon-regions-earlybound-regions.rs:17:12
|
13 | fn baz<'a, 'b, T>(x: &mut Vec<&'a T>, y: &T)
| ----- -- these two types are declared with different lifetimes...
...
17 | x.push(y);
| ^ ...but data from `y` flows into `x` here
error: aborting due to previous error