Skip to content

Commit 4771970

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

33 files changed

+630
-160
lines changed

src/librustc/infer/error_reporting/anon_anon_conflict.rs

Lines changed: 174 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -27,66 +27,97 @@ 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() {
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() {
4452
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+
(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+
}
5365
}
66+
5467
} else {
5568
return false;
5669
}
5770
} else {
58-
return false; // inapplicable
71+
return false; //inapplicable
5972
};
6073

61-
if let (Some(sup_arg), Some(sub_arg)) =
74+
let (label1, label2) = if let (Some(sup_arg), Some(sub_arg)) =
6275
(self.find_arg_with_anonymous_region(sup, sup),
6376
self.find_arg_with_anonymous_region(sub, sub)) {
64-
let ((anon_arg1, _, _, _), (anon_arg2, _, _, _)) = (sup_arg, sub_arg);
6577

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-
};
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+
}
7183

72-
let span_label_var2 = if let Some(simple_name) = anon_arg2.pat.simple_name() {
73-
format!(" into `{}` ", simple_name)
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"))
7494
} 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();
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+
}
85109
} else {
86110
return false;
87-
}
111+
};
88112

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();
89119
return true;
120+
90121
}
91122

92123
/// This function calls the `visit_ty` method for the parameters
@@ -105,8 +136,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
105136
/// ```
106137
/// The function returns the nested type corresponding to the anonymous region
107138
/// 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) {
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) {
110141
let (def_id, _) = anon_reg;
111142
if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
112143
let ret_ty = self.tcx.type_of(def_id);
@@ -117,19 +148,30 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
117148
.inputs
118149
.iter()
119150
.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-
})
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+
})
133175
.next();
134176
}
135177
}
@@ -138,6 +180,20 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
138180
}
139181
None
140182
}
183+
184+
fn find_visitor_found_type(&self,
185+
arg: &'gcx hir::Ty,
186+
br: &ty::BoundRegion)
187+
-> Option<(&'gcx hir::Ty)> {
188+
let mut nested_visitor = FindNestedTypeVisitor {
189+
infcx: &self,
190+
hir_map: &self.tcx.hir,
191+
bound_region: *br,
192+
found_type: None,
193+
};
194+
nested_visitor.visit_ty(arg);
195+
nested_visitor.found_type
196+
}
141197
}
142198

143199
// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
@@ -191,10 +247,78 @@ impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
191247
}
192248
}
193249
}
250+
// Checks if it is of type `hir::TyPath` which corresponds to a struct.
251+
hir::TyPath(_) => {
252+
let subvisitor = &mut TyPathVisitor {
253+
infcx: self.infcx,
254+
found_it: false,
255+
bound_region: self.bound_region,
256+
hir_map: self.hir_map,
257+
};
258+
intravisit::walk_ty(subvisitor, arg); // call walk_ty; as visit_ty is empty,
259+
// this will visit only outermost type
260+
if subvisitor.found_it {
261+
self.found_type = Some(arg);
262+
}
263+
}
194264
_ => {}
195265
}
196266
// walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
197267
// go on to visit `&Foo`
198268
intravisit::walk_ty(self, arg);
199269
}
200270
}
271+
272+
// The visitor captures the corresponding `hir::Ty` of the anonymous region
273+
// in the case of structs ie. `hir::TyPath`.
274+
// This visitor would be invoked for each lifetime corresponding to a struct,
275+
// and would walk the types like Vec<Ref> in the above example and Ref looking for the HIR
276+
// where that lifetime appears. This allows us to highlight the
277+
// specific part of the type in the error message.
278+
struct TyPathVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
279+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
280+
hir_map: &'a hir::map::Map<'gcx>,
281+
found_it: bool,
282+
bound_region: ty::BoundRegion,
283+
}
284+
285+
impl<'a, 'gcx, 'tcx> Visitor<'gcx> for TyPathVisitor<'a, 'gcx, 'tcx> {
286+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
287+
NestedVisitorMap::OnlyBodies(&self.hir_map)
288+
}
289+
290+
fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
291+
let br_index = match self.bound_region {
292+
ty::BrAnon(index) => index,
293+
_ => return,
294+
};
295+
296+
297+
match self.infcx.tcx.named_region_map.defs.get(&lifetime.id) {
298+
// the lifetime of the TyPath!
299+
Some(&rl::Region::LateBoundAnon(debuijn_index, anon_index)) => {
300+
if debuijn_index.depth == 1 && anon_index == br_index {
301+
self.found_it = true;
302+
}
303+
}
304+
Some(&rl::Region::Static) |
305+
Some(&rl::Region::EarlyBound(_, _)) |
306+
Some(&rl::Region::LateBound(_, _)) |
307+
Some(&rl::Region::Free(_, _)) |
308+
None => {
309+
debug!("no arg found");
310+
}
311+
}
312+
}
313+
314+
fn visit_ty(&mut self, arg: &'gcx hir::Ty) {
315+
// ignore nested types
316+
//
317+
// If you have a type like `Foo<'a, &Ty>` we
318+
// are only interested in the immediate lifetimes ('a).
319+
//
320+
// Making `visit_ty` empty will ignore the `&Ty` embedded
321+
// inside, it will get reached by the outer visitor.
322+
debug!("`Ty` corresponding to a struct is {:?}", arg);
323+
}
324+
}

src/librustc/infer/error_reporting/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -963,4 +963,4 @@ impl<'tcx> ObligationCause<'tcx> {
963963
_ => "types are compatible",
964964
}
965965
}
966-
}
966+
}

src/librustc/infer/error_reporting/named_anon_conflict.rs

Lines changed: 31 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
//! Error Reporting for Anonymous Region Lifetime Errors
1212
//! where one region is named and the other is anonymous.
1313
use infer::InferCtxt;
14-
use ty;
1514
use infer::region_inference::RegionResolutionError::*;
1615
use infer::region_inference::RegionResolutionError;
1716

@@ -31,63 +30,42 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
3130
// only introduced anonymous regions in parameters) as well as a
3231
// version new_ty of its type where the anonymous region is replaced
3332
// with the named one.
34-
let (named, (arg, new_ty, br, is_first), (scope_def_id, _)) =
35-
if sub.is_named_region() && self.is_suitable_anonymous_region(sup).is_some() {
36-
(sub,
37-
self.find_arg_with_anonymous_region(sup, sub).unwrap(),
38-
self.is_suitable_anonymous_region(sup).unwrap())
39-
} else if sup.is_named_region() && self.is_suitable_anonymous_region(sub).is_some() {
40-
(sup,
41-
self.find_arg_with_anonymous_region(sub, sup).unwrap(),
42-
self.is_suitable_anonymous_region(sub).unwrap())
43-
} else {
44-
return false; // inapplicable
45-
};
46-
47-
// Here, we check for the case where the anonymous region
48-
// is in the return type.
49-
// FIXME(#42703) - Need to handle certain cases here.
50-
let ret_ty = self.tcx.type_of(scope_def_id);
51-
match ret_ty.sty {
52-
ty::TyFnDef(_, _) => {
53-
let sig = ret_ty.fn_sig(self.tcx);
54-
let late_bound_regions = self.tcx
55-
.collect_referenced_late_bound_regions(&sig.output());
56-
if late_bound_regions.iter().any(|r| *r == br) {
57-
return false;
58-
}
59-
}
60-
_ => {}
61-
}
33+
let (named, (arg, new_ty, br, is_first), (scope_def_id, _)) = if
34+
sub.is_named_region() && self.is_suitable_anonymous_region(sup, false).is_some() {
35+
(sub,
36+
self.find_arg_with_anonymous_region(sup, sub).unwrap(),
37+
self.is_suitable_anonymous_region(sup, false).unwrap())
38+
} else if
39+
sup.is_named_region() && self.is_suitable_anonymous_region(sub, false).is_some() {
40+
(sup,
41+
self.find_arg_with_anonymous_region(sub, sup).unwrap(),
42+
self.is_suitable_anonymous_region(sub, false).unwrap())
43+
} else {
44+
return false; // inapplicable
45+
};
6246

63-
// Here we check for the case where anonymous region
64-
// corresponds to self and if yes, we display E0312.
65-
// FIXME(#42700) - Need to format self properly to
66-
// enable E0621 for it.
67-
if is_first &&
68-
self.tcx
69-
.opt_associated_item(scope_def_id)
70-
.map(|i| i.method_has_self_argument)
71-
.unwrap_or(false) {
47+
if self.is_return_type_anon(scope_def_id, br) || self.is_self_anon(is_first, scope_def_id) {
7248
return false;
73-
}
74-
75-
let (error_var, span_label_var) = if let Some(simple_name) = arg.pat.simple_name() {
76-
(format!("the type of `{}`", simple_name), format!("the type of `{}`", simple_name))
7749
} else {
78-
("parameter type".to_owned(), "type".to_owned())
79-
};
8050

81-
struct_span_err!(self.tcx.sess,
82-
span,
83-
E0621,
84-
"explicit lifetime required in {}",
85-
error_var)
86-
.span_label(arg.pat.span,
87-
format!("consider changing {} to `{}`", span_label_var, new_ty))
88-
.span_label(span, format!("lifetime `{}` required", named))
89-
.emit();
51+
let (error_var, span_label_var) = if let Some(simple_name) = arg.pat.simple_name() {
52+
(format!("the type of `{}`", simple_name), format!("the type of `{}`", simple_name))
53+
} else {
54+
("parameter type".to_owned(), "type".to_owned())
55+
};
9056

57+
struct_span_err!(self.tcx.sess,
58+
span,
59+
E0621,
60+
"explicit lifetime required in {}",
61+
error_var)
62+
.span_label(arg.pat.span,
63+
format!("consider changing {} to `{}`", span_label_var, new_ty))
64+
.span_label(span, format!("lifetime `{}` required", named))
65+
.emit();
66+
67+
68+
}
9169
return true;
9270
}
9371
}

0 commit comments

Comments
 (0)