Skip to content

Commit 91b71f5

Browse files
committed
Identify borrows captured by trait objects.
This commit enhances `LaterUseKind` detection to identify when a borrow is captured by a trait object which helps explain why there is a borrow error.
1 parent 987a50b commit 91b71f5

File tree

2 files changed

+137
-38
lines changed

2 files changed

+137
-38
lines changed

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

+136-37
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ use borrow_check::error_reporting::UseSpans;
1313
use borrow_check::nll::region_infer::Cause;
1414
use borrow_check::{Context, MirBorrowckCtxt, WriteKind};
1515
use rustc::ty::{self, Region, TyCtxt};
16-
use rustc::mir::{FakeReadCause, Local, Location, Mir, Operand};
17-
use rustc::mir::{Place, StatementKind, TerminatorKind};
16+
use rustc::mir::{
17+
FakeReadCause, Local, Location, Mir, Operand, Place, Rvalue, Statement, StatementKind,
18+
TerminatorKind
19+
};
1820
use rustc_errors::DiagnosticBuilder;
1921
use syntax_pos::Span;
2022

@@ -34,6 +36,7 @@ pub(in borrow_check) enum BorrowExplanation<'tcx> {
3436

3537
#[derive(Clone, Copy)]
3638
pub(in borrow_check) enum LaterUseKind {
39+
TraitCapture,
3740
ClosureCapture,
3841
Call,
3942
FakeLetRead,
@@ -51,6 +54,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
5154
match *self {
5255
BorrowExplanation::UsedLater(later_use_kind, var_or_use_span) => {
5356
let message = match later_use_kind {
57+
LaterUseKind::TraitCapture => "borrow later captured here by trait object",
5458
LaterUseKind::ClosureCapture => "borrow later captured here by closure",
5559
LaterUseKind::Call => "borrow later used by call",
5660
LaterUseKind::FakeLetRead => "borrow later stored here",
@@ -60,9 +64,10 @@ impl<'tcx> BorrowExplanation<'tcx> {
6064
},
6165
BorrowExplanation::UsedLaterInLoop(later_use_kind, var_or_use_span) => {
6266
let message = match later_use_kind {
63-
LaterUseKind::ClosureCapture => {
64-
"borrow captured here by closure, in later iteration of loop"
65-
},
67+
LaterUseKind::TraitCapture =>
68+
"borrow later captured here by trait object, in later iteration of loop",
69+
LaterUseKind::ClosureCapture =>
70+
"borrow captured here by closure, in later iteration of loop",
6671
LaterUseKind::Call => "borrow used by call, in later iteration of loop",
6772
LaterUseKind::FakeLetRead => "borrow later stored here",
6873
LaterUseKind::Other => "borrow used here, in later iteration of loop",
@@ -200,13 +205,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
200205
.or_else(|| self.borrow_spans(span, location));
201206

202207
if self.is_borrow_location_in_loop(context.loc) {
203-
let later_use = self.later_use_kind(spans, location);
208+
let later_use = self.later_use_kind(borrow, spans, location);
204209
BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1)
205210
} else {
206211
// Check if the location represents a `FakeRead`, and adapt the error
207212
// message to the `FakeReadCause` it is from: in particular,
208213
// the ones inserted in optimized `let var = <expr>` patterns.
209-
let later_use = self.later_use_kind(spans, location);
214+
let later_use = self.later_use_kind(borrow, spans, location);
210215
BorrowExplanation::UsedLater(later_use.0, later_use.1)
211216
}
212217
}
@@ -316,42 +321,136 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
316321
false
317322
}
318323

319-
fn later_use_kind(&self, use_spans: UseSpans, location: Location) -> (LaterUseKind, Span) {
320-
use self::LaterUseKind::*;
321-
322-
let block = &self.mir.basic_blocks()[location.block];
324+
/// Determine how the borrow was later used.
325+
fn later_use_kind(
326+
&self,
327+
borrow: &BorrowData<'tcx>,
328+
use_spans: UseSpans,
329+
location: Location
330+
) -> (LaterUseKind, Span) {
323331
match use_spans {
324-
UseSpans::ClosureUse { var_span, .. } => (LaterUseKind::ClosureCapture, var_span),
332+
UseSpans::ClosureUse { var_span, .. } => {
333+
// Used in a closure.
334+
(LaterUseKind::ClosureCapture, var_span)
335+
},
325336
UseSpans::OtherUse(span) => {
326-
(if let Some(stmt) = block.statements.get(location.statement_index) {
327-
match stmt.kind {
328-
StatementKind::FakeRead(FakeReadCause::ForLet, _) => FakeLetRead,
329-
_ => Other,
337+
let block = &self.mir.basic_blocks()[location.block];
338+
339+
let kind = if let Some(&Statement {
340+
kind: StatementKind::FakeRead(FakeReadCause::ForLet, _),
341+
..
342+
}) = block.statements.get(location.statement_index) {
343+
LaterUseKind::FakeLetRead
344+
} else if self.was_captured_by_trait_object(borrow) {
345+
LaterUseKind::TraitCapture
346+
} else if location.statement_index == block.statements.len() {
347+
if let TerminatorKind::Call {
348+
ref func, from_hir_call: true, ..
349+
} = block.terminator().kind {
350+
// Just point to the function, to reduce the chance of overlapping spans.
351+
let function_span = match func {
352+
Operand::Constant(c) => c.span,
353+
Operand::Copy(Place::Local(l)) | Operand::Move(Place::Local(l)) => {
354+
let local_decl = &self.mir.local_decls[*l];
355+
if local_decl.name.is_none() {
356+
local_decl.source_info.span
357+
} else {
358+
span
359+
}
360+
},
361+
_ => span,
362+
};
363+
return (LaterUseKind::Call, function_span);
364+
} else {
365+
LaterUseKind::Other
330366
}
331367
} else {
332-
assert_eq!(location.statement_index, block.statements.len());
333-
match block.terminator().kind {
334-
TerminatorKind::Call { ref func, from_hir_call: true, .. } => {
335-
// Just point to the function, to reduce the chance
336-
// of overlapping spans.
337-
let function_span = match func {
338-
Operand::Constant(c) => c.span,
339-
Operand::Copy(Place::Local(l)) | Operand::Move(Place::Local(l)) => {
340-
let local_decl = &self.mir.local_decls[*l];
341-
if local_decl.name.is_none() {
342-
local_decl.source_info.span
343-
} else {
344-
span
345-
}
346-
},
347-
_ => span,
348-
};
349-
return (Call, function_span);
350-
},
351-
_ => Other,
368+
LaterUseKind::Other
369+
};
370+
371+
(kind, span)
372+
}
373+
}
374+
}
375+
376+
/// Check if a borrowed value was captured by a trait object.
377+
fn was_captured_by_trait_object(&self, borrow: &BorrowData<'tcx>) -> bool {
378+
// In order to check if a value was captured by a trait object, we want to look through
379+
// statements after the reserve location in the current block. We expect the reserve
380+
// location to be a statement assigning to a local. We follow that local in the subsequent
381+
// statements, checking for an assignment of our local (or something intermediate that
382+
// it was assigned into) that results in a trait object.
383+
let location = borrow.reserve_location;
384+
let block = &self.mir[location.block];
385+
let stmt = block.statements.get(location.statement_index);
386+
debug!(
387+
"was_captured_by_trait_object: location={:?} block={:?} stmt={:?}",
388+
location, block, stmt
389+
);
390+
let mut target = if let Some(&Statement {
391+
kind: StatementKind::Assign(Place::Local(local), _),
392+
..
393+
}) = stmt {
394+
local
395+
} else {
396+
return false;
397+
};
398+
399+
debug!("was_captured_by_trait_object: target={:?}", target);
400+
for stmt in &block.statements[location.statement_index + 1..] {
401+
debug!("was_captured_by_trait_object: stmt={:?}", stmt);
402+
// Simple case where our target is assigned into another local, and we start
403+
// watching that local instead.
404+
if let StatementKind::Assign(
405+
Place::Local(into),
406+
box Rvalue::Use(operand),
407+
) = &stmt.kind {
408+
debug!("was_captured_by_trait_object: target={:?} operand={:?}", target, operand);
409+
match operand {
410+
Operand::Copy(Place::Local(from)) |
411+
Operand::Move(Place::Local(from)) if *from == target => target = *into,
412+
_ => {},
413+
}
414+
}
415+
}
416+
417+
if let Some(terminator) = &block.terminator {
418+
if let TerminatorKind::Call {
419+
destination: Some((Place::Local(dest), _)),
420+
args,
421+
..
422+
} = &terminator.kind {
423+
debug!(
424+
"was_captured_by_trait_object: target={:?} dest={:?} args={:?}",
425+
target, dest, args
426+
);
427+
let mut found_target = false;
428+
for arg in args {
429+
if let Operand::Move(Place::Local(potential)) = arg {
430+
if *potential == target {
431+
found_target = true;
432+
}
433+
}
434+
}
435+
436+
if found_target {
437+
let local_decl_ty = &self.mir.local_decls[*dest].ty;
438+
debug!("was_captured_by_trait_object: local_decl_ty={:?}", local_decl_ty);
439+
match local_decl_ty.sty {
440+
// `&dyn Trait`
441+
ty::TyKind::Ref(_, ty, _) if ty.is_trait() => return true,
442+
// `Box<dyn Trait>`
443+
_ if local_decl_ty.is_box() && local_decl_ty.boxed_ty().is_trait() =>
444+
return true,
445+
// `dyn Trait`
446+
_ if local_decl_ty.is_trait() => return true,
447+
// Anything else.
448+
_ => return false,
352449
}
353-
}, span)
450+
}
354451
}
355452
}
453+
454+
false
356455
}
357456
}

src/test/ui/span/regions-close-over-type-parameter-2.nll.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0597]: `tmp0` does not live long enough
44
LL | let tmp1 = &tmp0;
55
| ^^^^^ borrowed value does not live long enough
66
LL | repeater3(tmp1)
7-
| --------------- borrow later used here
7+
| --------------- borrow later captured here by trait object
88
LL | };
99
| - `tmp0` dropped here while still borrowed
1010

0 commit comments

Comments
 (0)