Skip to content

Commit fe9c68a

Browse files
committed
Adding E0623 for structs
1 parent e324594 commit fe9c68a

32 files changed

+621
-177
lines changed

src/librustc/infer/error_reporting/anon_anon_conflict.rs

Lines changed: 168 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -27,65 +27,83 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
2727
// { x.push(y); }.
2828
// The example gives
2929
// fn foo(x: &mut Vec<&u8>, y: &u8) {
30-
// --- --- these references must have the same lifetime
30+
// --- --- these references are declared with different lifetimes...
3131
// x.push(y);
32-
// ^ data from `y` flows into `x` here
33-
// It will later be extended to trait objects and structs.
32+
// ^ ...but data from `y` flows into `x` here
33+
// It has been extended for the case of structs too.
34+
// Consider the example
35+
// struct Ref<'a> { x: &'a u32 }
36+
// fn foo(mut x: Vec<Ref>, y: Ref) {
37+
// --- --- these structs are declared with different lifetimes...
38+
// x.push(y);
39+
// ^ ...but data from `y` flows into `x` here
40+
// }
41+
// It will later be extended to trait objects.
3442
pub fn try_report_anon_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool {
35-
3643
let (span, sub, sup) = match *error {
3744
ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup),
3845
_ => return false, // inapplicable
3946
};
4047

4148
// Determine whether the sub and sup consist of both anonymous (elided) regions.
42-
let (ty1, ty2) = if self.is_suitable_anonymous_region(sup).is_some() &&
43-
self.is_suitable_anonymous_region(sub).is_some() {
44-
if let (Some(anon_reg1), Some(anon_reg2)) =
45-
(self.is_suitable_anonymous_region(sup), self.is_suitable_anonymous_region(sub)) {
46-
let ((_, br1), (_, br2)) = (anon_reg1, anon_reg2);
47-
if self.find_anon_type(sup, &br1).is_some() &&
48-
self.find_anon_type(sub, &br2).is_some() {
49-
(self.find_anon_type(sup, &br1).unwrap(),
50-
self.find_anon_type(sub, &br2).unwrap())
49+
let (ty_sup, ty_sub, scope_def_id_sup, scope_def_id_sub, bregion_sup, bregion_sub) =
50+
if let (Some(anon_reg_sup), Some(anon_reg_sub)) =
51+
(self.is_suitable_anonymous_region(sup, true),
52+
self.is_suitable_anonymous_region(sub, true)) {
53+
let ((def_id_sup, br_sup), (def_id_sub, br_sub)) = (anon_reg_sup, anon_reg_sub);
54+
if let (Some(anonarg_sup), Some(anonarg_sub)) =
55+
(self.find_anon_type(sup, &br_sup), self.find_anon_type(sub, &br_sub)) {
56+
(anonarg_sup, anonarg_sub, def_id_sup, def_id_sub, br_sup, br_sub)
5157
} else {
5258
return false;
5359
}
5460
} else {
5561
return false;
56-
}
57-
} else {
58-
return false; // inapplicable
59-
};
62+
};
6063

61-
if let (Some(sup_arg), Some(sub_arg)) =
64+
let (label1, label2) = if let (Some(sup_arg), Some(sub_arg)) =
6265
(self.find_arg_with_anonymous_region(sup, sup),
6366
self.find_arg_with_anonymous_region(sub, sub)) {
64-
let ((anon_arg1, _, _, _), (anon_arg2, _, _, _)) = (sup_arg, sub_arg);
6567

66-
let span_label_var1 = if let Some(simple_name) = anon_arg1.pat.simple_name() {
67-
format!(" from `{}` ", simple_name)
68-
} else {
69-
format!(" ")
70-
};
68+
let ((anon_arg_sup, _, _, is_first_sup), (anon_arg_sub, _, _, is_first_sub)) =
69+
(sup_arg, sub_arg);
70+
if self.is_self_anon(is_first_sup, scope_def_id_sup) ||
71+
self.is_self_anon(is_first_sub, scope_def_id_sub) {
72+
return false;
73+
}
74+
75+
if self.is_return_type_anon(scope_def_id_sup, bregion_sup) ||
76+
self.is_return_type_anon(scope_def_id_sub, bregion_sub) {
77+
return false;
78+
}
7179

72-
let span_label_var2 = if let Some(simple_name) = anon_arg2.pat.simple_name() {
73-
format!(" into `{}` ", simple_name)
80+
if anon_arg_sup == anon_arg_sub {
81+
(format!(" with one lifetime"), format!(" into the other"))
7482
} else {
75-
format!(" ")
76-
};
83+
let span_label_var1 = if let Some(simple_name) = anon_arg_sup.pat.simple_name() {
84+
format!(" from `{}`", simple_name)
85+
} else {
86+
format!("")
87+
};
88+
89+
let span_label_var2 = if let Some(simple_name) = anon_arg_sub.pat.simple_name() {
90+
format!(" into `{}`", simple_name)
91+
} else {
92+
format!("")
93+
};
7794

78-
struct_span_err!(self.tcx.sess, span, E0623, "lifetime mismatch")
79-
.span_label(ty1.span,
80-
format!("these references are not declared with the same lifetime..."))
81-
.span_label(ty2.span, format!(""))
82-
.span_label(span,
83-
format!("...but data{}flows{}here", span_label_var1, span_label_var2))
84-
.emit();
95+
(span_label_var1, span_label_var2)
96+
}
8597
} else {
8698
return false;
87-
}
99+
};
88100

101+
struct_span_err!(self.tcx.sess, span, E0623, "lifetime mismatch")
102+
.span_label(ty_sup.span,
103+
format!("these two types are declared with different lifetimes..."))
104+
.span_label(ty_sub.span, format!(""))
105+
.span_label(span, format!("...but data{} flows{} here", label1, label2))
106+
.emit();
89107
return true;
90108
}
91109

@@ -94,7 +112,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
94112
/// contains the anonymous type.
95113
///
96114
/// # Arguments
97-
///
98115
/// region - the anonymous region corresponding to the anon_anon conflict
99116
/// br - the bound region corresponding to the above region which is of type `BrAnon(_)`
100117
///
@@ -105,8 +122,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
105122
/// ```
106123
/// The function returns the nested type corresponding to the anonymous region
107124
/// for e.g. `&u8` and Vec<`&u8`.
108-
fn find_anon_type(&self, region: Region<'tcx>, br: &ty::BoundRegion) -> Option<&hir::Ty> {
109-
if let Some(anon_reg) = self.is_suitable_anonymous_region(region) {
125+
pub fn find_anon_type(&self, region: Region<'tcx>, br: &ty::BoundRegion) -> Option<&hir::Ty> {
126+
if let Some(anon_reg) = self.is_suitable_anonymous_region(region, true) {
110127
let (def_id, _) = anon_reg;
111128
if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
112129
let ret_ty = self.tcx.type_of(def_id);
@@ -117,19 +134,33 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
117134
.inputs
118135
.iter()
119136
.filter_map(|arg| {
120-
let mut nested_visitor = FindNestedTypeVisitor {
121-
infcx: &self,
122-
hir_map: &self.tcx.hir,
123-
bound_region: *br,
124-
found_type: None,
125-
};
126-
nested_visitor.visit_ty(&**arg);
127-
if nested_visitor.found_type.is_some() {
128-
nested_visitor.found_type
129-
} else {
130-
None
131-
}
132-
})
137+
self.find_component_for_bound_region(&**arg,
138+
br)
139+
})
140+
.next();
141+
}
142+
} else if let hir_map::NodeTraitItem(it) = self.tcx.hir.get(node_id) {
143+
if let hir::TraitItemKind::Method(ref fndecl, _) = it.node {
144+
return fndecl
145+
.decl
146+
.inputs
147+
.iter()
148+
.filter_map(|arg| {
149+
self.find_component_for_bound_region(&**arg,
150+
br)
151+
})
152+
.next();
153+
}
154+
} else if let hir_map::NodeImplItem(it) = self.tcx.hir.get(node_id) {
155+
if let hir::ImplItemKind::Method(ref fndecl, _) = it.node {
156+
return fndecl
157+
.decl
158+
.inputs
159+
.iter()
160+
.filter_map(|arg| {
161+
self.find_component_for_bound_region(&**arg,
162+
br)
163+
})
133164
.next();
134165
}
135166
}
@@ -138,6 +169,22 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
138169
}
139170
None
140171
}
172+
173+
// This method creates a FindNestedTypeVisitor which returns the type corresponding
174+
// to the anonymous region.
175+
fn find_component_for_bound_region(&self,
176+
arg: &'gcx hir::Ty,
177+
br: &ty::BoundRegion)
178+
-> Option<(&'gcx hir::Ty)> {
179+
let mut nested_visitor = FindNestedTypeVisitor {
180+
infcx: &self,
181+
hir_map: &self.tcx.hir,
182+
bound_region: *br,
183+
found_type: None,
184+
};
185+
nested_visitor.visit_ty(arg);
186+
nested_visitor.found_type
187+
}
141188
}
142189

143190
// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
@@ -176,8 +223,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
176223
hir::TyRptr(ref lifetime, _) => {
177224
match self.infcx.tcx.named_region_map.defs.get(&lifetime.id) {
178225
// the lifetime of the TyRptr
179-
Some(&rl::Region::LateBoundAnon(debuijn_index, anon_index)) => {
180-
if debuijn_index.depth == 1 && anon_index == br_index {
226+
Some(&rl::Region::LateBoundAnon(debruijn_index, anon_index)) => {
227+
if debruijn_index.depth == 1 && anon_index == br_index {
181228
self.found_type = Some(arg);
182229
return; // we can stop visiting now
183230
}
@@ -191,10 +238,77 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
191238
}
192239
}
193240
}
241+
// Checks if it is of type `hir::TyPath` which corresponds to a struct.
242+
hir::TyPath(_) => {
243+
let subvisitor = &mut TyPathVisitor {
244+
infcx: self.infcx,
245+
found_it: false,
246+
bound_region: self.bound_region,
247+
hir_map: self.hir_map,
248+
};
249+
intravisit::walk_ty(subvisitor, arg); // call walk_ty; as visit_ty is empty,
250+
// this will visit only outermost type
251+
if subvisitor.found_it {
252+
self.found_type = Some(arg);
253+
}
254+
}
194255
_ => {}
195256
}
196257
// walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
197258
// go on to visit `&Foo`
198259
intravisit::walk_ty(self, arg);
199260
}
200261
}
262+
263+
// The visitor captures the corresponding `hir::Ty` of the anonymous region
264+
// in the case of structs ie. `hir::TyPath`.
265+
// This visitor would be invoked for each lifetime corresponding to a struct,
266+
// and would walk the types like Vec<Ref> in the above example and Ref looking for the HIR
267+
// where that lifetime appears. This allows us to highlight the
268+
// specific part of the type in the error message.
269+
struct TyPathVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
270+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
271+
hir_map: &'a hir::map::Map<'gcx>,
272+
found_it: bool,
273+
bound_region: ty::BoundRegion,
274+
}
275+
276+
impl<'a, 'gcx, 'tcx> Visitor<'gcx> for TyPathVisitor<'a, 'gcx, 'tcx> {
277+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
278+
NestedVisitorMap::OnlyBodies(&self.hir_map)
279+
}
280+
281+
fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
282+
let br_index = match self.bound_region {
283+
ty::BrAnon(index) => index,
284+
_ => return,
285+
};
286+
287+
match self.infcx.tcx.named_region_map.defs.get(&lifetime.id) {
288+
// the lifetime of the TyPath!
289+
Some(&rl::Region::LateBoundAnon(debruijn_index, anon_index)) => {
290+
if debruijn_index.depth == 1 && anon_index == br_index {
291+
self.found_it = true;
292+
}
293+
}
294+
Some(&rl::Region::Static) |
295+
Some(&rl::Region::EarlyBound(_, _)) |
296+
Some(&rl::Region::LateBound(_, _)) |
297+
Some(&rl::Region::Free(_, _)) |
298+
None => {
299+
debug!("no arg found");
300+
}
301+
}
302+
}
303+
304+
fn visit_ty(&mut self, arg: &'gcx hir::Ty) {
305+
// ignore nested types
306+
//
307+
// If you have a type like `Foo<'a, &Ty>` we
308+
// are only interested in the immediate lifetimes ('a).
309+
//
310+
// Making `visit_ty` empty will ignore the `&Ty` embedded
311+
// inside, it will get reached by the outer visitor.
312+
debug!("`Ty` corresponding to a struct is {:?}", arg);
313+
}
314+
}

0 commit comments

Comments
 (0)