Skip to content

Commit 2f50c33

Browse files
committed
Adding E0623 for structs
1 parent 11f64d8 commit 2f50c33

8 files changed

+413
-0
lines changed
Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! Error Reporting for Anonymous Region Lifetime Errors
12+
//! where both the regions are anonymous.
13+
use hir;
14+
use infer::InferCtxt;
15+
use ty::{self, Region};
16+
use infer::region_inference::RegionResolutionError::*;
17+
use infer::region_inference::RegionResolutionError;
18+
use hir::map as hir_map;
19+
use middle::resolve_lifetime as rl;
20+
use hir::intravisit::{self, Visitor, NestedVisitorMap};
21+
22+
impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
23+
// This method prints the error message for lifetime errors when both the concerned regions
24+
// are anonymous.
25+
// Consider a case where we have
26+
// fn foo(x: &mut Vec<&u8>, y: &u8)
27+
// { x.push(y); }.
28+
// The example gives
29+
// fn foo(x: &mut Vec<&u8>, y: &u8) {
30+
// --- --- these references are declared with different lifetimes...
31+
// x.push(y);
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.
42+
pub fn try_report_anon_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool {
43+
let (span, sub, sup) = match *error {
44+
ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup),
45+
_ => return false, // inapplicable
46+
};
47+
48+
// Determine whether the sub and sup consist of both anonymous (elided) regions.
49+
let (ty1, ty2, scope_def_id_1, scope_def_id_2, bregion1, bregion2) = if
50+
self.is_suitable_anonymous_region(sup, true).is_some() &&
51+
self.is_suitable_anonymous_region(sub, true).is_some() {
52+
if let (Some(anon_reg1), Some(anon_reg2)) =
53+
(self.is_suitable_anonymous_region(sup, true),
54+
self.is_suitable_anonymous_region(sub, true)) {
55+
let ((def_id1, br1), (def_id2, br2)) = (anon_reg1, anon_reg2);
56+
let found_arg1 = self.find_anon_type(sup, &br1);
57+
let found_arg2 = self.find_anon_type(sub, &br2);
58+
match (found_arg1, found_arg2) {
59+
(Some(anonarg_1), Some(anonarg_2)) => {
60+
(anonarg_1, anonarg_2, def_id1, def_id2, br1, br2)
61+
}
62+
_ => {
63+
return false;
64+
}
65+
}
66+
67+
} else {
68+
return false;
69+
}
70+
} else {
71+
return false; //inapplicable
72+
};
73+
74+
let (label1, label2) = if let (Some(sup_arg), Some(sub_arg)) =
75+
(self.find_arg_with_anonymous_region(sup, sup),
76+
self.find_arg_with_anonymous_region(sub, sub)) {
77+
78+
let ((anon_arg1, _, _, is_first1), (anon_arg2, _, _, is_first2)) = (sup_arg, sub_arg);
79+
if self.is_self_anon(is_first1, scope_def_id_1) ||
80+
self.is_self_anon(is_first2, scope_def_id_2) {
81+
return false;
82+
}
83+
84+
if self.is_return_type_anon(scope_def_id_1, bregion1) ||
85+
self.is_return_type_anon(scope_def_id_2, bregion2) {
86+
return false;
87+
}
88+
89+
90+
91+
92+
if anon_arg1 == anon_arg2 {
93+
(format!(" with one lifetime"), format!(" into the other"))
94+
} else {
95+
let span_label_var1 = if let Some(simple_name) = anon_arg1.pat.simple_name() {
96+
format!(" from `{}`", simple_name)
97+
} else {
98+
format!("")
99+
};
100+
101+
let span_label_var2 = if let Some(simple_name) = anon_arg2.pat.simple_name() {
102+
format!(" into `{}`", simple_name)
103+
} else {
104+
format!("")
105+
};
106+
107+
(span_label_var1, span_label_var2)
108+
}
109+
} else {
110+
return false;
111+
};
112+
113+
struct_span_err!(self.tcx.sess, span, E0623, "lifetime mismatch")
114+
.span_label(ty1.span,
115+
format!("these two types are declared with different lifetimes..."))
116+
.span_label(ty2.span, format!(""))
117+
.span_label(span, format!("...but data{} flows{} here", label1, label2))
118+
.emit();
119+
return true;
120+
121+
}
122+
123+
/// This function calls the `visit_ty` method for the parameters
124+
/// corresponding to the anonymous regions. The `nested_visitor.found_type`
125+
/// contains the anonymous type.
126+
///
127+
/// # Arguments
128+
///
129+
/// region - the anonymous region corresponding to the anon_anon conflict
130+
/// br - the bound region corresponding to the above region which is of type `BrAnon(_)`
131+
///
132+
/// # Example
133+
/// ```
134+
/// fn foo(x: &mut Vec<&u8>, y: &u8)
135+
/// { x.push(y); }
136+
/// ```
137+
/// The function returns the nested type corresponding to the anonymous region
138+
/// for e.g. `&u8` and Vec<`&u8`.
139+
pub fn find_anon_type(&self, region: Region<'tcx>, br: &ty::BoundRegion) -> Option<(&hir::Ty)> {
140+
if let Some(anon_reg) = self.is_suitable_anonymous_region(region, true) {
141+
let (def_id, _) = anon_reg;
142+
if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
143+
let ret_ty = self.tcx.type_of(def_id);
144+
if let ty::TyFnDef(_, _) = ret_ty.sty {
145+
if let hir_map::NodeItem(it) = self.tcx.hir.get(node_id) {
146+
if let hir::ItemFn(ref fndecl, _, _, _, _, _) = it.node {
147+
return fndecl
148+
.inputs
149+
.iter()
150+
.filter_map(|arg| {
151+
self.find_visitor_found_type(&**arg, br)
152+
})
153+
.next();
154+
}
155+
} else if let hir_map::NodeTraitItem(it) = self.tcx.hir.get(node_id) {
156+
if let hir::TraitItemKind::Method(ref fndecl, _) = it.node {
157+
return fndecl
158+
.decl
159+
.inputs
160+
.iter()
161+
.filter_map(|arg| {
162+
self.find_visitor_found_type(&**arg, br)
163+
})
164+
.next();
165+
}
166+
} else if let hir_map::NodeImplItem(it) = self.tcx.hir.get(node_id) {
167+
if let hir::ImplItemKind::Method(ref fndecl, _) = it.node {
168+
return fndecl
169+
.decl
170+
.inputs
171+
.iter()
172+
.filter_map(|arg| {
173+
self.find_visitor_found_type(&**arg, br)
174+
})
175+
.next();
176+
}
177+
}
178+
}
179+
}
180+
}
181+
None
182+
}
183+
184+
// This method creates a FindNestedTypeVisitor which returns the type corresponding
185+
// to the anonymous region.
186+
fn find_visitor_found_type(&self,
187+
arg: &'gcx hir::Ty,
188+
br: &ty::BoundRegion)
189+
-> Option<(&'gcx hir::Ty)> {
190+
let mut nested_visitor = FindNestedTypeVisitor {
191+
infcx: &self,
192+
hir_map: &self.tcx.hir,
193+
bound_region: *br,
194+
found_type: None,
195+
};
196+
nested_visitor.visit_ty(arg);
197+
nested_visitor.found_type
198+
}
199+
}
200+
201+
// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
202+
// anonymous region. The example above would lead to a conflict between
203+
// the two anonymous lifetimes for &u8 in x and y respectively. This visitor
204+
// would be invoked twice, once for each lifetime, and would
205+
// walk the types like &mut Vec<&u8> and &u8 looking for the HIR
206+
// where that lifetime appears. This allows us to highlight the
207+
// specific part of the type in the error message.
208+
struct FindNestedTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
209+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
210+
hir_map: &'a hir::map::Map<'gcx>,
211+
// The bound_region corresponding to the Refree(freeregion)
212+
// associated with the anonymous region we are looking for.
213+
bound_region: ty::BoundRegion,
214+
// The type where the anonymous lifetime appears
215+
// for e.g. Vec<`&u8`> and <`&u8`>
216+
found_type: Option<&'gcx hir::Ty>,
217+
}
218+
219+
impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
220+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
221+
NestedVisitorMap::OnlyBodies(&self.hir_map)
222+
}
223+
224+
fn visit_ty(&mut self, arg: &'gcx hir::Ty) {
225+
// Find the index of the anonymous region that was part of the
226+
// error. We will then search the function parameters for a bound
227+
// region at the right depth with the same index.
228+
let br_index = match self.bound_region {
229+
ty::BrAnon(index) => index,
230+
_ => return,
231+
};
232+
233+
match arg.node {
234+
hir::TyRptr(ref lifetime, _) => {
235+
match self.infcx.tcx.named_region_map.defs.get(&lifetime.id) {
236+
// the lifetime of the TyRptr
237+
Some(&rl::Region::LateBoundAnon(debuijn_index, anon_index)) => {
238+
if debuijn_index.depth == 1 && anon_index == br_index {
239+
self.found_type = Some(arg);
240+
return; // we can stop visiting now
241+
}
242+
}
243+
Some(&rl::Region::Static) |
244+
Some(&rl::Region::EarlyBound(_, _)) |
245+
Some(&rl::Region::LateBound(_, _)) |
246+
Some(&rl::Region::Free(_, _)) |
247+
None => {
248+
debug!("no arg found");
249+
}
250+
}
251+
}
252+
// Checks if it is of type `hir::TyPath` which corresponds to a struct.
253+
hir::TyPath(_) => {
254+
let subvisitor = &mut TyPathVisitor {
255+
infcx: self.infcx,
256+
found_it: false,
257+
bound_region: self.bound_region,
258+
hir_map: self.hir_map,
259+
};
260+
intravisit::walk_ty(subvisitor, arg); // call walk_ty; as visit_ty is empty,
261+
// this will visit only outermost type
262+
if subvisitor.found_it {
263+
self.found_type = Some(arg);
264+
}
265+
}
266+
_ => {}
267+
}
268+
// walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
269+
// go on to visit `&Foo`
270+
intravisit::walk_ty(self, arg);
271+
}
272+
}
273+
274+
// The visitor captures the corresponding `hir::Ty` of the anonymous region
275+
// in the case of structs ie. `hir::TyPath`.
276+
// This visitor would be invoked for each lifetime corresponding to a struct,
277+
// and would walk the types like Vec<Ref> in the above example and Ref looking for the HIR
278+
// where that lifetime appears. This allows us to highlight the
279+
// specific part of the type in the error message.
280+
struct TyPathVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
281+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
282+
hir_map: &'a hir::map::Map<'gcx>,
283+
found_it: bool,
284+
bound_region: ty::BoundRegion,
285+
}
286+
287+
impl<'a, 'gcx, 'tcx> Visitor<'gcx> for TyPathVisitor<'a, 'gcx, 'tcx> {
288+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
289+
NestedVisitorMap::OnlyBodies(&self.hir_map)
290+
}
291+
292+
fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
293+
let br_index = match self.bound_region {
294+
ty::BrAnon(index) => index,
295+
_ => return,
296+
};
297+
298+
299+
match self.infcx.tcx.named_region_map.defs.get(&lifetime.id) {
300+
// the lifetime of the TyPath!
301+
Some(&rl::Region::LateBoundAnon(debuijn_index, anon_index)) => {
302+
if debuijn_index.depth == 1 && anon_index == br_index {
303+
self.found_it = true;
304+
}
305+
}
306+
Some(&rl::Region::Static) |
307+
Some(&rl::Region::EarlyBound(_, _)) |
308+
Some(&rl::Region::LateBound(_, _)) |
309+
Some(&rl::Region::Free(_, _)) |
310+
None => {
311+
debug!("no arg found");
312+
}
313+
}
314+
}
315+
316+
fn visit_ty(&mut self, arg: &'gcx hir::Ty) {
317+
// ignore nested types
318+
//
319+
// If you have a type like `Foo<'a, &Ty>` we
320+
// are only interested in the immediate lifetimes ('a).
321+
//
322+
// Making `visit_ty` empty will ignore the `&Ty` embedded
323+
// inside, it will get reached by the outer visitor.
324+
debug!("`Ty` corresponding to a struct is {:?}", arg);
325+
}
326+
}

src/librustc/infer/error_reporting/different_lifetimes.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,13 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
204204

205205
fn visit_ty(&mut self, arg: &'gcx hir::Ty) {
206206
match arg.node {
207+
hir::TyBareFn(_) => {
208+
self.depth += 1;
209+
intravisit::walk_ty(self, arg);
210+
self.depth -= 1;
211+
return;
212+
}
213+
207214
hir::TyRptr(ref lifetime, _) => {
208215
// the lifetime of the TyRptr
209216
let hir_id = self.infcx.tcx.hir.node_to_hir_id(lifetime.id);
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0623]: lifetime mismatch
2+
--> $DIR/ex3-both-anon-regions-4.rs:12:13
3+
|
4+
11 | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) {
5+
| --- --- these references are declared with different lifetimes...
6+
12 | z.push((x,y));
7+
| ^ ...but data flows into `z` here
8+
9+
error[E0623]: lifetime mismatch
10+
--> $DIR/ex3-both-anon-regions-4.rs:12:15
11+
|
12+
11 | fn foo(z: &mut Vec<(&u8,&u8)>, (x, y): (&u8, &u8)) {
13+
| --- --- these references are declared with different lifetimes...
14+
12 | z.push((x,y));
15+
| ^ ...but data flows into `z` here
16+
17+
error: aborting due to 2 previous errors
18+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
struct Ref<'a, 'b> {
11+
a: &'a u32,
12+
b: &'b u32,
13+
}
14+
15+
fn foo(mut x: Ref) {
16+
x.a = x.b;
17+
}
18+
19+
fn main() {}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0623]: lifetime mismatch
2+
--> $DIR/ex3-both-anon-regions-both-are-structs-4.rs:16:11
3+
|
4+
15 | fn foo(mut x: Ref) {
5+
| ---
6+
| |
7+
| these two types are declared with different lifetimes...
8+
16 | x.a = x.b;
9+
| ^^^ ...but data with one lifetime flows into the other here
10+
11+
error: aborting due to previous error
12+

0 commit comments

Comments
 (0)