@@ -463,13 +463,20 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
463463 target : _,
464464 unwind : _,
465465 } => {
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) ;
473480 }
474481 TerminatorKind :: DropAndReplace {
475482 location : ref drop_place,
@@ -717,6 +724,65 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
717724 self . tcx . sess . opts . debugging_opts . two_phase_beyond_autoref )
718725 }
719726
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+
720786 /// Checks an access to the given place to see if it is allowed. Examines the set of borrows
721787 /// that are in scope, as well as which paths have been initialized, to ensure that (a) the
722788 /// place is initialized and (b) it is not borrowed in some way that would prevent this
0 commit comments