Skip to content

Commit eba9d7f

Browse files
committed
Auto merge of #43298 - gaurikholkar:lifetime_errors, r=estebank
improve case with both anonymous lifetime parameters #43269 This is a fix to #43269. Sample output message- ``` error[E0623]: lifetime mismatch --> $DIR/ex3-both-anon-regions.rs:12:12 | 11 | fn foo(x: &mut Vec<&u8>, y: &u8) { | --- --- these references must have the same lifetime 12 | x.push(y); | ^ data from `y` flows into `x` here error: aborting due to 2 previous errors ``` r? @nikomatsakis
2 parents e2b5d7e + 4fb1808 commit eba9d7f

13 files changed

+462
-125
lines changed

src/librustc/diagnostics.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2025,4 +2025,5 @@ register_diagnostics! {
20252025
E0490, // a value of type `..` is borrowed for too long
20262026
E0495, // cannot infer an appropriate lifetime due to conflicting requirements
20272027
E0566, // conflicting representation hints
2028+
E0623, // lifetime mismatch where both parameters are anonymous regions
20282029
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
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 must have the same lifetime
31+
// x.push(y);
32+
// ^ data from `y` flows into `x` here
33+
// It will later be extended to trait objects and structs.
34+
pub fn try_report_anon_anon_conflict(&self, error: &RegionResolutionError<'tcx>) -> bool {
35+
36+
let (span, sub, sup) = match *error {
37+
ConcreteFailure(ref origin, sub, sup) => (origin.span(), sub, sup),
38+
_ => return false, // inapplicable
39+
};
40+
41+
// 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+
};
60+
61+
if let (Some(sup_arg), Some(sub_arg)) =
62+
(self.find_arg_with_anonymous_region(sup, sup),
63+
self.find_arg_with_anonymous_region(sub, sub)) {
64+
let ((anon_arg1, _, _, _), (anon_arg2, _, _, _)) = (sup_arg, sub_arg);
65+
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+
};
71+
72+
let span_label_var2 = if let Some(simple_name) = anon_arg2.pat.simple_name() {
73+
format!(" into `{}` ", simple_name)
74+
} 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 must have the same lifetime"))
81+
.span_label(ty2.span, format!(""))
82+
.span_label(span,
83+
format!("data{}flows{}here", span_label_var1, span_label_var2))
84+
.emit();
85+
} else {
86+
return false;
87+
}
88+
89+
return true;
90+
}
91+
92+
/// This function calls the `visit_ty` method for the parameters
93+
/// corresponding to the anonymous regions. The `nested_visitor.found_type`
94+
/// contains the anonymous type.
95+
///
96+
/// # Arguments
97+
///
98+
/// region - the anonymous region corresponding to the anon_anon conflict
99+
/// br - the bound region corresponding to the above region which is of type `BrAnon(_)`
100+
///
101+
/// # Example
102+
/// ```
103+
/// fn foo(x: &mut Vec<&u8>, y: &u8)
104+
/// { x.push(y); }
105+
/// ```
106+
/// The function returns the nested type corresponding to the anonymous region
107+
/// 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) {
110+
let (def_id, _) = anon_reg;
111+
if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
112+
let ret_ty = self.tcx.type_of(def_id);
113+
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+
}
136+
}
137+
}
138+
}
139+
None
140+
}
141+
}
142+
143+
// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
144+
// anonymous region. The example above would lead to a conflict between
145+
// the two anonymous lifetimes for &u8 in x and y respectively. This visitor
146+
// would be invoked twice, once for each lifetime, and would
147+
// walk the types like &mut Vec<&u8> and &u8 looking for the HIR
148+
// where that lifetime appears. This allows us to highlight the
149+
// specific part of the type in the error message.
150+
struct FindNestedTypeVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
151+
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
152+
hir_map: &'a hir::map::Map<'gcx>,
153+
// The bound_region corresponding to the Refree(freeregion)
154+
// associated with the anonymous region we are looking for.
155+
bound_region: ty::BoundRegion,
156+
// The type where the anonymous lifetime appears
157+
// for e.g. Vec<`&u8`> and <`&u8`>
158+
found_type: Option<&'gcx hir::Ty>,
159+
}
160+
161+
impl<'a, 'gcx, 'tcx> Visitor<'gcx> for FindNestedTypeVisitor<'a, 'gcx, 'tcx> {
162+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'gcx> {
163+
NestedVisitorMap::OnlyBodies(&self.hir_map)
164+
}
165+
166+
fn visit_ty(&mut self, arg: &'gcx hir::Ty) {
167+
// Find the index of the anonymous region that was part of the
168+
// error. We will then search the function parameters for a bound
169+
// region at the right depth with the same index.
170+
let br_index = match self.bound_region {
171+
ty::BrAnon(index) => index,
172+
_ => return,
173+
};
174+
175+
match arg.node {
176+
hir::TyRptr(ref lifetime, _) => {
177+
match self.infcx.tcx.named_region_map.defs.get(&lifetime.id) {
178+
// the lifetime of the TyRptr
179+
Some(&rl::Region::LateBoundAnon(debuijn_index, anon_index)) => {
180+
if debuijn_index.depth == 1 && anon_index == br_index {
181+
self.found_type = Some(arg);
182+
return; // we can stop visiting now
183+
}
184+
}
185+
Some(&rl::Region::Static) |
186+
Some(&rl::Region::EarlyBound(_, _)) |
187+
Some(&rl::Region::LateBound(_, _)) |
188+
Some(&rl::Region::Free(_, _)) |
189+
None => {
190+
debug!("no arg found");
191+
}
192+
}
193+
}
194+
_ => {}
195+
}
196+
// walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
197+
// go on to visit `&Foo`
198+
intravisit::walk_ty(self, arg);
199+
}
200+
}

src/librustc/infer/error_reporting/mod.rs

+29-22
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ use errors::{DiagnosticBuilder, DiagnosticStyledString};
7676
mod note;
7777

7878
mod need_type_info;
79+
mod util;
7980
mod named_anon_conflict;
81+
mod anon_anon_conflict;
8082

8183
impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
8284
pub fn note_and_explain_region(self,
@@ -270,29 +272,34 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
270272
for error in errors {
271273
debug!("report_region_errors: error = {:?}", error);
272274

273-
if !self.try_report_named_anon_conflict(&error) {
274-
match error.clone() {
275-
// These errors could indicate all manner of different
276-
// problems with many different solutions. Rather
277-
// than generate a "one size fits all" error, what we
278-
// attempt to do is go through a number of specific
279-
// scenarios and try to find the best way to present
280-
// the error. If all of these fails, we fall back to a rather
281-
// general bit of code that displays the error information
282-
ConcreteFailure(origin, sub, sup) => {
283-
self.report_concrete_failure(origin, sub, sup).emit();
284-
}
285-
GenericBoundFailure(kind, param_ty, sub) => {
286-
self.report_generic_bound_failure(kind, param_ty, sub);
287-
}
288-
SubSupConflict(var_origin,
289-
sub_origin, sub_r,
290-
sup_origin, sup_r) => {
275+
if !self.try_report_named_anon_conflict(&error) &&
276+
!self.try_report_anon_anon_conflict(&error) {
277+
278+
match error.clone() {
279+
// These errors could indicate all manner of different
280+
// problems with many different solutions. Rather
281+
// than generate a "one size fits all" error, what we
282+
// attempt to do is go through a number of specific
283+
// scenarios and try to find the best way to present
284+
// the error. If all of these fails, we fall back to a rather
285+
// general bit of code that displays the error information
286+
ConcreteFailure(origin, sub, sup) => {
287+
288+
self.report_concrete_failure(origin, sub, sup).emit();
289+
}
290+
291+
GenericBoundFailure(kind, param_ty, sub) => {
292+
self.report_generic_bound_failure(kind, param_ty, sub);
293+
}
294+
295+
SubSupConflict(var_origin, sub_origin, sub_r, sup_origin, sup_r) => {
291296
self.report_sub_sup_conflict(var_origin,
292-
sub_origin, sub_r,
293-
sup_origin, sup_r);
294-
}
295-
}
297+
sub_origin,
298+
sub_r,
299+
sup_origin,
300+
sup_r);
301+
}
302+
}
296303
}
297304
}
298305
}

0 commit comments

Comments
 (0)