@@ -20,6 +20,7 @@ use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
20
20
use rustc:: mir:: { Statement , StatementKind , Terminator , TerminatorKind } ;
21
21
use transform:: nll;
22
22
23
+ use rustc_data_structures:: fx:: FxHashSet ;
23
24
use rustc_data_structures:: indexed_set:: { self , IdxSetBuf } ;
24
25
use rustc_data_structures:: indexed_vec:: { Idx } ;
25
26
@@ -136,6 +137,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
136
137
node_id : id,
137
138
move_data : & mdpe. move_data ,
138
139
param_env : param_env,
140
+ storage_drop_or_dead_error_reported : FxHashSet ( ) ,
139
141
} ;
140
142
141
143
let mut state = InProgress :: new ( flow_borrows,
@@ -153,6 +155,10 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
153
155
node_id : ast:: NodeId ,
154
156
move_data : & ' cx MoveData < ' tcx > ,
155
157
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 > ,
156
162
}
157
163
158
164
// (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
281
287
}
282
288
283
289
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
+ }
288
299
}
289
300
}
290
301
}
@@ -427,24 +438,30 @@ enum ReadKind {
427
438
428
439
#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
429
440
enum WriteKind {
430
- StorageDead ,
441
+ StorageDeadOrDrop ,
431
442
MutableBorrow ( BorrowKind ) ,
432
443
Mutate ,
433
444
Move ,
434
445
}
435
446
436
447
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.
437
454
fn access_lvalue ( & mut self ,
438
455
context : Context ,
439
456
lvalue_span : ( & Lvalue < ' tcx > , Span ) ,
440
457
kind : ( ShallowOrDeep , ReadOrWrite ) ,
441
- flow_state : & InProgress < ' cx , ' gcx , ' tcx > ) {
442
-
458
+ flow_state : & InProgress < ' cx , ' gcx , ' tcx > ) -> bool {
443
459
let ( sd, rw) = kind;
444
460
445
461
// Check permissions
446
462
self . check_access_permissions ( lvalue_span, rw) ;
447
463
464
+ let mut error_reported = false ;
448
465
self . each_borrow_involving_path (
449
466
context, ( sd, lvalue_span. 0 ) , flow_state, |this, _index, borrow, common_prefix| {
450
467
match ( rw, borrow. kind ) {
@@ -454,13 +471,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
454
471
( Read ( kind) , BorrowKind :: Unique ) |
455
472
( Read ( kind) , BorrowKind :: Mut ) => {
456
473
match kind {
457
- ReadKind :: Copy =>
474
+ ReadKind :: Copy => {
475
+ error_reported = true ;
458
476
this. report_use_while_mutably_borrowed (
459
- context, lvalue_span, borrow) ,
477
+ context, lvalue_span, borrow)
478
+ } ,
460
479
ReadKind :: Borrow ( bk) => {
461
480
let end_issued_loan_span =
462
481
flow_state. borrows . base_results . operator ( ) . opt_region_end_span (
463
482
& borrow. region ) ;
483
+ error_reported = true ;
464
484
this. report_conflicting_borrow (
465
485
context, common_prefix, lvalue_span, bk,
466
486
& borrow, end_issued_loan_span)
@@ -474,22 +494,35 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
474
494
let end_issued_loan_span =
475
495
flow_state. borrows . base_results . operator ( ) . opt_region_end_span (
476
496
& borrow. region ) ;
497
+ error_reported = true ;
477
498
this. report_conflicting_borrow (
478
499
context, common_prefix, lvalue_span, bk,
479
500
& borrow, end_issued_loan_span)
480
501
}
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 ;
483
512
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 ;
486
517
this. report_move_out_while_borrowed (
487
- context, lvalue_span, & borrow) ,
518
+ context, lvalue_span, & borrow)
519
+ } ,
488
520
}
489
521
Control :: Break
490
522
}
491
523
}
492
524
} ) ;
525
+ error_reported
493
526
}
494
527
495
528
fn mutate_lvalue ( & mut self ,
@@ -604,12 +637,39 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
604
637
let erased_ty = gcx. lift ( & self . tcx . erase_regions ( & ty) ) . unwrap ( ) ;
605
638
let moves_by_default = erased_ty. moves_by_default ( gcx, self . param_env , DUMMY_SP ) ;
606
639
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
+ }
613
673
}
614
674
615
675
// Finally, check if path was already moved.
@@ -1458,6 +1518,27 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1458
1518
err. emit ( ) ;
1459
1519
}
1460
1520
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
+
1461
1542
fn report_illegal_mutation_of_borrowed ( & mut self ,
1462
1543
_: Context ,
1463
1544
( lvalue, span) : ( & Lvalue < ' tcx > , Span ) ,
0 commit comments