Skip to content

Commit 727f017

Browse files
committed
write code to extract region names and emit new style message
1 parent fa02d68 commit 727f017

File tree

49 files changed

+523
-155
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+523
-155
lines changed

src/librustc_mir/borrow_check/mod.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
128128
input_mir: &Mir<'gcx>,
129129
def_id: DefId,
130130
) -> BorrowCheckResult<'gcx> {
131+
debug!("do_mir_borrowck(def_id = {:?})", def_id);
132+
131133
let tcx = infcx.tcx;
132134
let attributes = tcx.get_attrs(def_id);
133135
let param_env = tcx.param_env(def_id);
@@ -319,10 +321,14 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
319321
}
320322
}
321323

322-
BorrowCheckResult {
324+
let result = BorrowCheckResult {
323325
closure_requirements: opt_closure_req,
324326
used_mut_upvars: mbcx.used_mut_upvars,
325-
}
327+
};
328+
329+
debug!("do_mir_borrowck: result = {:#?}", result);
330+
331+
result
326332
}
327333

328334
#[allow(dead_code)]

src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs

+29-32
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
// except according to those terms.
1010

1111
use borrow_check::nll::region_infer::values::ToElementIndex;
12-
use borrow_check::nll::region_infer::{Cause, ConstraintIndex, RegionInferenceContext};
1312
use borrow_check::nll::region_infer::{ConstraintIndex, RegionInferenceContext};
1413
use borrow_check::nll::type_check::Locations;
1514
use rustc::hir::def_id::DefId;
@@ -22,6 +21,8 @@ use rustc_data_structures::indexed_vec::IndexVec;
2221
use std::fmt;
2322
use syntax_pos::Span;
2423

24+
mod region_name;
25+
2526
/// Constraints that are considered interesting can be categorized to
2627
/// determine why they are interesting. Order of variants indicates
2728
/// sort order of the category, thereby influencing diagnostic output.
@@ -200,27 +201,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
200201
) {
201202
debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
202203

203-
let fr_name = self.to_error_region(fr);
204-
let outlived_fr_name = self.to_error_region(outlived_fr);
205-
206-
if let (Some(f), Some(o)) = (fr_name, outlived_fr_name) {
204+
if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
207205
let tables = infcx.tcx.typeck_tables_of(mir_def_id);
208206
let nice = NiceRegionError::new_from_span(infcx.tcx, blame_span, o, f, Some(tables));
209207
if let Some(_error_reported) = nice.try_report() {
210208
return;
211209
}
212210
}
213211

214-
let fr_string = match fr_name {
215-
Some(r) => format!("free region `{}`", r),
216-
None => format!("free region `{:?}`", fr),
217-
};
218-
219-
let outlived_fr_string = match outlived_fr_name {
220-
Some(r) => format!("free region `{}`", r),
221-
None => format!("free region `{:?}`", outlived_fr),
222-
};
223-
224212
// Find all paths
225213
let constraint_paths = self.find_constraint_paths_between_regions(outlived_fr, |r| r == fr);
226214
debug!("report_error: constraint_paths={:#?}", constraint_paths);
@@ -239,25 +227,34 @@ impl<'tcx> RegionInferenceContext<'tcx> {
239227
categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
240228
debug!("report_error: sorted_path={:?}", categorized_path);
241229

242-
// If we found something, cite that as the main cause of the problem.
243-
if let Some((category, span)) = categorized_path.first() {
244-
let mut diag = infcx.tcx.sess.struct_span_err(
245-
*span,
246-
&format!(
247-
"{} requires that data must outlive {}",
248-
category, outlived_fr_string
249-
),
250-
);
230+
// Get a span
231+
let (category, span) = categorized_path.first().unwrap();
232+
let diag = &mut infcx.tcx.sess.struct_span_err(
233+
*span,
234+
&format!("unsatisfied lifetime constraints"), // FIXME
235+
);
251236

252-
diag.emit();
253-
} else {
254-
let mut diag = infcx.tcx.sess.struct_span_err(
255-
blame_span,
256-
&format!("{} does not outlive {}", fr_string, outlived_fr_string,),
257-
);
237+
// Figure out how we can refer
238+
let counter = &mut 1;
239+
let fr_name = self.give_region_a_name(infcx.tcx, mir, mir_def_id, fr, counter, diag);
240+
let outlived_fr_name = self.give_region_a_name(
241+
infcx.tcx,
242+
mir,
243+
mir_def_id,
244+
outlived_fr,
245+
counter,
246+
diag,
247+
);
258248

259-
diag.emit();
260-
}
249+
diag.span_label(
250+
*span,
251+
format!(
252+
"{} requires that `{}` must outlive `{}`",
253+
category, fr_name, outlived_fr_name,
254+
),
255+
);
256+
257+
diag.emit();
261258
}
262259

263260
// Find some constraint `X: Y` where:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
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+
11+
use borrow_check::nll::region_infer::RegionInferenceContext;
12+
use borrow_check::nll::ToRegionVid;
13+
use rustc::hir::def_id::DefId;
14+
use rustc::mir::{Local, Mir};
15+
use rustc::ty::{self, RegionVid, TyCtxt};
16+
use rustc_data_structures::indexed_vec::Idx;
17+
use rustc_errors::DiagnosticBuilder;
18+
use syntax::ast::Name;
19+
use syntax::symbol::keywords;
20+
use syntax_pos::symbol::InternedString;
21+
22+
impl<'tcx> RegionInferenceContext<'tcx> {
23+
/// Maps from an internal MIR region vid to something that we can
24+
/// report to the user. In some cases, the region vids will map
25+
/// directly to lifetimes that the user has a name for (e.g.,
26+
/// `'static`). But frequently they will not, in which case we
27+
/// have to find some way to identify the lifetime to the user. To
28+
/// that end, this function takes a "diagnostic" so that it can
29+
/// create auxiliary notes as needed.
30+
///
31+
/// Example (function arguments):
32+
///
33+
/// Suppose we are trying to give a name to the lifetime of the
34+
/// reference `x`:
35+
///
36+
/// ```
37+
/// fn foo(x: &u32) { .. }
38+
/// ```
39+
///
40+
/// This function would create a label like this:
41+
///
42+
/// ```
43+
/// | fn foo(x: &u32) { .. }
44+
/// ------- fully elaborated type of `x` is `&'1 u32`
45+
/// ```
46+
///
47+
/// and then return the name `'1` for us to use.
48+
crate fn give_region_a_name(
49+
&self,
50+
tcx: TyCtxt<'_, '_, 'tcx>,
51+
mir: &Mir<'tcx>,
52+
mir_def_id: DefId,
53+
fr: RegionVid,
54+
counter: &mut usize,
55+
diag: &mut DiagnosticBuilder,
56+
) -> InternedString {
57+
debug!("give_region_a_name(fr={:?}, counter={})", fr, counter);
58+
59+
assert!(self.universal_regions.is_universal_region(fr));
60+
61+
self.give_name_from_error_region(tcx, mir_def_id, fr, counter, diag)
62+
.or_else(|| {
63+
self.give_name_if_anonymous_region_appears_in_arguments(tcx, mir, fr, counter, diag)
64+
})
65+
.or_else(|| {
66+
self.give_name_if_anonymous_region_appears_in_upvars(tcx, mir, fr, counter, diag)
67+
})
68+
.or_else(|| {
69+
self.give_name_if_anonymous_region_appears_in_output(tcx, mir, fr, counter, diag)
70+
})
71+
.unwrap_or_else(|| span_bug!(mir.span, "can't make a name for free region {:?}", fr))
72+
}
73+
74+
/// Check for the case where `fr` maps to something that the
75+
/// *user* has a name for. In that case, we'll be able to map
76+
/// `fr` to a `Region<'tcx>`, and that region will be one of
77+
/// named variants.
78+
fn give_name_from_error_region(
79+
&self,
80+
tcx: TyCtxt<'_, '_, 'tcx>,
81+
mir_def_id: DefId,
82+
fr: RegionVid,
83+
counter: &mut usize,
84+
diag: &mut DiagnosticBuilder<'_>,
85+
) -> Option<InternedString> {
86+
let error_region = self.to_error_region(fr)?;
87+
debug!("give_region_a_name: error_region = {:?}", error_region);
88+
match error_region {
89+
ty::ReEarlyBound(ebr) => Some(ebr.name),
90+
91+
ty::ReStatic => Some(keywords::StaticLifetime.name().as_interned_str()),
92+
93+
ty::ReFree(free_region) => match free_region.bound_region {
94+
ty::BoundRegion::BrNamed(_, name) => Some(name),
95+
96+
ty::BoundRegion::BrEnv => {
97+
let closure_span = tcx.hir.span_if_local(mir_def_id).unwrap();
98+
let region_name = self.synthesize_region_name(counter);
99+
diag.span_label(
100+
closure_span,
101+
format!("lifetime `{}` represents the closure body", region_name),
102+
);
103+
Some(region_name)
104+
}
105+
106+
ty::BoundRegion::BrAnon(_) | ty::BoundRegion::BrFresh(_) => None,
107+
},
108+
109+
ty::ReLateBound(..)
110+
| ty::ReScope(..)
111+
| ty::ReVar(..)
112+
| ty::ReSkolemized(..)
113+
| ty::ReEmpty
114+
| ty::ReErased
115+
| ty::ReClosureBound(..)
116+
| ty::ReCanonical(..) => None,
117+
}
118+
}
119+
120+
/// Find an argument that contains `fr` and label it with a fully
121+
/// elaborated type, returning something like `'1`. Result looks
122+
/// like:
123+
///
124+
/// ```
125+
/// | fn foo(x: &u32) { .. }
126+
/// ------- fully elaborated type of `x` is `&'1 u32`
127+
/// ```
128+
fn give_name_if_anonymous_region_appears_in_arguments(
129+
&self,
130+
tcx: TyCtxt<'_, '_, 'tcx>,
131+
mir: &Mir<'tcx>,
132+
fr: RegionVid,
133+
counter: &mut usize,
134+
diag: &mut DiagnosticBuilder<'_>,
135+
) -> Option<InternedString> {
136+
let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
137+
let argument_index = self.universal_regions
138+
.unnormalized_input_tys
139+
.iter()
140+
.skip(implicit_inputs)
141+
.position(|arg_ty| {
142+
debug!("give_name_if_anonymous_region_appears_in_arguments: arg_ty = {:?}", arg_ty);
143+
tcx.any_free_region_meets(arg_ty, |r| r.to_region_vid() == fr)
144+
})?
145+
+ implicit_inputs;
146+
147+
debug!(
148+
"give_name_if_anonymous_region_appears_in_arguments: \
149+
found {:?} in argument {} which has type {:?}",
150+
fr, argument_index, self.universal_regions.unnormalized_input_tys[argument_index],
151+
);
152+
153+
let region_name = self.synthesize_region_name(counter);
154+
155+
let argument_local = Local::new(argument_index + 1);
156+
let argument_span = mir.local_decls[argument_local].source_info.span;
157+
diag.span_label(
158+
argument_span,
159+
format!("lifetime `{}` appears in this argument", region_name,),
160+
);
161+
162+
Some(region_name)
163+
}
164+
165+
/// Find a closure upvar that contains `fr` and label it with a
166+
/// fully elaborated type, returning something like `'1`. Result
167+
/// looks like:
168+
///
169+
/// ```
170+
/// | let x = Some(&22);
171+
/// - fully elaborated type of `x` is `Option<&'1 u32>`
172+
/// ```
173+
fn give_name_if_anonymous_region_appears_in_upvars(
174+
&self,
175+
tcx: TyCtxt<'_, '_, 'tcx>,
176+
mir: &Mir<'tcx>,
177+
fr: RegionVid,
178+
counter: &mut usize,
179+
diag: &mut DiagnosticBuilder<'_>,
180+
) -> Option<InternedString> {
181+
let upvar_index = self.universal_regions
182+
.defining_ty
183+
.upvar_tys(tcx)
184+
.position(|upvar_ty| {
185+
debug!(
186+
"give_name_if_anonymous_region_appears_in_upvars: upvar_ty = {:?}",
187+
upvar_ty,
188+
);
189+
tcx.any_free_region_meets(&upvar_ty, |r| r.to_region_vid() == fr)
190+
})?;
191+
192+
debug!(
193+
"give_name_if_anonymous_region_appears_in_upvars: \
194+
found {:?} in upvar {} which has type {:?}",
195+
fr,
196+
upvar_index,
197+
self.universal_regions
198+
.defining_ty
199+
.upvar_tys(tcx)
200+
.nth(upvar_index),
201+
);
202+
203+
let region_name = self.synthesize_region_name(counter);
204+
205+
let upvar_hir_id = mir.upvar_decls[upvar_index].var_hir_id.assert_crate_local();
206+
let upvar_node_id = tcx.hir.hir_to_node_id(upvar_hir_id);
207+
let upvar_span = tcx.hir.span(upvar_node_id);
208+
let upvar_name = tcx.hir.name(upvar_node_id);
209+
diag.span_label(
210+
upvar_span,
211+
format!(
212+
"lifetime `{}` appears in the type of `{}`",
213+
region_name, upvar_name,
214+
),
215+
);
216+
217+
Some(region_name)
218+
}
219+
220+
/// Check for arguments appearing in the (closure) return type. It
221+
/// must be a closure since, in a free fn, such an argument would
222+
/// have to either also appear in an argument (if using elision)
223+
/// or be early bound (named, not in argument).
224+
fn give_name_if_anonymous_region_appears_in_output(
225+
&self,
226+
tcx: TyCtxt<'_, '_, 'tcx>,
227+
mir: &Mir<'tcx>,
228+
fr: RegionVid,
229+
counter: &mut usize,
230+
diag: &mut DiagnosticBuilder<'_>,
231+
) -> Option<InternedString> {
232+
let return_ty = self.universal_regions
233+
.unnormalized_output_ty;
234+
debug!("give_name_if_anonymous_region_appears_in_output: return_ty = {:?}", return_ty);
235+
if !tcx.any_free_region_meets(&return_ty, |r| r.to_region_vid() == fr) {
236+
return None;
237+
}
238+
239+
let region_name = self.synthesize_region_name(counter);
240+
diag.span_label(
241+
mir.span,
242+
format!("lifetime `{}` appears in return type", region_name),
243+
);
244+
245+
Some(region_name)
246+
}
247+
248+
/// Create a synthetic region named `'1`, incrementing the
249+
/// counter.
250+
fn synthesize_region_name(&self, counter: &mut usize) -> InternedString {
251+
let c = *counter;
252+
*counter += 1;
253+
254+
Name::intern(&format!("'{:?}", c)).as_interned_str()
255+
}
256+
}

src/librustc_mir/borrow_check/nll/region_infer/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
256256
fn init_universal_regions(&mut self) {
257257
// Update the names (if any)
258258
for (external_name, variable) in self.universal_regions.named_universal_regions() {
259+
debug!(
260+
"init_universal_regions: region {:?} has external name {:?}",
261+
variable,
262+
external_name
263+
);
259264
self.definitions[variable].external_name = Some(external_name);
260265
}
261266

0 commit comments

Comments
 (0)