Skip to content

Commit 571eec6

Browse files
committed
Improved closure errors.
1 parent 24c5751 commit 571eec6

File tree

11 files changed

+299
-131
lines changed

11 files changed

+299
-131
lines changed

src/librustc/mir/tcx.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,37 @@ 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>,
129+
recurse: bool) -> Option<Field> {
130+
match *self {
131+
Place::Projection(ref proj) => match proj.elem {
132+
ProjectionElem::Field(field, _ty) => {
133+
let base_ty = proj.base.ty(mir, *tcx).to_ty(*tcx);
134+
135+
if base_ty.is_closure() || base_ty.is_generator() {
136+
Some(field)
137+
} else {
138+
None
139+
}
140+
},
141+
ProjectionElem::Deref => {
142+
if recurse {
143+
proj.base.is_upvar_field_projection(mir, tcx, recurse)
144+
} else {
145+
None
146+
}
147+
},
148+
_ => None,
149+
},
150+
_ => None,
151+
}
152+
}
122153
}
123154

124155
pub enum RvalueInitializationState {

src/librustc_mir/borrow_check/error_reporting.rs

Lines changed: 6 additions & 2 deletions
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 = proj.base.is_upvar_field_projection(
730+
self.mir, &self.tcx, false);
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, false);
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

Lines changed: 8 additions & 26 deletions
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, false) {
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, false) {
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 proj.base.is_upvar_field_projection(
1871+
self.mir, &self.tcx, false)
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, false);
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

Lines changed: 77 additions & 21 deletions
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,6 +31,7 @@ mod region_name;
3031
enum ConstraintCategory {
3132
Cast,
3233
Assignment,
34+
AssignmentToUpvar,
3335
Return,
3436
CallArgument,
3537
Other,
@@ -39,7 +41,8 @@ enum ConstraintCategory {
3941
impl fmt::Display for ConstraintCategory {
4042
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4143
match self {
42-
ConstraintCategory::Assignment => write!(f, "assignment"),
44+
ConstraintCategory::Assignment |
45+
ConstraintCategory::AssignmentToUpvar => write!(f, "assignment"),
4346
ConstraintCategory::Return => write!(f, "return"),
4447
ConstraintCategory::Cast => write!(f, "cast"),
4548
ConstraintCategory::CallArgument => write!(f, "argument"),
@@ -130,6 +133,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
130133
&self,
131134
index: ConstraintIndex,
132135
mir: &Mir<'tcx>,
136+
infcx: &InferCtxt<'_, '_, 'tcx>,
133137
) -> (ConstraintCategory, Span) {
134138
let constraint = self.constraints[index];
135139
debug!("classify_constraint: constraint={:?}", constraint);
@@ -159,7 +163,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
159163
match statement.kind {
160164
StatementKind::Assign(ref place, ref rvalue) => {
161165
debug!("classify_constraint: place={:?} rvalue={:?}", place, rvalue);
162-
if *place == Place::Local(mir::RETURN_PLACE) {
166+
let initial_category = if *place == Place::Local(mir::RETURN_PLACE) {
163167
ConstraintCategory::Return
164168
} else {
165169
match rvalue {
@@ -168,6 +172,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
168172
Rvalue::Aggregate(..) => ConstraintCategory::Assignment,
169173
_ => ConstraintCategory::Other,
170174
}
175+
};
176+
177+
if initial_category == ConstraintCategory::Assignment
178+
&& place.is_upvar_field_projection(mir, &infcx.tcx, true).is_some() {
179+
ConstraintCategory::AssignmentToUpvar
180+
} else {
181+
initial_category
171182
}
172183
}
173184
_ => ConstraintCategory::Other,
@@ -214,7 +225,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
214225

215226
// Classify each of the constraints along the path.
216227
let mut categorized_path: Vec<(ConstraintCategory, Span)> = path.iter()
217-
.map(|&index| self.classify_constraint(index, mir))
228+
.map(|&index| self.classify_constraint(index, mir, infcx))
218229
.collect();
219230
debug!("report_error: categorized_path={:?}", categorized_path);
220231

@@ -224,30 +235,75 @@ impl<'tcx> RegionInferenceContext<'tcx> {
224235

225236
// Get a span
226237
let (category, span) = categorized_path.first().unwrap();
238+
239+
match category {
240+
ConstraintCategory::AssignmentToUpvar =>
241+
self.report_closure_error(mir, infcx, fr, outlived_fr, span),
242+
_ =>
243+
self.report_general_error(mir, infcx, mir_def_id, fr, outlived_fr, category, span),
244+
}
245+
}
246+
247+
fn report_closure_error(
248+
&self,
249+
mir: &Mir<'tcx>,
250+
infcx: &InferCtxt<'_, '_, 'tcx>,
251+
fr: RegionVid,
252+
outlived_fr: RegionVid,
253+
span: &Span,
254+
) {
227255
let diag = &mut infcx.tcx.sess.struct_span_err(
228-
*span,
229-
&format!("unsatisfied lifetime constraints"), // FIXME
256+
*span, &format!("borrowed data escapes outside of closure"),
257+
);
258+
259+
let (outlived_fr_name, outlived_fr_span) = self.get_var_name_and_span_for_region(
260+
infcx.tcx, mir, outlived_fr);
261+
262+
if let Some(name) = outlived_fr_name {
263+
diag.span_label(
264+
outlived_fr_span,
265+
format!("`{}` is declared here, outside of the closure body", name),
266+
);
267+
}
268+
269+
let (fr_name, fr_span) = self.get_var_name_and_span_for_region(infcx.tcx, mir, fr);
270+
271+
if let Some(name) = fr_name {
272+
diag.span_label(
273+
fr_span,
274+
format!("`{}` is a reference that is only valid in the closure body", name),
275+
);
276+
277+
diag.span_label(*span, format!("`{}` escapes the closure body here", name));
278+
}
279+
280+
diag.emit();
281+
}
282+
283+
fn report_general_error(
284+
&self,
285+
mir: &Mir<'tcx>,
286+
infcx: &InferCtxt<'_, '_, 'tcx>,
287+
mir_def_id: DefId,
288+
fr: RegionVid,
289+
outlived_fr: RegionVid,
290+
category: &ConstraintCategory,
291+
span: &Span,
292+
) {
293+
let diag = &mut infcx.tcx.sess.struct_span_err(
294+
*span, &format!("unsatisfied lifetime constraints"), // FIXME
230295
);
231296

232-
// Figure out how we can refer
233297
let counter = &mut 1;
234-
let fr_name = self.give_region_a_name(infcx.tcx, mir, mir_def_id, fr, counter, diag);
298+
let fr_name = self.give_region_a_name(
299+
infcx.tcx, mir, mir_def_id, fr, counter, diag);
235300
let outlived_fr_name = self.give_region_a_name(
236-
infcx.tcx,
237-
mir,
238-
mir_def_id,
239-
outlived_fr,
240-
counter,
241-
diag,
242-
);
301+
infcx.tcx, mir, mir_def_id, outlived_fr, counter, diag);
243302

244-
diag.span_label(
245-
*span,
246-
format!(
247-
"{} requires that `{}` must outlive `{}`",
248-
category, fr_name, outlived_fr_name,
249-
),
250-
);
303+
diag.span_label(*span, format!(
304+
"{} requires that `{}` must outlive `{}`",
305+
category, fr_name, outlived_fr_name,
306+
));
251307

252308
diag.emit();
253309
}

0 commit comments

Comments
 (0)