Skip to content

Commit 32772fd

Browse files
committed
Auto merge of #52572 - davidtwco:issue-51027, r=nikomatsakis
NLL diagnostics replaced nice closure errors w/ indecipherable free region errors Fixes #51027. r? @nikomatsakis
2 parents d3b3bc5 + c64db00 commit 32772fd

19 files changed

+367
-157
lines changed

src/librustc/mir/tcx.rs

+33
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,39 @@ impl<'tcx> Place<'tcx> {
119119
proj.base.ty(local_decls, tcx).projection_ty(tcx, &proj.elem),
120120
}
121121
}
122+
123+
/// If this is a field projection, and the field is being projected from a closure type,
124+
/// then returns the index of the field being projected. Note that this closure will always
125+
/// be `self` in the current MIR, because that is the only time we directly access the fields
126+
/// of a closure type.
127+
pub fn is_upvar_field_projection<'cx, 'gcx>(&self, mir: &'cx Mir<'tcx>,
128+
tcx: &TyCtxt<'cx, 'gcx, 'tcx>) -> Option<Field> {
129+
let place = if let Place::Projection(ref proj) = self {
130+
if let ProjectionElem::Deref = proj.elem {
131+
&proj.base
132+
} else {
133+
self
134+
}
135+
} else {
136+
self
137+
};
138+
139+
match place {
140+
Place::Projection(ref proj) => match proj.elem {
141+
ProjectionElem::Field(field, _ty) => {
142+
let base_ty = proj.base.ty(mir, *tcx).to_ty(*tcx);
143+
144+
if base_ty.is_closure() || base_ty.is_generator() {
145+
Some(field)
146+
} else {
147+
None
148+
}
149+
},
150+
_ => None,
151+
},
152+
_ => None,
153+
}
154+
}
122155
}
123156

124157
pub enum RvalueInitializationState {

src/librustc_mir/borrow_check/error_reporting.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
726726
Place::Projection(ref proj) => {
727727
match proj.elem {
728728
ProjectionElem::Deref => {
729-
if let Some(field) = self.is_upvar_field_projection(&proj.base) {
729+
let upvar_field_projection = place.is_upvar_field_projection(
730+
self.mir, &self.tcx);
731+
if let Some(field) = upvar_field_projection {
730732
let var_index = field.index();
731733
let name = self.mir.upvar_decls[var_index].debug_name.to_string();
732734
if self.mir.upvar_decls[var_index].by_ref {
@@ -785,7 +787,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
785787
ProjectionElem::Field(field, _ty) => {
786788
autoderef = true;
787789

788-
if let Some(field) = self.is_upvar_field_projection(place) {
790+
let upvar_field_projection = place.is_upvar_field_projection(
791+
self.mir, &self.tcx);
792+
if let Some(field) = upvar_field_projection {
789793
let var_index = field.index();
790794
let name = self.mir.upvar_decls[var_index].debug_name.to_string();
791795
buf.push_str(&name);

src/librustc_mir/borrow_check/mod.rs

+8-26
Original file line numberDiff line numberDiff line change
@@ -1214,7 +1214,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
12141214
}
12151215
Operand::Move(ref place @ Place::Projection(_))
12161216
| Operand::Copy(ref place @ Place::Projection(_)) => {
1217-
if let Some(field) = self.is_upvar_field_projection(place) {
1217+
if let Some(field) = place.is_upvar_field_projection(
1218+
self.mir, &self.tcx) {
12181219
self.used_mut_upvars.push(field);
12191220
}
12201221
}
@@ -1803,7 +1804,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
18031804
place: place @ Place::Projection(_),
18041805
is_local_mutation_allowed: _,
18051806
} => {
1806-
if let Some(field) = self.is_upvar_field_projection(&place) {
1807+
if let Some(field) = place.is_upvar_field_projection(self.mir, &self.tcx) {
18071808
self.used_mut_upvars.push(field);
18081809
}
18091810
}
@@ -1866,7 +1867,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
18661867
// Mutably borrowed data is mutable, but only if we have a
18671868
// unique path to the `&mut`
18681869
hir::MutMutable => {
1869-
let mode = match self.is_upvar_field_projection(&proj.base)
1870+
let mode = match place.is_upvar_field_projection(
1871+
self.mir, &self.tcx)
18701872
{
18711873
Some(field)
18721874
if {
@@ -1911,7 +1913,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
19111913
| ProjectionElem::ConstantIndex { .. }
19121914
| ProjectionElem::Subslice { .. }
19131915
| ProjectionElem::Downcast(..) => {
1914-
if let Some(field) = self.is_upvar_field_projection(place) {
1916+
let upvar_field_projection = place.is_upvar_field_projection(
1917+
self.mir, &self.tcx);
1918+
if let Some(field) = upvar_field_projection {
19151919
let decl = &self.mir.upvar_decls[field.index()];
19161920
debug!(
19171921
"decl.mutability={:?} local_mutation_is_allowed={:?} place={:?}",
@@ -1965,28 +1969,6 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
19651969
}
19661970
}
19671971
}
1968-
1969-
/// If this is a field projection, and the field is being projected from a closure type,
1970-
/// then returns the index of the field being projected. Note that this closure will always
1971-
/// be `self` in the current MIR, because that is the only time we directly access the fields
1972-
/// of a closure type.
1973-
fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option<Field> {
1974-
match *place {
1975-
Place::Projection(ref proj) => match proj.elem {
1976-
ProjectionElem::Field(field, _ty) => {
1977-
let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
1978-
1979-
if base_ty.is_closure() || base_ty.is_generator() {
1980-
Some(field)
1981-
} else {
1982-
None
1983-
}
1984-
}
1985-
_ => None,
1986-
},
1987-
_ => None,
1988-
}
1989-
}
19901972
}
19911973

19921974
#[derive(Copy, Clone, PartialEq, Eq, Debug)]

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

+104-22
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use std::fmt;
2222
use syntax_pos::Span;
2323

2424
mod region_name;
25+
mod var_name;
2526

2627
/// Constraints that are considered interesting can be categorized to
2728
/// determine why they are interesting. Order of variants indicates
@@ -30,7 +31,9 @@ mod region_name;
3031
enum ConstraintCategory {
3132
Cast,
3233
Assignment,
34+
AssignmentToUpvar,
3335
Return,
36+
CallArgumentToUpvar,
3437
CallArgument,
3538
Other,
3639
Boring,
@@ -39,10 +42,12 @@ enum ConstraintCategory {
3942
impl fmt::Display for ConstraintCategory {
4043
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4144
match self {
42-
ConstraintCategory::Assignment => write!(f, "assignment"),
45+
ConstraintCategory::Assignment |
46+
ConstraintCategory::AssignmentToUpvar => write!(f, "assignment"),
4347
ConstraintCategory::Return => write!(f, "return"),
4448
ConstraintCategory::Cast => write!(f, "cast"),
45-
ConstraintCategory::CallArgument => write!(f, "argument"),
49+
ConstraintCategory::CallArgument |
50+
ConstraintCategory::CallArgumentToUpvar => write!(f, "argument"),
4651
_ => write!(f, "free region"),
4752
}
4853
}
@@ -130,8 +135,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
130135
&self,
131136
index: ConstraintIndex,
132137
mir: &Mir<'tcx>,
138+
_infcx: &InferCtxt<'_, '_, 'tcx>,
133139
) -> (ConstraintCategory, Span) {
134140
let constraint = self.constraints[index];
141+
debug!("classify_constraint: constraint={:?}", constraint);
135142
let span = constraint.locations.span(mir);
136143
let location = constraint.locations.from_location().unwrap_or(Location::START);
137144

@@ -140,8 +147,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
140147
}
141148

142149
let data = &mir[location.block];
150+
debug!("classify_constraint: location={:?} data={:?}", location, data);
143151
let category = if location.statement_index == data.statements.len() {
144152
if let Some(ref terminator) = data.terminator {
153+
debug!("classify_constraint: terminator.kind={:?}", terminator.kind);
145154
match terminator.kind {
146155
TerminatorKind::DropAndReplace { .. } => ConstraintCategory::Assignment,
147156
TerminatorKind::Call { .. } => ConstraintCategory::CallArgument,
@@ -152,14 +161,17 @@ impl<'tcx> RegionInferenceContext<'tcx> {
152161
}
153162
} else {
154163
let statement = &data.statements[location.statement_index];
164+
debug!("classify_constraint: statement.kind={:?}", statement.kind);
155165
match statement.kind {
156166
StatementKind::Assign(ref place, ref rvalue) => {
167+
debug!("classify_constraint: place={:?} rvalue={:?}", place, rvalue);
157168
if *place == Place::Local(mir::RETURN_PLACE) {
158169
ConstraintCategory::Return
159170
} else {
160171
match rvalue {
161172
Rvalue::Cast(..) => ConstraintCategory::Cast,
162-
Rvalue::Use(..) => ConstraintCategory::Assignment,
173+
Rvalue::Use(..) |
174+
Rvalue::Aggregate(..) => ConstraintCategory::Assignment,
163175
_ => ConstraintCategory::Other,
164176
}
165177
}
@@ -208,7 +220,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
208220

209221
// Classify each of the constraints along the path.
210222
let mut categorized_path: Vec<(ConstraintCategory, Span)> = path.iter()
211-
.map(|&index| self.classify_constraint(index, mir))
223+
.map(|&index| self.classify_constraint(index, mir, infcx))
212224
.collect();
213225
debug!("report_error: categorized_path={:?}", categorized_path);
214226

@@ -218,30 +230,100 @@ impl<'tcx> RegionInferenceContext<'tcx> {
218230

219231
// Get a span
220232
let (category, span) = categorized_path.first().unwrap();
233+
234+
let category = match (
235+
category,
236+
self.universal_regions.is_local_free_region(fr),
237+
self.universal_regions.is_local_free_region(outlived_fr),
238+
) {
239+
(ConstraintCategory::Assignment, true, false) =>
240+
&ConstraintCategory::AssignmentToUpvar,
241+
(ConstraintCategory::CallArgument, true, false) =>
242+
&ConstraintCategory::CallArgumentToUpvar,
243+
(category, _, _) => category,
244+
};
245+
246+
debug!("report_error: category={:?}", category);
247+
match category {
248+
ConstraintCategory::AssignmentToUpvar |
249+
ConstraintCategory::CallArgumentToUpvar =>
250+
self.report_closure_error(mir, infcx, mir_def_id, fr, outlived_fr, category, span),
251+
_ =>
252+
self.report_general_error(mir, infcx, mir_def_id, fr, outlived_fr, category, span),
253+
}
254+
}
255+
256+
fn report_closure_error(
257+
&self,
258+
mir: &Mir<'tcx>,
259+
infcx: &InferCtxt<'_, '_, 'tcx>,
260+
mir_def_id: DefId,
261+
fr: RegionVid,
262+
outlived_fr: RegionVid,
263+
category: &ConstraintCategory,
264+
span: &Span,
265+
) {
266+
let fr_name_and_span = self.get_var_name_and_span_for_region(
267+
infcx.tcx, mir, fr);
268+
let outlived_fr_name_and_span = self.get_var_name_and_span_for_region(
269+
infcx.tcx, mir,outlived_fr);
270+
271+
if fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none() {
272+
return self.report_general_error(mir, infcx, mir_def_id, fr, outlived_fr, category,
273+
span);
274+
}
275+
276+
let diag = &mut infcx.tcx.sess.struct_span_err(
277+
*span, &format!("borrowed data escapes outside of closure"),
278+
);
279+
280+
if let Some((outlived_fr_name, outlived_fr_span)) = outlived_fr_name_and_span {
281+
if let Some(name) = outlived_fr_name {
282+
diag.span_label(
283+
outlived_fr_span,
284+
format!("`{}` is declared here, outside of the closure body", name),
285+
);
286+
}
287+
}
288+
289+
if let Some((fr_name, fr_span)) = fr_name_and_span {
290+
if let Some(name) = fr_name {
291+
diag.span_label(
292+
fr_span,
293+
format!("`{}` is a reference that is only valid in the closure body", name),
294+
);
295+
296+
diag.span_label(*span, format!("`{}` escapes the closure body here", name));
297+
}
298+
}
299+
300+
diag.emit();
301+
}
302+
303+
fn report_general_error(
304+
&self,
305+
mir: &Mir<'tcx>,
306+
infcx: &InferCtxt<'_, '_, 'tcx>,
307+
mir_def_id: DefId,
308+
fr: RegionVid,
309+
outlived_fr: RegionVid,
310+
category: &ConstraintCategory,
311+
span: &Span,
312+
) {
221313
let diag = &mut infcx.tcx.sess.struct_span_err(
222-
*span,
223-
&format!("unsatisfied lifetime constraints"), // FIXME
314+
*span, &format!("unsatisfied lifetime constraints"), // FIXME
224315
);
225316

226-
// Figure out how we can refer
227317
let counter = &mut 1;
228-
let fr_name = self.give_region_a_name(infcx.tcx, mir, mir_def_id, fr, counter, diag);
318+
let fr_name = self.give_region_a_name(
319+
infcx.tcx, mir, mir_def_id, fr, counter, diag);
229320
let outlived_fr_name = self.give_region_a_name(
230-
infcx.tcx,
231-
mir,
232-
mir_def_id,
233-
outlived_fr,
234-
counter,
235-
diag,
236-
);
321+
infcx.tcx, mir, mir_def_id, outlived_fr, counter, diag);
237322

238-
diag.span_label(
239-
*span,
240-
format!(
241-
"{} requires that `{}` must outlive `{}`",
242-
category, fr_name, outlived_fr_name,
243-
),
244-
);
323+
diag.span_label(*span, format!(
324+
"{} requires that `{}` must outlive `{}`",
325+
category, fr_name, outlived_fr_name,
326+
));
245327

246328
diag.emit();
247329
}

0 commit comments

Comments
 (0)