Rollup merge of #44124 - gaurikholkar:return_self, r=arielb1

adding E0623 for return types - both parameters are anonymous

This is a fix for #44018
```
error[E0621]: explicit lifetime required in the type of `self`
  --> $DIR/ex3-both-anon-regions-return-type-is-anon.rs:17:5
   |
16 |   fn foo<'a>(&self, x: &i32) -> &i32 {
   |                        ----     ----
   |                        |
   |                        this parameter and the return type are
                            declared with different lifetimes...
17 |     x
   |     ^ ...but data from `x` is returned here

error: aborting due to previous error
```
It also works for the below case where we have self as anonymous

```
error[E0623]: lifetime mismatch
  --> src/test/ui/lifetime-errors/ex3-both-anon-regions-self-is-anon.rs:17:19
   |
16 |     fn foo<'a>(&self, x: &Foo) -> &Foo {
   |                          ----     ----
   |                          |
   |                          this parameter and the return type are
                            declared with different lifetimes...
17 |         if true { x } else { self }
   |                   ^ ...but data from `x` is returned here

error: aborting due to previous error
```
r? @nikomatsakis

Currently, I have enabled E0621 where return type and self are anonymous, hence WIP.
This commit is contained in:
Mark Simulacrum 2017-09-29 17:58:51 -06:00 committed by GitHub
commit f407b2bf4a
12 changed files with 146 additions and 254 deletions

View File

@ -1351,74 +1351,6 @@ struct Foo<T: 'static> {
```
"##,
E0312: r##"
A lifetime of reference outlives lifetime of borrowed content.
Erroneous code example:
```compile_fail,E0312
fn make_child<'tree, 'human>(
x: &'human i32,
y: &'tree i32
) -> &'human i32 {
if x > y
{ x }
else
{ y }
// error: lifetime of reference outlives lifetime of borrowed content
}
```
The function declares that it returns a reference with the `'human`
lifetime, but it may return data with the `'tree` lifetime. As neither
lifetime is declared longer than the other, this results in an
error. Sometimes, this error is because the function *body* is
incorrect -- that is, maybe you did not *mean* to return data from
`y`. In that case, you should fix the function body.
Often, however, the body is correct. In that case, the function
signature needs to be altered to match the body, so that the caller
understands that data from either `x` or `y` may be returned. The
simplest way to do this is to give both function parameters the *same*
named lifetime:
```
fn make_child<'human>(
x: &'human i32,
y: &'human i32
) -> &'human i32 {
if x > y
{ x }
else
{ y } // ok!
}
```
However, in some cases, you may prefer to explicitly declare that one lifetime
outlives another using a `where` clause:
```
fn make_child<'tree, 'human>(
x: &'human i32,
y: &'tree i32
) -> &'human i32
where
'tree: 'human
{
if x > y
{ x }
else
{ y } // ok!
}
```
Here, the where clause `'tree: 'human` can be read as "the lifetime
'tree outlives the lifetime 'human" -- meaning, references with the
`'tree` lifetime live *at least as long as* references with the
`'human` lifetime. Therefore, it is safe to return data with lifetime
`'tree` when data with the lifetime `'human` is needed.
"##,
E0317: r##"
This error occurs when an `if` expression without an `else` block is used in a
context where a type other than `()` is expected, for example a `let`
@ -2028,6 +1960,7 @@ register_diagnostics! {
// E0304, // expected signed integer constant
// E0305, // expected constant
E0311, // thing may not live long enough
E0312, // lifetime of reference outlives lifetime of borrowed content
E0313, // lifetime of borrowed pointer outlives lifetime of captured variable
E0314, // closure outlives stack frame
E0315, // cannot invoke closure outside of its lifetime

View File

@ -18,6 +18,7 @@ use infer::region_inference::RegionResolutionError;
use hir::map as hir_map;
use middle::resolve_lifetime as rl;
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
@ -57,6 +58,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
let ty_sup = or_false!(self.find_anon_type(sup, &bregion_sup));
let ty_sub = or_false!(self.find_anon_type(sub, &bregion_sub));
debug!("try_report_anon_anon_conflict: found_arg1={:?} sup={:?} br1={:?}",
ty_sub,
sup,
@ -66,56 +68,70 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
sub,
bregion_sub);
let (main_label, label1, label2) = if let (Some(sup_arg), Some(sub_arg)) =
(self.find_arg_with_region(sup, sup), self.find_arg_with_region(sub, sub)) {
let (ty_sup, ty_fndecl_sup) = ty_sup;
let (ty_sub, ty_fndecl_sub) = ty_sub;
let (anon_arg_sup, is_first_sup, anon_arg_sub, is_first_sub) =
(sup_arg.arg, sup_arg.is_first, sub_arg.arg, sub_arg.is_first);
if self.is_self_anon(is_first_sup, scope_def_id_sup) ||
self.is_self_anon(is_first_sub, scope_def_id_sub) {
return false;
}
let AnonymousArgInfo { arg: anon_arg_sup, .. } =
or_false!(self.find_arg_with_region(sup, sup));
let AnonymousArgInfo { arg: anon_arg_sub, .. } =
or_false!(self.find_arg_with_region(sub, sub));
if self.is_return_type_anon(scope_def_id_sup, bregion_sup) ||
self.is_return_type_anon(scope_def_id_sub, bregion_sub) {
return false;
}
let sup_is_ret_type =
self.is_return_type_anon(scope_def_id_sup, bregion_sup, ty_fndecl_sup);
let sub_is_ret_type =
self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub);
if anon_arg_sup == anon_arg_sub {
(format!("this type was declared with multiple lifetimes..."),
format!(" with one lifetime"),
format!(" into the other"))
} else {
let span_label_var1 = if let Some(simple_name) = anon_arg_sup.pat.simple_name() {
format!(" from `{}`", simple_name)
} else {
format!("")
};
let span_label_var2 = if let Some(simple_name) = anon_arg_sub.pat.simple_name() {
format!(" into `{}`", simple_name)
} else {
format!("")
};
let span_label =
format!("these two types are declared with different lifetimes...",);
(span_label, span_label_var1, span_label_var2)
}
let span_label_var1 = if let Some(simple_name) = anon_arg_sup.pat.simple_name() {
format!(" from `{}`", simple_name)
} else {
debug!("no arg with anon region found");
debug!("try_report_anon_anon_conflict: is_suitable(sub) = {:?}",
self.is_suitable_region(sub));
debug!("try_report_anon_anon_conflict: is_suitable(sup) = {:?}",
self.is_suitable_region(sup));
return false;
format!("")
};
let span_label_var2 = if let Some(simple_name) = anon_arg_sub.pat.simple_name() {
format!(" into `{}`", simple_name)
} else {
format!("")
};
let (span_1, span_2, main_label, span_label) = match (sup_is_ret_type, sub_is_ret_type) {
(None, None) => {
let (main_label_1, span_label_1) = if ty_sup == ty_sub {
(format!("this type is declared with multiple lifetimes..."),
format!("...but data{} flows{} here",
format!(" with one lifetime"),
format!(" into the other")))
} else {
(format!("these two types are declared with different lifetimes..."),
format!("...but data{} flows{} here",
span_label_var1,
span_label_var2))
};
(ty_sup.span, ty_sub.span, main_label_1, span_label_1)
}
(Some(ret_span), _) => {
(ty_sub.span,
ret_span,
format!("this parameter and the return type are declared \
with different lifetimes...",),
format!("...but data{} is returned here", span_label_var1))
}
(_, Some(ret_span)) => {
(ty_sup.span,
ret_span,
format!("this parameter and the return type are declared \
with different lifetimes...",),
format!("...but data{} is returned here", span_label_var1))
}
};
struct_span_err!(self.tcx.sess, span, E0623, "lifetime mismatch")
.span_label(ty_sup.span, main_label)
.span_label(ty_sub.span, format!(""))
.span_label(span, format!("...but data{} flows{} here", label1, label2))
.span_label(span_1, main_label)
.span_label(span_2, format!(""))
.span_label(span, span_label)
.emit();
return true;
}
@ -135,28 +151,32 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
/// ```
/// The function returns the nested type corresponding to the anonymous region
/// for e.g. `&u8` and Vec<`&u8`.
pub fn find_anon_type(&self, region: Region<'tcx>, br: &ty::BoundRegion) -> Option<&hir::Ty> {
pub fn find_anon_type(&self,
region: Region<'tcx>,
br: &ty::BoundRegion)
-> Option<(&hir::Ty, &hir::FnDecl)> {
if let Some(anon_reg) = self.is_suitable_region(region) {
let def_id = anon_reg.def_id;
if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
let inputs: &[_] = match self.tcx.hir.get(node_id) {
let fndecl = match self.tcx.hir.get(node_id) {
hir_map::NodeItem(&hir::Item { node: hir::ItemFn(ref fndecl, ..), .. }) => {
&fndecl.inputs
&fndecl
}
hir_map::NodeTraitItem(&hir::TraitItem {
node: hir::TraitItemKind::Method(ref fndecl, ..), ..
}) => &fndecl.decl.inputs,
node: hir::TraitItemKind::Method(ref m, ..), ..
}) |
hir_map::NodeImplItem(&hir::ImplItem {
node: hir::ImplItemKind::Method(ref fndecl, ..), ..
}) => &fndecl.decl.inputs,
_ => &[],
node: hir::ImplItemKind::Method(ref m, ..), ..
}) => &m.decl,
_ => return None,
};
return inputs
return fndecl
.inputs
.iter()
.filter_map(|arg| self.find_component_for_bound_region(&**arg, br))
.next();
.filter_map(|arg| self.find_component_for_bound_region(arg, br))
.next()
.map(|ty| (ty, &**fndecl));
}
}
None

View File

@ -35,15 +35,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// only introduced anonymous regions in parameters) as well as a
// version new_ty of its type where the anonymous region is replaced
// with the named one.//scope_def_id
let (named, anon_arg_info, region_info) =
let (named, anon, anon_arg_info, region_info) =
if self.is_named_region(sub) && self.is_suitable_region(sup).is_some() &&
self.find_arg_with_region(sup, sub).is_some() {
(sub,
sup,
self.find_arg_with_region(sup, sub).unwrap(),
self.is_suitable_region(sup).unwrap())
} else if self.is_named_region(sup) && self.is_suitable_region(sub).is_some() &&
self.find_arg_with_region(sub, sup).is_some() {
(sup,
sub,
self.find_arg_with_region(sub, sup).unwrap(),
self.is_suitable_region(sub).unwrap())
} else {
@ -76,33 +78,29 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
return false;
}
if self.is_return_type_anon(scope_def_id, br) {
debug!("try_report_named_anon_conflict: is_return_type_anon({:?}, {:?}) = true",
scope_def_id,
br);
return false;
} else if self.is_self_anon(is_first, scope_def_id) {
debug!("try_report_named_anon_conflict: is_self_anon({:?}, {:?}) = true",
is_first,
scope_def_id);
return false;
} else {
let (error_var, span_label_var) = if let Some(simple_name) = arg.pat.simple_name() {
(format!("the type of `{}`", simple_name), format!("the type of `{}`", simple_name))
} else {
("parameter type".to_owned(), "type".to_owned())
};
struct_span_err!(self.tcx.sess,
span,
E0621,
"explicit lifetime required in {}",
error_var)
.span_label(arg.pat.span,
format!("consider changing {} to `{}`", span_label_var, new_ty))
.span_label(span, format!("lifetime `{}` required", named))
.emit();
return true;
if let Some((_, fndecl)) = self.find_anon_type(anon, &br) {
if self.is_return_type_anon(scope_def_id, br, fndecl).is_some() ||
self.is_self_anon(is_first, scope_def_id) {
return false;
}
}
let (error_var, span_label_var) = if let Some(simple_name) = arg.pat.simple_name() {
(format!("the type of `{}`", simple_name), format!("the type of `{}`", simple_name))
} else {
("parameter type".to_owned(), "type".to_owned())
};
struct_span_err!(self.tcx.sess,
span,
E0621,
"explicit lifetime required in {}",
error_var)
.span_label(arg.pat.span,
format!("consider changing {} to `{}`", span_label_var, new_ty))
.span_label(span, format!("lifetime `{}` required", named))
.emit();
return true;
}
}

View File

@ -15,6 +15,7 @@ use infer::InferCtxt;
use ty::{self, Region, Ty};
use hir::def_id::DefId;
use hir::map as hir_map;
use syntax_pos::Span;
macro_rules! or_false {
($v:expr) => {
@ -163,7 +164,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// Here, we check for the case where the anonymous region
// is in the return type.
// FIXME(#42703) - Need to handle certain cases here.
pub fn is_return_type_anon(&self, scope_def_id: DefId, br: ty::BoundRegion) -> bool {
pub fn is_return_type_anon(&self,
scope_def_id: DefId,
br: ty::BoundRegion,
decl: &hir::FnDecl)
-> Option<Span> {
let ret_ty = self.tcx.type_of(scope_def_id);
match ret_ty.sty {
ty::TyFnDef(_, _) => {
@ -171,12 +176,12 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
let late_bound_regions = self.tcx
.collect_referenced_late_bound_regions(&sig.output());
if late_bound_regions.iter().any(|r| *r == br) {
return true;
return Some(decl.output.span());
}
}
_ => {}
}
false
None
}
// Here we check for the case where anonymous region
// corresponds to self and if yes, we display E0312.

View File

@ -34,7 +34,7 @@ fn load1<'a,'b>(a: &'a MyBox<SomeTrait>,
b: &'b MyBox<SomeTrait>)
-> &'b MyBox<SomeTrait>
{
a //~ ERROR E0312
a //~ ERROR lifetime mismatch
}
fn load2<'a>(ss: &MyBox<SomeTrait+'a>) -> MyBox<SomeTrait+'a> {

View File

@ -1,27 +1,13 @@
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
error[E0623]: lifetime mismatch
--> $DIR/ex1-return-one-existing-name-if-else-using-impl.rs:21:20
|
19 | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
| ---- -------
| |
| this parameter and the return type are declared with different lifetimes...
20 |
21 | if x > y { x } else { y }
| ^
|
note: ...the reference is valid for the lifetime 'a as defined on the method body at 19:5...
--> $DIR/ex1-return-one-existing-name-if-else-using-impl.rs:19:5
|
19 | / fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
20 | |
21 | | if x > y { x } else { y }
22 | |
23 | | }
| |_____^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 19:5
--> $DIR/ex1-return-one-existing-name-if-else-using-impl.rs:19:5
|
19 | / fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
20 | |
21 | | if x > y { x } else { y }
22 | |
23 | | }
| |_____^
| ^ ...but data from `x` is returned here
error: aborting due to previous error

View File

@ -1,27 +1,13 @@
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
error[E0623]: lifetime mismatch
--> $DIR/ex1-return-one-existing-name-return-type-is-anon.rs:18:5
|
16 | fn foo<'a>(&self, x: &'a i32) -> &i32 {
| ------- ----
| |
| this parameter and the return type are declared with different lifetimes...
17 |
18 | x
| ^
|
note: ...the reference is valid for the anonymous lifetime #1 defined on the method body at 16:3...
--> $DIR/ex1-return-one-existing-name-return-type-is-anon.rs:16:3
|
16 | / fn foo<'a>(&self, x: &'a i32) -> &i32 {
17 | |
18 | | x
19 | |
20 | | }
| |___^
note: ...but the borrowed content is only valid for the lifetime 'a as defined on the method body at 16:3
--> $DIR/ex1-return-one-existing-name-return-type-is-anon.rs:16:3
|
16 | / fn foo<'a>(&self, x: &'a i32) -> &i32 {
17 | |
18 | | x
19 | |
20 | | }
| |___^
| ^ ...but data from `x` is returned here
error: aborting due to previous error

View File

@ -1,27 +1,13 @@
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
error[E0623]: lifetime mismatch
--> $DIR/ex1-return-one-existing-name-self-is-anon.rs:18:30
|
16 | fn foo<'a>(&self, x: &'a Foo) -> &'a Foo {
| ----- -------
| |
| this parameter and the return type are declared with different lifetimes...
17 |
18 | if true { x } else { self }
| ^^^^
|
note: ...the reference is valid for the lifetime 'a as defined on the method body at 16:5...
--> $DIR/ex1-return-one-existing-name-self-is-anon.rs:16:5
|
16 | / fn foo<'a>(&self, x: &'a Foo) -> &'a Foo {
17 | |
18 | | if true { x } else { self }
19 | |
20 | | }
| |_____^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 16:5
--> $DIR/ex1-return-one-existing-name-self-is-anon.rs:16:5
|
16 | / fn foo<'a>(&self, x: &'a Foo) -> &'a Foo {
17 | |
18 | | if true { x } else { self }
19 | |
20 | | }
| |_____^
| ^^^^ ...but data from `self` is returned here
error: aborting due to previous error

View File

@ -4,7 +4,7 @@ error[E0623]: lifetime mismatch
15 | fn foo(mut x: Ref) {
| ---
| |
| this type was declared with multiple lifetimes...
| this type is declared with multiple lifetimes...
16 | x.a = x.b;
| ^^^ ...but data with one lifetime flows into the other here

View File

@ -4,7 +4,7 @@ error[E0623]: lifetime mismatch
15 | fn foo(mut x: Ref) {
| ---
| |
| this type was declared with multiple lifetimes...
| this type is declared with multiple lifetimes...
16 | x.a = x.b;
| ^^^ ...but data with one lifetime flows into the other here

View File

@ -1,23 +1,12 @@
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
error[E0623]: lifetime mismatch
--> $DIR/ex3-both-anon-regions-return-type-is-anon.rs:17:5
|
16 | fn foo<'a>(&self, x: &i32) -> &i32 {
| ---- ----
| |
| this parameter and the return type are declared with different lifetimes...
17 | x
| ^
|
note: ...the reference is valid for the anonymous lifetime #1 defined on the method body at 16:3...
--> $DIR/ex3-both-anon-regions-return-type-is-anon.rs:16:3
|
16 | / fn foo<'a>(&self, x: &i32) -> &i32 {
17 | | x
18 | | }
| |___^
note: ...but the borrowed content is only valid for the anonymous lifetime #2 defined on the method body at 16:3
--> $DIR/ex3-both-anon-regions-return-type-is-anon.rs:16:3
|
16 | / fn foo<'a>(&self, x: &i32) -> &i32 {
17 | | x
18 | | }
| |___^
| ^ ...but data from `x` is returned here
error: aborting due to previous error

View File

@ -1,23 +1,12 @@
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
error[E0623]: lifetime mismatch
--> $DIR/ex3-both-anon-regions-self-is-anon.rs:17:19
|
16 | fn foo<'a>(&self, x: &Foo) -> &Foo {
| ---- ----
| |
| this parameter and the return type are declared with different lifetimes...
17 | if true { x } else { self }
| ^
|
note: ...the reference is valid for the anonymous lifetime #1 defined on the method body at 16:5...
--> $DIR/ex3-both-anon-regions-self-is-anon.rs:16:5
|
16 | / fn foo<'a>(&self, x: &Foo) -> &Foo {
17 | | if true { x } else { self }
18 | | }
| |_____^
note: ...but the borrowed content is only valid for the anonymous lifetime #2 defined on the method body at 16:5
--> $DIR/ex3-both-anon-regions-self-is-anon.rs:16:5
|
16 | / fn foo<'a>(&self, x: &Foo) -> &Foo {
17 | | if true { x } else { self }
18 | | }
| |_____^
| ^ ...but data from `x` is returned here
error: aborting due to previous error