Skip to content

Improve error message for one named, one anonymous lifetime parameters - underline Type #43851

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

Closed
wants to merge 2 commits into from
Closed
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
227 changes: 136 additions & 91 deletions src/librustc/infer/error_reporting/anon_anon_conflict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@
//! where both the regions are anonymous.
use hir;
use infer::InferCtxt;
use ty::{self, Region};
use ty;
use infer::region_inference::RegionResolutionError::*;
use infer::region_inference::RegionResolutionError;
use hir::map as hir_map;
use middle::resolve_lifetime as rl;
use hir::intravisit::{self, Visitor, NestedVisitorMap};

Expand All @@ -27,116 +26,94 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// { x.push(y); }.
// The example gives
// fn foo(x: &mut Vec<&u8>, y: &u8) {
// --- --- these references must have the same lifetime
// --- --- these references are declared with different lifetimes...
// x.push(y);
// ^ data from `y` flows into `x` here
// It will later be extended to trait objects and structs.
// ^ ...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.
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),
_ => return false, // inapplicable
};

// Determine whether the sub and sup consist of both anonymous (elided) regions.
let (ty1, ty2) = if self.is_suitable_anonymous_region(sup).is_some() &&
self.is_suitable_anonymous_region(sub).is_some() {
let (ty1, ty2, scope_def_id_1, scope_def_id_2, bregion1, bregion2) = if
self.is_suitable_anonymous_region(sup, true).is_some() &&
self.is_suitable_anonymous_region(sub, true).is_some() {
if let (Some(anon_reg1), Some(anon_reg2)) =
(self.is_suitable_anonymous_region(sup), self.is_suitable_anonymous_region(sub)) {
let ((_, br1), (_, br2)) = (anon_reg1, anon_reg2);
if self.find_anon_type(sup, &br1).is_some() &&
self.find_anon_type(sub, &br2).is_some() {
(self.find_anon_type(sup, &br1).unwrap(),
self.find_anon_type(sub, &br2).unwrap())
} else {
return false;
(self.is_suitable_anonymous_region(sup, true),
self.is_suitable_anonymous_region(sub, true)) {
let ((def_id1, br1), (def_id2, br2)) = (anon_reg1, anon_reg2);
let found_arg1 = self.find_anon_type(sup, &br1);
let found_arg2 = self.find_anon_type(sub, &br2);
match (found_arg1, found_arg2) {
(Some(anonarg_1), Some(anonarg_2)) => {
(anonarg_1, anonarg_2, def_id1, def_id2, br1, br2)
}
_ => {
return false;
}
}

} else {
return false;
}
} else {
return false; // inapplicable
return false; //inapplicable
};

if let (Some(sup_arg), Some(sub_arg)) =
let (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)) {
let ((anon_arg1, _, _, _), (anon_arg2, _, _, _)) = (sup_arg, sub_arg);

let span_label_var1 = if let Some(simple_name) = anon_arg1.pat.simple_name() {
format!(" from `{}` ", simple_name)
} else {
format!(" ")
};
let ((anon_arg1, _, _, is_first1), (anon_arg2, _, _, is_first2)) = (sup_arg, sub_arg);
if self.is_self_anon(is_first1, scope_def_id_1) ||
self.is_self_anon(is_first2, scope_def_id_2) {
return false;
}

if self.is_return_type_anon(scope_def_id_1, bregion1) ||
self.is_return_type_anon(scope_def_id_2, bregion2) {
return false;
}

let span_label_var2 = if let Some(simple_name) = anon_arg2.pat.simple_name() {
format!(" into `{}` ", simple_name)
if anon_arg1 == anon_arg2 {
(format!(" with one lifetime"), format!(" into the other"))
} else {
format!(" ")
};

struct_span_err!(self.tcx.sess, span, E0623, "lifetime mismatch")
.span_label(ty1.span,
format!("these references are not declared with the same lifetime..."))
.span_label(ty2.span, format!(""))
.span_label(span,
format!("...but data{}flows{}here", span_label_var1, span_label_var2))
.emit();
let span_label_var1 = if let Some(simple_name) = anon_arg1.pat.simple_name() {
format!(" from `{}`", simple_name)
} else {
format!("")
};

let span_label_var2 = if let Some(simple_name) = anon_arg2.pat.simple_name() {
format!(" into `{}`", simple_name)
} else {
format!("")
};

(span_label_var1, span_label_var2)
}
} else {
return false;
}
};

struct_span_err!(self.tcx.sess, span, E0623, "lifetime mismatch")
.span_label(ty1.span,
format!("these two types are declared with different lifetimes..."))
.span_label(ty2.span, format!(""))
.span_label(span, format!("...but data{} flows{} here", label1, label2))
.emit();
return true;
}

/// This function calls the `visit_ty` method for the parameters
/// corresponding to the anonymous regions. The `nested_visitor.found_type`
/// contains the anonymous type.
///
/// # Arguments
///
/// region - the anonymous region corresponding to the anon_anon conflict
/// br - the bound region corresponding to the above region which is of type `BrAnon(_)`
///
/// # Example
/// ```
/// fn foo(x: &mut Vec<&u8>, y: &u8)
/// { x.push(y); }
/// ```
/// The function returns the nested type corresponding to the anonymous region
/// for e.g. `&u8` and Vec<`&u8`.
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) {
let (def_id, _) = anon_reg;
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 {
if let hir_map::NodeItem(it) = self.tcx.hir.get(node_id) {
if let hir::ItemFn(ref fndecl, _, _, _, _, _) = it.node {
return fndecl
.inputs
.iter()
.filter_map(|arg| {
let mut nested_visitor = FindNestedTypeVisitor {
infcx: &self,
hir_map: &self.tcx.hir,
bound_region: *br,
found_type: None,
};
nested_visitor.visit_ty(&**arg);
if nested_visitor.found_type.is_some() {
nested_visitor.found_type
} else {
None
}
})
.next();
}
}
}
}
}
None
}
}

Expand All @@ -147,15 +124,15 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// walk the types like &mut Vec<&u8> and &u8 looking for the HIR
// where that lifetime appears. This allows us to highlight the
// specific part of the type in the error message.
struct FindNestedTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
hir_map: &'a hir::map::Map<'gcx>,
pub struct FindNestedTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
pub infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
pub hir_map: &'a hir::map::Map<'gcx>,
// The bound_region corresponding to the Refree(freeregion)
// associated with the anonymous region we are looking for.
bound_region: ty::BoundRegion,
pub bound_region: ty::BoundRegion,
// The type where the anonymous lifetime appears
// for e.g. Vec<`&u8`> and <`&u8`>
found_type: Option<&'gcx hir::Ty>,
pub found_type: Option<&'gcx hir::Ty>,
}

impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
Expand Down Expand Up @@ -191,10 +168,78 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
}
}
}
// Checks if it is of type `hir::TyPath` which corresponds to a struct.
hir::TyPath(_) => {
let subvisitor = &mut TyPathVisitor {
infcx: self.infcx,
found_it: false,
bound_region: self.bound_region,
hir_map: self.hir_map,
};
intravisit::walk_ty(subvisitor, arg); // call walk_ty; as visit_ty is empty,
// this will visit only outermost type
if subvisitor.found_it {
self.found_type = Some(arg);
}
}
_ => {}
}
// walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
// go on to visit `&Foo`
intravisit::walk_ty(self, arg);
}
}

// The visitor captures the corresponding `hir::Ty` of the anonymous region
// in the case of structs ie. `hir::TyPath`.
// This visitor would be invoked for each lifetime corresponding to a struct,
// and would walk the types like Vec<Ref> in the above example and Ref looking for the HIR
// where that lifetime appears. This allows us to highlight the
// specific part of the type in the error message.
struct TyPathVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
hir_map: &'a hir::map::Map<'gcx>,
found_it: bool,
bound_region: ty::BoundRegion,
}

impl<'a, 'gcx, 'tcx> Visitor<'gcx> for TyPathVisitor<'a, 'gcx, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
NestedVisitorMap::OnlyBodies(&self.hir_map)
}

fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
let br_index = match self.bound_region {
ty::BrAnon(index) => index,
_ => return,
};


match self.infcx.tcx.named_region_map.defs.get(&lifetime.id) {
// the lifetime of the TyPath!
Some(&rl::Region::LateBoundAnon(debuijn_index, anon_index)) => {
if debuijn_index.depth == 1 && anon_index == br_index {
self.found_it = true;
}
}
Some(&rl::Region::Static) |
Some(&rl::Region::EarlyBound(_, _)) |
Some(&rl::Region::LateBound(_, _)) |
Some(&rl::Region::Free(_, _)) |
None => {
debug!("no arg found");
}
}
}

fn visit_ty(&mut self, arg: &'gcx hir::Ty) {
// ignore nested types
//
// If you have a type like `Foo<'a, &Ty>` we
// are only interested in the immediate lifetimes ('a).
//
// Making `visit_ty` empty will ignore the `&Ty` embedded
// inside, it will get reached by the outer visitor.
debug!("`Ty` corresponding to a struct is {:?}", arg);
}
}
3 changes: 2 additions & 1 deletion src/librustc/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -963,4 +963,5 @@ impl<'tcx> ObligationCause<'tcx> {
_ => "types are compatible",
}
}
}

}
Loading