Skip to content

Commit 80bc171

Browse files
committed
Improve diagnostics for borrow-check errors that result from drops of temporary r-values.
Changed `BorrowExplanation::UsedLaterWhenDropped` to handle both named locals and also unnamed (aka temporaries). If the dropped temporary does not implement `Drop`, then we print its full type; but when the dropped temporary is itself an ADT `D` that implements `Drop`, then diagnostic points the user directly at `D`.
1 parent 504ab34 commit 80bc171

File tree

3 files changed

+108
-47
lines changed

3 files changed

+108
-47
lines changed

src/librustc_mir/borrow_check/error_reporting.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
770770
match explanation {
771771
BorrowExplanation::UsedLater(..)
772772
| BorrowExplanation::UsedLaterInLoop(..)
773-
| BorrowExplanation::UsedLaterWhenDropped(..) => {
773+
| BorrowExplanation::UsedLaterWhenDropped { .. } => {
774774
// Only give this note and suggestion if it could be relevant.
775775
err.note("consider using a `let` binding to create a longer lived value");
776776
}

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

+101-46
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,22 @@ use borrow_check::borrow_set::BorrowData;
1212
use borrow_check::error_reporting::UseSpans;
1313
use borrow_check::nll::region_infer::Cause;
1414
use borrow_check::{Context, MirBorrowckCtxt, WriteKind};
15-
use rustc::ty::{Region, TyCtxt};
16-
use rustc::mir::{FakeReadCause, Location, Mir, Operand, Place, StatementKind, TerminatorKind};
15+
use rustc::ty::{self, Region, TyCtxt};
16+
use rustc::mir::{FakeReadCause, Local, Location, Mir, Operand};
17+
use rustc::mir::{Place, StatementKind, TerminatorKind};
1718
use rustc_errors::DiagnosticBuilder;
1819
use syntax_pos::Span;
19-
use syntax_pos::symbol::Symbol;
2020

2121
mod find_use;
2222

2323
pub(in borrow_check) enum BorrowExplanation<'tcx> {
2424
UsedLater(LaterUseKind, Span),
2525
UsedLaterInLoop(LaterUseKind, Span),
26-
UsedLaterWhenDropped(Span, Symbol, bool),
26+
UsedLaterWhenDropped {
27+
drop_loc: Location,
28+
dropped_local: Local,
29+
should_note_order: bool,
30+
},
2731
MustBeValidFor(Region<'tcx>),
2832
Unexplained,
2933
}
@@ -40,7 +44,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
4044
pub(in borrow_check) fn add_explanation_to_diagnostic<'cx, 'gcx>(
4145
&self,
4246
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
43-
_mir: &Mir<'tcx>,
47+
mir: &Mir<'tcx>,
4448
err: &mut DiagnosticBuilder<'_>,
4549
borrow_desc: &str,
4650
) {
@@ -65,23 +69,76 @@ impl<'tcx> BorrowExplanation<'tcx> {
6569
};
6670
err.span_label(var_or_use_span, format!("{}{}", borrow_desc, message));
6771
},
68-
BorrowExplanation::UsedLaterWhenDropped(span, local_name, should_note_order) => {
69-
err.span_label(
70-
span,
71-
format!(
72-
"{}borrow later used here, when `{}` is dropped",
73-
borrow_desc,
74-
local_name,
75-
),
76-
);
72+
BorrowExplanation::UsedLaterWhenDropped { drop_loc, dropped_local,
73+
should_note_order } =>
74+
{
75+
let local_decl = &mir.local_decls[dropped_local];
76+
let (dtor_desc, type_desc) = match local_decl.ty.sty {
77+
// If type is an ADT that implements Drop, then
78+
// simplify output by reporting just the ADT name.
79+
ty::Adt(adt, _substs) if adt.has_dtor(tcx) && !adt.is_box() =>
80+
("`Drop` code", format!("type `{}`", tcx.item_path_str(adt.did))),
81+
82+
// Otherwise, just report the whole type (and use
83+
// the intentionally fuzzy phrase "destructor")
84+
ty::Closure(..) =>
85+
("destructor", format!("closure")),
86+
ty::Generator(..) =>
87+
("destructor", format!("generator")),
88+
89+
_ => ("destructor", format!("type `{}`", local_decl.ty)),
90+
};
91+
92+
match local_decl.name {
93+
Some(local_name) => {
94+
let message =
95+
format!("{B}borrow might be used here, when `{LOC}` is dropped \
96+
and runs the {DTOR} for {TYPE}",
97+
B=borrow_desc, LOC=local_name, TYPE=type_desc, DTOR=dtor_desc);
98+
err.span_label(mir.source_info(drop_loc).span, message);
99+
100+
if should_note_order {
101+
err.note(
102+
"values in a scope are dropped \
103+
in the opposite order they are defined",
104+
);
105+
}
106+
}
107+
None => {
108+
err.span_label(local_decl.source_info.span,
109+
format!("a temporary with access to the {B}borrow \
110+
is created here ...",
111+
B=borrow_desc));
112+
let message =
113+
format!("... and the {B}borrow might be used here, \
114+
when that temporary is dropped \
115+
and runs the {DTOR} for {TYPE}",
116+
B=borrow_desc, TYPE=type_desc, DTOR=dtor_desc);
117+
err.span_label(mir.source_info(drop_loc).span, message);
118+
119+
if let Some(info) = &local_decl.is_block_tail {
120+
// FIXME: use span_suggestion instead, highlighting the
121+
// whole block tail expression.
122+
let msg = if info.tail_result_is_ignored {
123+
"The temporary is part of an expression at the end of a block. \
124+
Consider adding semicolon after the expression so its temporaries \
125+
are dropped sooner, before the local variables declared by the \
126+
block are dropped."
127+
} else {
128+
"The temporary is part of an expression at the end of a block. \
129+
Consider forcing this temporary to be dropped sooner, before \
130+
the block's local variables are dropped. \
131+
For example, you could save the expression's value in a new \
132+
local variable `x` and then make `x` be the expression \
133+
at the end of the block."
134+
};
77135

78-
if should_note_order {
79-
err.note(
80-
"values in a scope are dropped \
81-
in the opposite order they are defined",
82-
);
136+
err.note(msg);
137+
}
138+
}
83139
}
84-
},
140+
}
141+
85142
BorrowExplanation::MustBeValidFor(region) => {
86143
tcx.note_and_explain_free_region(
87144
err,
@@ -116,8 +173,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
116173
kind_place: Option<(WriteKind, &Place<'tcx>)>,
117174
) -> BorrowExplanation<'tcx> {
118175
debug!(
119-
"explain_why_borrow_contains_point(context={:?}, borrow={:?})",
120-
context, borrow,
176+
"explain_why_borrow_contains_point(context={:?}, borrow={:?}, kind_place={:?})",
177+
context, borrow, kind_place
121178
);
122179

123180
let regioncx = &self.nonlexical_regioncx;
@@ -154,32 +211,30 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
154211
}
155212
}
156213

157-
Some(Cause::DropVar(local, location)) => match &mir.local_decls[local].name {
158-
Some(local_name) => {
159-
let mut should_note_order = false;
160-
if let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place {
161-
if let Place::Local(borrowed_local) = place {
162-
let dropped_local_scope = mir.local_decls[local].visibility_scope;
163-
let borrowed_local_scope =
164-
mir.local_decls[*borrowed_local].visibility_scope;
214+
Some(Cause::DropVar(local, location)) => {
215+
let mut should_note_order = false;
216+
if mir.local_decls[local].name.is_some() {
217+
if let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place {
218+
if let Place::Local(borrowed_local) = place {
219+
let dropped_local_scope = mir.local_decls[local].visibility_scope;
220+
let borrowed_local_scope =
221+
mir.local_decls[*borrowed_local].visibility_scope;
165222

166-
if mir.is_sub_scope(borrowed_local_scope, dropped_local_scope)
167-
&& local != *borrowed_local
168-
{
169-
should_note_order = true;
170-
}
171-
}
172-
}
173-
174-
BorrowExplanation::UsedLaterWhenDropped(
175-
mir.source_info(location).span,
176-
*local_name,
177-
should_note_order
178-
)
179-
},
223+
if mir.is_sub_scope(borrowed_local_scope, dropped_local_scope)
224+
&& local != *borrowed_local
225+
{
226+
should_note_order = true;
227+
}
228+
}
229+
}
230+
}
180231

181-
None => BorrowExplanation::Unexplained,
182-
},
232+
BorrowExplanation::UsedLaterWhenDropped {
233+
drop_loc: location,
234+
dropped_local: local,
235+
should_note_order,
236+
}
237+
}
183238

184239
None => if let Some(region) = regioncx.to_error_region(region_sub) {
185240
BorrowExplanation::MustBeValidFor(region)

src/librustc_mir/build/expr/stmt.rs

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
2020
let source_info = this.source_info(expr.span);
2121
// Handle a number of expressions that don't need a destination at all. This
2222
// avoids needing a mountain of temporary `()` variables.
23+
let expr2 = expr.clone();
2324
match expr.kind {
2425
ExprKind::Scope {
2526
region_scope,
@@ -40,6 +41,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
4041
// is better for borrowck interaction with overloaded
4142
// operators like x[j] = x[i].
4243

44+
debug!("stmt_expr Assign block_context.push(SubExpr) : {:?}", expr2);
4345
this.block_context.push(BlockFrame::SubExpr);
4446

4547
// Generate better code for things that don't need to be
@@ -69,6 +71,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
6971
let lhs = this.hir.mirror(lhs);
7072
let lhs_ty = lhs.ty;
7173

74+
debug!("stmt_expr AssignOp block_context.push(SubExpr) : {:?}", expr2);
7275
this.block_context.push(BlockFrame::SubExpr);
7376

7477
// As above, RTL.
@@ -120,6 +123,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
120123
(break_block, region_scope, break_destination.clone())
121124
};
122125
if let Some(value) = value {
126+
debug!("stmt_expr Break val block_context.push(SubExpr) : {:?}", expr2);
123127
this.block_context.push(BlockFrame::SubExpr);
124128
unpack!(block = this.into(&destination, block, value));
125129
this.block_context.pop();
@@ -132,6 +136,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
132136
ExprKind::Return { value } => {
133137
block = match value {
134138
Some(value) => {
139+
debug!("stmt_expr Return val block_context.push(SubExpr) : {:?}", expr2);
135140
this.block_context.push(BlockFrame::SubExpr);
136141
let result = unpack!(this.into(&Place::Local(RETURN_PLACE), block, value));
137142
this.block_context.pop();
@@ -153,6 +158,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
153158
outputs,
154159
inputs,
155160
} => {
161+
debug!("stmt_expr InlineAsm block_context.push(SubExpr) : {:?}", expr2);
156162
this.block_context.push(BlockFrame::SubExpr);
157163
let outputs = outputs
158164
.into_iter()

0 commit comments

Comments
 (0)