Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend E0623 for LateBound and EarlyBound Regions #44079

Merged
merged 2 commits into from
Sep 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 48 additions & 12 deletions src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1389,30 +1389,66 @@ A lifetime of reference outlives lifetime of borrowed content.
Erroneous code example:

```compile_fail,E0312
fn make_child<'human, 'elve>(x: &mut &'human isize, y: &mut &'elve isize) {
*x = *y;
// error: lifetime of reference outlives lifetime of borrowed content
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 compiler cannot determine if the `human` lifetime will live long enough
to keep up on the elve one. To solve this error, you have to give an
explicit lifetime hierarchy:
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, 'elve: 'human>(x: &mut &'human isize,
y: &mut &'elve isize) {
*x = *y; // ok!
fn make_child<'human>(
x: &'human i32,
y: &'human i32
) -> &'human i32 {
if x > y
{ x }
else
{ y } // ok!
}
```

Or use the same lifetime for every variable:
However, in some cases, you may prefer to explicitly declare that one lifetime
outlives another using a `where` clause:

```
fn make_child<'elve>(x: &mut &'elve isize, y: &mut &'elve isize) {
*x = *y; // ok!
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##"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
};

// Determine whether the sub and sup consist of both anonymous (elided) regions.
let anon_reg_sup = or_false!(self.is_suitable_anonymous_region(sup));
let anon_reg_sup = or_false!(self.is_suitable_region(sup));

let anon_reg_sub = or_false!(self.is_suitable_anonymous_region(sub));
let anon_reg_sub = or_false!(self.is_suitable_region(sub));
let scope_def_id_sup = anon_reg_sup.def_id;
let bregion_sup = anon_reg_sup.boundregion;
let scope_def_id_sub = anon_reg_sub.def_id;
Expand All @@ -57,10 +57,17 @@ 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,
bregion_sup);
debug!("try_report_anon_anon_conflict: found_arg2={:?} sub={:?} br2={:?}",
ty_sup,
sub,
bregion_sub);

let (main_label, label1, label2) = if let (Some(sup_arg), Some(sub_arg)) =
(self.find_arg_with_anonymous_region(sup, sup),
self.find_arg_with_anonymous_region(sub, sub)) {
(self.find_arg_with_region(sup, sup), self.find_arg_with_region(sub, 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);
Expand Down Expand Up @@ -97,6 +104,11 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
(span_label, span_label_var1, span_label_var2)
}
} 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;
};

Expand Down Expand Up @@ -124,35 +136,27 @@ 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> {
if let Some(anon_reg) = self.is_suitable_anonymous_region(region) {
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 ret_ty = self.tcx.type_of(def_id);
if let ty::TyFnDef(_, _) = ret_ty.sty {
let inputs: &[_] =
match self.tcx.hir.get(node_id) {
hir_map::NodeItem(&hir::Item {
node: hir::ItemFn(ref fndecl, ..), ..
}) => &fndecl.inputs,
hir_map::NodeTraitItem(&hir::TraitItem {
node: hir::TraitItemKind::Method(ref fndecl, ..),
..
}) => &fndecl.decl.inputs,
hir_map::NodeImplItem(&hir::ImplItem {
node: hir::ImplItemKind::Method(ref fndecl, ..),
..
}) => &fndecl.decl.inputs,
let inputs: &[_] = match self.tcx.hir.get(node_id) {
hir_map::NodeItem(&hir::Item { node: hir::ItemFn(ref fndecl, ..), .. }) => {
&fndecl.inputs
}
hir_map::NodeTraitItem(&hir::TraitItem {
node: hir::TraitItemKind::Method(ref fndecl, ..), ..
}) => &fndecl.decl.inputs,
hir_map::NodeImplItem(&hir::ImplItem {
node: hir::ImplItemKind::Method(ref fndecl, ..), ..
}) => &fndecl.decl.inputs,

_ => &[],
};
_ => &[],
};

return inputs
.iter()
.filter_map(|arg| {
self.find_component_for_bound_region(&**arg, br)
})
.next();
}
return inputs
.iter()
.filter_map(|arg| self.find_component_for_bound_region(&**arg, br))
.next();
}
}
None
Expand Down Expand Up @@ -199,30 +203,62 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
}

fn visit_ty(&mut self, arg: &'gcx hir::Ty) {
// Find the index of the anonymous region that was part of the
// error. We will then search the function parameters for a bound
// region at the right depth with the same index.
let br_index = match self.bound_region {
ty::BrAnon(index) => index,
_ => return,
};

match arg.node {
hir::TyRptr(ref lifetime, _) => {
// the lifetime of the TyRptr
let hir_id = self.infcx.tcx.hir.node_to_hir_id(lifetime.id);
match self.infcx.tcx.named_region(hir_id) {
// the lifetime of the TyRptr
Some(rl::Region::LateBoundAnon(debruijn_index, anon_index)) => {
match (self.infcx.tcx.named_region(hir_id), self.bound_region) {
// Find the index of the anonymous region that was part of the
// error. We will then search the function parameters for a bound
// region at the right depth with the same index
(Some(rl::Region::LateBoundAnon(debruijn_index, anon_index)),
ty::BrAnon(br_index)) => {
debug!("LateBoundAnon depth = {:?} anon_index = {:?} br_index={:?}",
debruijn_index.depth,
anon_index,
br_index);
if debruijn_index.depth == 1 && anon_index == br_index {
self.found_type = Some(arg);
return; // we can stop visiting now
}
}
Some(rl::Region::Static) |
Some(rl::Region::EarlyBound(_, _)) |
Some(rl::Region::LateBound(_, _)) |
Some(rl::Region::Free(_, _)) |
None => {

// Find the index of the named region that was part of the
// error. We will then search the function parameters for a bound
// region at the right depth with the same index
(Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => {
debug!("EarlyBound self.infcx.tcx.hir.local_def_id(id)={:?} \
def_id={:?}",
self.infcx.tcx.hir.local_def_id(id),
def_id);
if self.infcx.tcx.hir.local_def_id(id) == def_id {
self.found_type = Some(arg);
return; // we can stop visiting now
}
}

// Find the index of the named region that was part of the
// error. We will then search the function parameters for a bound
// region at the right depth with the same index
(Some(rl::Region::LateBound(debruijn_index, id)), ty::BrNamed(def_id, _)) => {
debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}",
debruijn_index.depth);
debug!("self.infcx.tcx.hir.local_def_id(id)={:?}",
self.infcx.tcx.hir.local_def_id(id));
debug!("def_id={:?}", def_id);
if debruijn_index.depth == 1 &&
self.infcx.tcx.hir.local_def_id(id) == def_id {
self.found_type = Some(arg);
return; // we can stop visiting now
}
}

(Some(rl::Region::Static), _) |
(Some(rl::Region::Free(_, _)), _) |
(Some(rl::Region::EarlyBound(_, _)), _) |
(Some(rl::Region::LateBound(_, _)), _) |
(Some(rl::Region::LateBoundAnon(_, _)), _) |
(None, _) => {
debug!("no arg found");
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ mod need_type_info;
mod named_anon_conflict;
#[macro_use]
mod util;
mod anon_anon_conflict;
mod different_lifetimes;

impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
pub fn note_and_explain_region(self,
Expand Down
50 changes: 39 additions & 11 deletions src/librustc/infer/error_reporting/named_anon_conflict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use infer::InferCtxt;
use infer::region_inference::RegionResolutionError::*;
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
Expand All @@ -24,39 +25,68 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
_ => return false, // inapplicable
};

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
// where the anonymous region appears (there must always be one; we
// 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) =
if sub.is_named_region() && self.is_suitable_anonymous_region(sup).is_some() {
if self.is_named_region(sub) && self.is_suitable_region(sup).is_some() &&
self.find_arg_with_region(sup, sub).is_some() {
(sub,
self.find_arg_with_anonymous_region(sup, sub).unwrap(),
self.is_suitable_anonymous_region(sup).unwrap())
} else if sup.is_named_region() && self.is_suitable_anonymous_region(sub).is_some() {
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,
self.find_arg_with_anonymous_region(sub, sup).unwrap(),
self.is_suitable_anonymous_region(sub).unwrap())
self.find_arg_with_region(sub, sup).unwrap(),
self.is_suitable_region(sub).unwrap())
} else {
return false; // inapplicable
};

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);

let (arg, new_ty, br, is_first, scope_def_id, is_impl_item) = (anon_arg_info.arg,
anon_arg_info.arg_ty,
anon_arg_info.bound_region,
anon_arg_info.is_first,
region_info.def_id,
region_info.is_impl_item);
match br {
ty::BrAnon(_) => {}
_ => {
/* not an anonymous region */
debug!("try_report_named_anon_conflict: not an anonymous region");
return false;
}
}

if is_impl_item {
debug!("try_report_named_anon_conflict: impl item, bail out");
return false;
}

if self.is_return_type_anon(scope_def_id, br) || self.is_self_anon(is_first, scope_def_id) {
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 {
Expand All @@ -72,9 +102,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
format!("consider changing {} to `{}`", span_label_var, new_ty))
.span_label(span, format!("lifetime `{}` required", named))
.emit();


return true;
}
return true;
}
}
Loading