Skip to content

Commit

Permalink
Rollup merge of rust-lang#44124 - gaurikholkar:return_self, r=arielb1
Browse files Browse the repository at this point in the history
adding E0623 for return types - both parameters are anonymous

This is a fix for rust-lang#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.
  • Loading branch information
Mark-Simulacrum authored Sep 29, 2017
2 parents 6f87d20 + 73543d5 commit f407b2b
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 249 deletions.
69 changes: 1 addition & 68 deletions src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -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
Expand Down
122 changes: 71 additions & 51 deletions src/librustc/infer/error_reporting/different_lifetimes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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_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_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 {

let span_label_var2 = if let Some(simple_name) = anon_arg_sub.pat.simple_name() {
format!(" into `{}`", simple_name)
(format!("this type is declared with multiple lifetimes..."),
format!("...but data{} flows{} here",
format!(" with one lifetime"),
format!(" into the other")))
} else {
format!("")
(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)
}

let span_label =
format!("these two types are declared with different lifetimes...",);

(span_label, span_label_var1, span_label_var2)
(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))
}
} 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;
};


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;
}
Expand All @@ -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
Expand Down
52 changes: 25 additions & 27 deletions src/librustc/infer/error_reporting/named_anon_conflict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
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 {
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())
};
("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;

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;
}
}
}
11 changes: 8 additions & 3 deletions src/librustc/infer/error_reporting/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -163,20 +164,24 @@ 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(_, _) => {
let sig = ret_ty.fn_sig(self.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.
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/object-lifetime-default-mybox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
Loading

0 comments on commit f407b2b

Please sign in to comment.