Skip to content

Commit

Permalink
Rollup merge of rust-lang#45751 - estebank:issue-44684, r=nikomatsakis
Browse files Browse the repository at this point in the history
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 rust-lang#44124 and rust-lang#42669. Fix rust-lang#44684.
  • Loading branch information
kennytm committed Nov 7, 2017
2 parents 264bfc4 + 805333b commit d8238e4
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 42 deletions.
55 changes: 36 additions & 19 deletions src/librustc/infer/error_reporting/different_lifetimes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
16 changes: 5 additions & 11 deletions src/librustc/infer/error_reporting/named_anon_conflict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -101,6 +96,5 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
.span_label(span, format!("lifetime `{}` required", named))
.emit();
return true;

}
}
1 change: 1 addition & 0 deletions src/librustc/infer/error_reporting/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
_ => false,
}
}
ty::ReEarlyBound(_) => true,
_ => false,
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/regions-infer-at-fn-not-param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
Original file line number Diff line number Diff line change
@@ -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() { }
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
@@ -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

This file was deleted.

0 comments on commit d8238e4

Please sign in to comment.