@@ -463,13 +463,20 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
463
463
target : _,
464
464
unwind : _,
465
465
} => {
466
- self . access_place (
467
- ContextKind :: Drop . new ( loc) ,
468
- ( drop_place, span) ,
469
- ( Deep , Write ( WriteKind :: StorageDeadOrDrop ) ) ,
470
- LocalMutationIsAllowed :: Yes ,
471
- flow_state,
472
- ) ;
466
+ let gcx = self . tcx . global_tcx ( ) ;
467
+
468
+ // Compute the type with accurate region information.
469
+ let drop_place_ty = drop_place. ty ( self . mir , self . tcx ) ;
470
+
471
+ // Erase the regions.
472
+ let drop_place_ty = self . tcx . erase_regions ( & drop_place_ty) . to_ty ( self . tcx ) ;
473
+
474
+ // "Lift" into the gcx -- once regions are erased, this type should be in the
475
+ // global arenas; this "lift" operation basically just asserts that is true, but
476
+ // that is useful later.
477
+ let drop_place_ty = gcx. lift ( & drop_place_ty) . unwrap ( ) ;
478
+
479
+ self . visit_terminator_drop ( loc, term, flow_state, drop_place, drop_place_ty, span) ;
473
480
}
474
481
TerminatorKind :: DropAndReplace {
475
482
location : ref drop_place,
@@ -717,6 +724,65 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
717
724
self . tcx . sess . opts . debugging_opts . two_phase_beyond_autoref )
718
725
}
719
726
727
+ /// Invokes `access_place` as appropriate for dropping the value
728
+ /// at `drop_place`. Note that the *actual* `Drop` in the MIR is
729
+ /// always for a variable (e.g., `Drop(x)`) -- but we recursively
730
+ /// break this variable down into subpaths (e.g., `Drop(x.foo)`)
731
+ /// to indicate more precisely which fields might actually be
732
+ /// accessed by a destructor.
733
+ fn visit_terminator_drop (
734
+ & mut self ,
735
+ loc : Location ,
736
+ term : & Terminator < ' tcx > ,
737
+ flow_state : & Flows < ' cx , ' gcx , ' tcx > ,
738
+ drop_place : & Place < ' tcx > ,
739
+ erased_drop_place_ty : ty:: Ty < ' gcx > ,
740
+ span : Span ,
741
+ ) {
742
+ match erased_drop_place_ty. sty {
743
+ // When a struct is being dropped, we need to check
744
+ // whether it has a destructor, if it does, then we can
745
+ // call it, if it does not then we need to check the
746
+ // individual fields instead. This way if `foo` has a
747
+ // destructor but `bar` does not, we will only check for
748
+ // borrows of `x.foo` and not `x.bar`. See #47703.
749
+ ty:: TyAdt ( def, substs) if def. is_struct ( ) && !def. has_dtor ( self . tcx ) => {
750
+ for ( index, field) in def. all_fields ( ) . enumerate ( ) {
751
+ let gcx = self . tcx . global_tcx ( ) ;
752
+ let field_ty = field. ty ( gcx, substs) ;
753
+ let field_ty = gcx. normalize_associated_type_in_env ( & field_ty, self . param_env ) ;
754
+ let place = drop_place. clone ( ) . field ( Field :: new ( index) , field_ty) ;
755
+
756
+ self . visit_terminator_drop (
757
+ loc,
758
+ term,
759
+ flow_state,
760
+ & place,
761
+ field_ty,
762
+ span,
763
+ ) ;
764
+ }
765
+ } ,
766
+ _ => {
767
+ // We have now refined the type of the value being
768
+ // dropped (potentially) to just the type of a
769
+ // subfield; so check whether that field's type still
770
+ // "needs drop". If so, we assume that the destructor
771
+ // may access any data it likes (i.e., a Deep Write).
772
+ let gcx = self . tcx . global_tcx ( ) ;
773
+ if erased_drop_place_ty. needs_drop ( gcx, self . param_env ) {
774
+ self . access_place (
775
+ ContextKind :: Drop . new ( loc) ,
776
+ ( drop_place, span) ,
777
+ ( Deep , Write ( WriteKind :: StorageDeadOrDrop ) ) ,
778
+ LocalMutationIsAllowed :: Yes ,
779
+ flow_state,
780
+ ) ;
781
+ }
782
+ } ,
783
+ }
784
+ }
785
+
720
786
/// Checks an access to the given place to see if it is allowed. Examines the set of borrows
721
787
/// that are in scope, as well as which paths have been initialized, to ensure that (a) the
722
788
/// place is initialized and (b) it is not borrowed in some way that would prevent this
0 commit comments