Skip to content

Commit 79a1385

Browse files
committed
Auto merge of #45989 - davidtwco:issue-45360, r=nikomatsakis
MIR-borrowck: emit "`foo` does not live long enough" instead of borrow errors Fixes #45360. As of writing, contains deduplication of existing errors. r? @nikomatsakis
2 parents 8752aee + a1d55be commit 79a1385

File tree

2 files changed

+119
-25
lines changed

2 files changed

+119
-25
lines changed

src/librustc_mir/borrow_check.rs

+101-20
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
2020
use rustc::mir::{Statement, StatementKind, Terminator, TerminatorKind};
2121
use transform::nll;
2222

23+
use rustc_data_structures::fx::FxHashSet;
2324
use rustc_data_structures::indexed_set::{self, IdxSetBuf};
2425
use rustc_data_structures::indexed_vec::{Idx};
2526

@@ -136,6 +137,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
136137
node_id: id,
137138
move_data: &mdpe.move_data,
138139
param_env: param_env,
140+
storage_drop_or_dead_error_reported: FxHashSet(),
139141
};
140142

141143
let mut state = InProgress::new(flow_borrows,
@@ -153,6 +155,10 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
153155
node_id: ast::NodeId,
154156
move_data: &'cx MoveData<'tcx>,
155157
param_env: ParamEnv<'gcx>,
158+
/// This field keeps track of when storage drop or dead errors are reported
159+
/// in order to stop duplicate error reporting and identify the conditions required
160+
/// for a "temporary value dropped here while still borrowed" error. See #45360.
161+
storage_drop_or_dead_error_reported: FxHashSet<Local>,
156162
}
157163

158164
// (forced to be `pub` due to its use as an associated type below.)
@@ -281,10 +287,15 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
281287
}
282288

283289
StatementKind::StorageDead(local) => {
284-
self.access_lvalue(ContextKind::StorageDead.new(location),
285-
(&Lvalue::Local(local), span),
286-
(Shallow(None), Write(WriteKind::StorageDead)),
287-
flow_state);
290+
if !self.storage_drop_or_dead_error_reported.contains(&local) {
291+
let error_reported = self.access_lvalue(ContextKind::StorageDead.new(location),
292+
(&Lvalue::Local(local), span),
293+
(Shallow(None), Write(WriteKind::StorageDeadOrDrop)), flow_state);
294+
295+
if error_reported {
296+
self.storage_drop_or_dead_error_reported.insert(local);
297+
}
298+
}
288299
}
289300
}
290301
}
@@ -427,24 +438,30 @@ enum ReadKind {
427438

428439
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
429440
enum WriteKind {
430-
StorageDead,
441+
StorageDeadOrDrop,
431442
MutableBorrow(BorrowKind),
432443
Mutate,
433444
Move,
434445
}
435446

436447
impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
448+
/// Checks an access to the given lvalue to see if it is allowed. Examines the set of borrows
449+
/// that are in scope, as well as which paths have been initialized, to ensure that (a) the
450+
/// lvalue is initialized and (b) it is not borrowed in some way that would prevent this
451+
/// access.
452+
///
453+
/// Returns true if an error is reported, false otherwise.
437454
fn access_lvalue(&mut self,
438455
context: Context,
439456
lvalue_span: (&Lvalue<'tcx>, Span),
440457
kind: (ShallowOrDeep, ReadOrWrite),
441-
flow_state: &InProgress<'cx, 'gcx, 'tcx>) {
442-
458+
flow_state: &InProgress<'cx, 'gcx, 'tcx>) -> bool {
443459
let (sd, rw) = kind;
444460

445461
// Check permissions
446462
self.check_access_permissions(lvalue_span, rw);
447463

464+
let mut error_reported = false;
448465
self.each_borrow_involving_path(
449466
context, (sd, lvalue_span.0), flow_state, |this, _index, borrow, common_prefix| {
450467
match (rw, borrow.kind) {
@@ -454,13 +471,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
454471
(Read(kind), BorrowKind::Unique) |
455472
(Read(kind), BorrowKind::Mut) => {
456473
match kind {
457-
ReadKind::Copy =>
474+
ReadKind::Copy => {
475+
error_reported = true;
458476
this.report_use_while_mutably_borrowed(
459-
context, lvalue_span, borrow),
477+
context, lvalue_span, borrow)
478+
},
460479
ReadKind::Borrow(bk) => {
461480
let end_issued_loan_span =
462481
flow_state.borrows.base_results.operator().opt_region_end_span(
463482
&borrow.region);
483+
error_reported = true;
464484
this.report_conflicting_borrow(
465485
context, common_prefix, lvalue_span, bk,
466486
&borrow, end_issued_loan_span)
@@ -474,22 +494,35 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
474494
let end_issued_loan_span =
475495
flow_state.borrows.base_results.operator().opt_region_end_span(
476496
&borrow.region);
497+
error_reported = true;
477498
this.report_conflicting_borrow(
478499
context, common_prefix, lvalue_span, bk,
479500
&borrow, end_issued_loan_span)
480501
}
481-
WriteKind::StorageDead |
482-
WriteKind::Mutate =>
502+
WriteKind::StorageDeadOrDrop => {
503+
let end_span =
504+
flow_state.borrows.base_results.operator().opt_region_end_span(
505+
&borrow.region);
506+
error_reported = true;
507+
this.report_borrowed_value_does_not_live_long_enough(
508+
context, lvalue_span, end_span)
509+
},
510+
WriteKind::Mutate => {
511+
error_reported = true;
483512
this.report_illegal_mutation_of_borrowed(
484-
context, lvalue_span, borrow),
485-
WriteKind::Move =>
513+
context, lvalue_span, borrow)
514+
},
515+
WriteKind::Move => {
516+
error_reported = true;
486517
this.report_move_out_while_borrowed(
487-
context, lvalue_span, &borrow),
518+
context, lvalue_span, &borrow)
519+
},
488520
}
489521
Control::Break
490522
}
491523
}
492524
});
525+
error_reported
493526
}
494527

495528
fn mutate_lvalue(&mut self,
@@ -604,12 +637,39 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
604637
let erased_ty = gcx.lift(&self.tcx.erase_regions(&ty)).unwrap();
605638
let moves_by_default = erased_ty.moves_by_default(gcx, self.param_env, DUMMY_SP);
606639

607-
if moves_by_default {
608-
// move of lvalue: check if this is move of already borrowed path
609-
self.access_lvalue(context, lvalue_span, (Deep, Write(WriteKind::Move)), flow_state);
610-
} else {
611-
// copy of lvalue: check if this is "copy of frozen path" (FIXME: see check_loans.rs)
612-
self.access_lvalue(context, lvalue_span, (Deep, Read(ReadKind::Copy)), flow_state);
640+
// Check if error has already been reported to stop duplicate reporting.
641+
let has_storage_drop_or_dead_error_reported = match *lvalue {
642+
Lvalue::Local(local) => self.storage_drop_or_dead_error_reported.contains(&local),
643+
_ => false,
644+
};
645+
646+
// If the error has been reported already, then we don't need the access_lvalue call.
647+
if !has_storage_drop_or_dead_error_reported || consume_via_drop != ConsumeKind::Drop {
648+
let error_reported;
649+
650+
if moves_by_default {
651+
let kind = match consume_via_drop {
652+
ConsumeKind::Drop => WriteKind::StorageDeadOrDrop,
653+
_ => WriteKind::Move,
654+
};
655+
656+
// move of lvalue: check if this is move of already borrowed path
657+
error_reported = self.access_lvalue(context, lvalue_span,
658+
(Deep, Write(kind)), flow_state);
659+
} else {
660+
// copy of lvalue: check if this is "copy of frozen path"
661+
// (FIXME: see check_loans.rs)
662+
error_reported = self.access_lvalue(context, lvalue_span,
663+
(Deep, Read(ReadKind::Copy)), flow_state);
664+
}
665+
666+
// If there was an error, then we keep track of it so as to deduplicate it.
667+
// We only do this on ConsumeKind::Drop.
668+
if error_reported && consume_via_drop == ConsumeKind::Drop {
669+
if let Lvalue::Local(local) = *lvalue {
670+
self.storage_drop_or_dead_error_reported.insert(local);
671+
}
672+
}
613673
}
614674

615675
// Finally, check if path was already moved.
@@ -1458,6 +1518,27 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
14581518
err.emit();
14591519
}
14601520

1521+
fn report_borrowed_value_does_not_live_long_enough(&mut self,
1522+
_: Context,
1523+
(lvalue, span): (&Lvalue, Span),
1524+
end_span: Option<Span>) {
1525+
let proper_span = match *lvalue {
1526+
Lvalue::Local(local) => self.mir.local_decls[local].source_info.span,
1527+
_ => span
1528+
};
1529+
1530+
let mut err = self.tcx.path_does_not_live_long_enough(span, "borrowed value", Origin::Mir);
1531+
err.span_label(proper_span, "temporary value created here");
1532+
err.span_label(span, "temporary value dropped here while still borrowed");
1533+
err.note("consider using a `let` binding to increase its lifetime");
1534+
1535+
if let Some(end) = end_span {
1536+
err.span_label(end, "temporary value needs to live until here");
1537+
}
1538+
1539+
err.emit();
1540+
}
1541+
14611542
fn report_illegal_mutation_of_borrowed(&mut self,
14621543
_: Context,
14631544
(lvalue, span): (&Lvalue<'tcx>, Span),

src/test/compile-fail/issue-36082.rs

+18-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// revisions: ast mir
12+
//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir
13+
1114
use std::cell::RefCell;
1215

1316
fn main() {
@@ -16,10 +19,20 @@ fn main() {
1619
let x = RefCell::new((&mut r,s));
1720

1821
let val: &_ = x.borrow().0;
19-
//~^ ERROR borrowed value does not live long enough
20-
//~| temporary value dropped here while still borrowed
21-
//~| temporary value created here
22-
//~| consider using a `let` binding to increase its lifetime
22+
//[ast]~^ ERROR borrowed value does not live long enough [E0597]
23+
//[ast]~| NOTE temporary value dropped here while still borrowed
24+
//[ast]~| NOTE temporary value created here
25+
//[ast]~| NOTE consider using a `let` binding to increase its lifetime
26+
//[mir]~^^^^^ ERROR borrowed value does not live long enough (Ast) [E0597]
27+
//[mir]~| NOTE temporary value dropped here while still borrowed
28+
//[mir]~| NOTE temporary value created here
29+
//[mir]~| NOTE consider using a `let` binding to increase its lifetime
30+
//[mir]~| ERROR borrowed value does not live long enough (Mir) [E0597]
31+
//[mir]~| NOTE temporary value dropped here while still borrowed
32+
//[mir]~| NOTE temporary value created here
33+
//[mir]~| NOTE consider using a `let` binding to increase its lifetime
2334
println!("{}", val);
2435
}
25-
//~^ temporary value needs to live until here
36+
//[ast]~^ NOTE temporary value needs to live until here
37+
//[mir]~^^ NOTE temporary value needs to live until here
38+
//[mir]~| NOTE temporary value needs to live until here

0 commit comments

Comments
 (0)