Skip to content

Commit a4d1149

Browse files
committed
Auto merge of #43700 - gaurikholkar:struct_lifetimes, r=nikomatsakis
Adding E0623 for structs This is a fix to #43275 The error message is ``` +error[E0623]: lifetime mismatch + --> $DIR/ex3-both-anon-regions-both-are-structs.rs:15:12 + | +14 | fn foo(mut x: Vec<Ref>, y: Ref) { + | --- --- these structs are declared with different lifetimes... +15 | x.push(y); + | ^ ...but data from `y` flows into `x` here + +error: aborting due to previous error ``` r? @nikomatsakis
2 parents ba65645 + 2cd1318 commit a4d1149

29 files changed

+624
-204
lines changed

src/librustc/infer/error_reporting/anon_anon_conflict.rs

+171-69
Original file line numberDiff line numberDiff line change
@@ -27,65 +27,84 @@ 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())
51-
} else {
52-
return false;
53-
}
54-
} else {
55-
return false;
56-
}
57-
} else {
58-
return false; // inapplicable
59-
};
49+
let anon_reg_sup = or_false!(self.is_suitable_anonymous_region(sup));
6050

61-
if let (Some(sup_arg), Some(sub_arg)) =
51+
let anon_reg_sub = or_false!(self.is_suitable_anonymous_region(sub));
52+
let scope_def_id_sup = anon_reg_sup.def_id;
53+
let bregion_sup = anon_reg_sup.boundregion;
54+
let scope_def_id_sub = anon_reg_sub.def_id;
55+
let bregion_sub = anon_reg_sub.boundregion;
56+
57+
let ty_sup = or_false!(self.find_anon_type(sup, &bregion_sup));
58+
59+
let ty_sub = or_false!(self.find_anon_type(sub, &bregion_sub));
60+
61+
let (main_label, label1, label2) = if let (Some(sup_arg), Some(sub_arg)) =
6262
(self.find_arg_with_anonymous_region(sup, sup),
6363
self.find_arg_with_anonymous_region(sub, sub)) {
64-
let ((anon_arg1, _, _, _), (anon_arg2, _, _, _)) = (sup_arg, sub_arg);
6564

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-
};
65+
let (anon_arg_sup, is_first_sup, anon_arg_sub, is_first_sub) =
66+
(sup_arg.arg, sup_arg.is_first, sub_arg.arg, sub_arg.is_first);
67+
if self.is_self_anon(is_first_sup, scope_def_id_sup) ||
68+
self.is_self_anon(is_first_sub, scope_def_id_sub) {
69+
return false;
70+
}
7171

72-
let span_label_var2 = if let Some(simple_name) = anon_arg2.pat.simple_name() {
73-
format!(" into `{}` ", simple_name)
72+
if self.is_return_type_anon(scope_def_id_sup, bregion_sup) ||
73+
self.is_return_type_anon(scope_def_id_sub, bregion_sub) {
74+
return false;
75+
}
76+
77+
if anon_arg_sup == anon_arg_sub {
78+
(format!("this type was declared with multiple lifetimes..."),
79+
format!(" with one lifetime"),
80+
format!(" into the other"))
7481
} else {
75-
format!(" ")
76-
};
77-
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();
82+
let span_label_var1 = if let Some(simple_name) = anon_arg_sup.pat.simple_name() {
83+
format!(" from `{}`", simple_name)
84+
} else {
85+
format!("")
86+
};
87+
88+
let span_label_var2 = if let Some(simple_name) = anon_arg_sub.pat.simple_name() {
89+
format!(" into `{}`", simple_name)
90+
} else {
91+
format!("")
92+
};
93+
94+
let span_label =
95+
format!("these two types are declared with different lifetimes...",);
96+
97+
(span_label, span_label_var1, span_label_var2)
98+
}
8599
} else {
86100
return false;
87-
}
101+
};
88102

103+
struct_span_err!(self.tcx.sess, span, E0623, "lifetime mismatch")
104+
.span_label(ty_sup.span, main_label)
105+
.span_label(ty_sub.span, format!(""))
106+
.span_label(span, format!("...but data{} flows{} here", label1, label2))
107+
.emit();
89108
return true;
90109
}
91110

@@ -94,7 +113,6 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
94113
/// contains the anonymous type.
95114
///
96115
/// # Arguments
97-
///
98116
/// region - the anonymous region corresponding to the anon_anon conflict
99117
/// br - the bound region corresponding to the above region which is of type `BrAnon(_)`
100118
///
@@ -105,39 +123,56 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
105123
/// ```
106124
/// The function returns the nested type corresponding to the anonymous region
107125
/// for e.g. `&u8` and Vec<`&u8`.
108-
fn find_anon_type(&self, region: Region<'tcx>, br: &ty::BoundRegion) -> Option<&hir::Ty> {
126+
pub fn find_anon_type(&self, region: Region<'tcx>, br: &ty::BoundRegion) -> Option<&hir::Ty> {
109127
if let Some(anon_reg) = self.is_suitable_anonymous_region(region) {
110-
let (def_id, _) = anon_reg;
128+
let def_id = anon_reg.def_id;
111129
if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
112130
let ret_ty = self.tcx.type_of(def_id);
113131
if let ty::TyFnDef(_, _) = ret_ty.sty {
114-
if let hir_map::NodeItem(it) = self.tcx.hir.get(node_id) {
115-
if let hir::ItemFn(ref fndecl, _, _, _, _, _) = it.node {
116-
return fndecl
117-
.inputs
118-
.iter()
119-
.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-
})
133-
.next();
134-
}
135-
}
132+
let inputs: &[_] =
133+
match self.tcx.hir.get(node_id) {
134+
hir_map::NodeItem(&hir::Item {
135+
node: hir::ItemFn(ref fndecl, ..), ..
136+
}) => &fndecl.inputs,
137+
hir_map::NodeTraitItem(&hir::TraitItem {
138+
node: hir::TraitItemKind::Method(ref fndecl, ..),
139+
..
140+
}) => &fndecl.decl.inputs,
141+
hir_map::NodeImplItem(&hir::ImplItem {
142+
node: hir::ImplItemKind::Method(ref fndecl, ..),
143+
..
144+
}) => &fndecl.decl.inputs,
145+
146+
_ => &[],
147+
};
148+
149+
return inputs
150+
.iter()
151+
.filter_map(|arg| {
152+
self.find_component_for_bound_region(&**arg, br)
153+
})
154+
.next();
136155
}
137156
}
138157
}
139158
None
140159
}
160+
161+
// This method creates a FindNestedTypeVisitor which returns the type corresponding
162+
// to the anonymous region.
163+
fn find_component_for_bound_region(&self,
164+
arg: &'gcx hir::Ty,
165+
br: &ty::BoundRegion)
166+
-> Option<(&'gcx hir::Ty)> {
167+
let mut nested_visitor = FindNestedTypeVisitor {
168+
infcx: &self,
169+
hir_map: &self.tcx.hir,
170+
bound_region: *br,
171+
found_type: None,
172+
};
173+
nested_visitor.visit_ty(arg);
174+
nested_visitor.found_type
175+
}
141176
}
142177

143178
// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
@@ -176,8 +211,8 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
176211
hir::TyRptr(ref lifetime, _) => {
177212
match self.infcx.tcx.named_region_map.defs.get(&lifetime.id) {
178213
// the lifetime of the TyRptr
179-
Some(&rl::Region::LateBoundAnon(debuijn_index, anon_index)) => {
180-
if debuijn_index.depth == 1 && anon_index == br_index {
214+
Some(&rl::Region::LateBoundAnon(debruijn_index, anon_index)) => {
215+
if debruijn_index.depth == 1 && anon_index == br_index {
181216
self.found_type = Some(arg);
182217
return; // we can stop visiting now
183218
}
@@ -191,10 +226,77 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
191226
}
192227
}
193228
}
229+
// Checks if it is of type `hir::TyPath` which corresponds to a struct.
230+
hir::TyPath(_) => {
231+
let subvisitor = &mut TyPathVisitor {
232+
infcx: self.infcx,
233+
found_it: false,
234+
bound_region: self.bound_region,
235+
hir_map: self.hir_map,
236+
};
237+
intravisit::walk_ty(subvisitor, arg); // call walk_ty; as visit_ty is empty,
238+
// this will visit only outermost type
239+
if subvisitor.found_it {
240+
self.found_type = Some(arg);
241+
}
242+
}
194243
_ => {}
195244
}
196245
// walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
197246
// go on to visit `&Foo`
198247
intravisit::walk_ty(self, arg);
199248
}
200249
}
250+
251+
// The visitor captures the corresponding `hir::Ty` of the anonymous region
252+
// in the case of structs ie. `hir::TyPath`.
253+
// This visitor would be invoked for each lifetime corresponding to a struct,
254+
// and would walk the types like Vec<Ref> in the above example and Ref looking for the HIR
255+
// where that lifetime appears. This allows us to highlight the
256+
// specific part of the type in the error message.
257+
struct TyPathVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
258+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
259+
hir_map: &'a hir::map::Map<'gcx>,
260+
found_it: bool,
261+
bound_region: ty::BoundRegion,
262+
}
263+
264+
impl<'a, 'gcx, 'tcx> Visitor<'gcx> for TyPathVisitor<'a, 'gcx, 'tcx> {
265+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
266+
NestedVisitorMap::OnlyBodies(&self.hir_map)
267+
}
268+
269+
fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
270+
let br_index = match self.bound_region {
271+
ty::BrAnon(index) => index,
272+
_ => return,
273+
};
274+
275+
match self.infcx.tcx.named_region_map.defs.get(&lifetime.id) {
276+
// the lifetime of the TyPath!
277+
Some(&rl::Region::LateBoundAnon(debruijn_index, anon_index)) => {
278+
if debruijn_index.depth == 1 && anon_index == br_index {
279+
self.found_it = true;
280+
}
281+
}
282+
Some(&rl::Region::Static) |
283+
Some(&rl::Region::EarlyBound(_, _)) |
284+
Some(&rl::Region::LateBound(_, _)) |
285+
Some(&rl::Region::Free(_, _)) |
286+
None => {
287+
debug!("no arg found");
288+
}
289+
}
290+
}
291+
292+
fn visit_ty(&mut self, arg: &'gcx hir::Ty) {
293+
// ignore nested types
294+
//
295+
// If you have a type like `Foo<'a, &Ty>` we
296+
// are only interested in the immediate lifetimes ('a).
297+
//
298+
// Making `visit_ty` empty will ignore the `&Ty` embedded
299+
// inside, it will get reached by the outer visitor.
300+
debug!("`Ty` corresponding to a struct is {:?}", arg);
301+
}
302+
}

src/librustc/infer/error_reporting/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,10 @@ use errors::{DiagnosticBuilder, DiagnosticStyledString};
7575
mod note;
7676

7777
mod need_type_info;
78-
mod util;
78+
7979
mod named_anon_conflict;
80+
#[macro_use]
81+
mod util;
8082
mod anon_anon_conflict;
8183

8284
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {

0 commit comments

Comments
 (0)