@@ -20,6 +20,7 @@ use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
2020use rustc:: mir:: { Statement , StatementKind , Terminator , TerminatorKind } ;
2121use transform:: nll;
2222
23+ use rustc_data_structures:: fx:: FxHashSet ;
2324use rustc_data_structures:: indexed_set:: { self , IdxSetBuf } ;
2425use 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 ) ]
429440enum WriteKind {
430- StorageDead ,
441+ StorageDeadOrDrop ,
431442 MutableBorrow ( BorrowKind ) ,
432443 Mutate ,
433444 Move ,
434445}
435446
436447impl < ' 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 ) ,
0 commit comments