/// Invokes `access_place` as appropriate for dropping the value
/// at `drop_place`. Note that the *actual* `Drop` in the MIR is
/// always for a variable (e.g., `Drop(x)`) -- but we recursively
/// break this variable down into subpaths (e.g., `Drop(x.foo)`)
/// to indicate more precisely which fields might actually be
/// accessed by a destructor.
Invokes `access_place` as appropriate for dropping the value
at `drop_place`. Note that the *actual* `Drop` in the MIR is
always for a variable `) -- but we recursively
break this variable down into subpaths `)
to indicate more precisely which fields might actually be
accessed by a destructor.
Invokes ` access_place ` as appropriate for dropping the value at ` drop_place ` . Note that the * actual * ` Drop ` in the MIR is always for a variable ` ) -- but we recursively break this variable down into subpaths ` ) to indicate more precisely which fields might actually be accessed by a destructor .
fn visit_terminator_drop(
        &mut self,
        loc: Location,
        term: &Terminator<'tcx>,
        flow_state: &Flows<'cx, 'gcx, 'tcx>,
        drop_place: &Place<'tcx>,
        erased_drop_place_ty: ty::Ty<'gcx>,
        span: Span,
        prev_seen: SeenTy<'_, 'gcx>,
    ) {
        if prev_seen.have_seen(erased_drop_place_ty) {
            // if we have directly seen the input ty `T`, then we must
            // have had some *direct* ownership loop between `T` and
            // some directly-owned (as in, actually traversed by
            // recursive calls below) part that is also of type `T`.
            //
            // Note: in *all* such cases, the data in question cannot
            // be constructed (nor destructed) in finite time/space.
            //
            // Proper examples, some of which are statically rejected:
            //
            // * `struct A { field: A, ... }`:
            //   statically rejected as infinite size
            //
            // * `type B = (B, ...);`:
            //   statically rejected as cyclic
            //
            // * `struct C { field: Box<C>, ... }`
            // * `struct D { field: Box<(D, D)>, ... }`:
            //   *accepted*, though impossible to construct
            //
            // Here is *NOT* an example:
            // * `struct Z { field: Option<Box<Z>>, ... }`:
            //   Here, the type is both representable in finite space (due to the boxed indirection)
            //   and constructable in finite time (since the recursion can bottom out with `None`).
            //   This is an obvious instance of something the compiler must accept.
            //
            // Since some of the above impossible cases like `C` and
            // `D` are accepted by the compiler, we must take care not
            // to infinite-loop while processing them. But since such
            // cases cannot actually arise, it is sound for us to just
            // skip them during drop. If the developer uses unsafe
            // code to construct them, they should not be surprised by
            // weird drop behavior in their resulting code.
            debug!("visit_terminator_drop previously seen \
                    erased_drop_place_ty: {:?} on prev_seen: {:?}; returning early.",
                   erased_drop_place_ty, prev_seen);
            return;
        }
        let gcx = self.tcx.global_tcx();
        let drop_field = |mir: &mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>,
                          (index, field): (usize, ty::Ty<'gcx>)| {
            let field_ty = gcx.normalize_erasing_regions(mir.param_env, field);
            let place = drop_place.clone().field(Field::new(index), field_ty);
            debug!("visit_terminator_drop drop_field place: {:?} field_ty: {:?}", place, field_ty);
            let seen = prev_seen.cons(erased_drop_place_ty);
            mir.visit_terminator_drop(loc, term, flow_state, &place, field_ty, span, seen);
        };
        match erased_drop_place_ty.sty {
            // When a struct is being dropped, we need to check
            // whether it has a destructor, if it does, then we can
            // call it, if it does not then we need to check the
            // individual fields instead. This way if `foo` has a
            // destructor but `bar` does not, we will only check for
            // borrows of `x.foo` and not `x.bar`. See #47703.
            ty::TyAdt(def, substs) if def.is_struct() && !def.has_dtor(self.tcx) => {
                def.all_fields()
                    .map(|field| field.ty(gcx, substs))
                    .enumerate()
                    .for_each(|field| drop_field(self, field));
            }
            // Same as above, but for tuples.
            ty::TyTuple(tys) => {
                tys.iter()
                    .cloned()
                    .enumerate()
                    .for_each(|field| drop_field(self, field));
            }
            // Closures also have disjoint fields, but they are only
            // directly accessed in the body of the closure.
            ty::TyClosure(def, substs)
                if *drop_place == Place::Local(Local::new(1))
                    && !self.mir.upvar_decls.is_empty() =>
            {
                substs
                    .upvar_tys(def, self.tcx)
                    .enumerate()
                    .for_each(|field| drop_field(self, field));
            }
            // Generators also have disjoint fields, but they are only
            // directly accessed in the body of the generator.
            ty::TyGenerator(def, substs, _)
                if *drop_place == Place::Local(Local::new(1))
                    && !self.mir.upvar_decls.is_empty() =>
            {
                substs
                    .upvar_tys(def, self.tcx)
                    .enumerate()
                    .for_each(|field| drop_field(self, field));
            }
            // #45696: special-case Box<T> by treating its dtor as
            // only deep *across owned content*. Namely, we know
            // dropping a box does not touch data behind any
            // references it holds; if we were to instead fall into
            // the base case below, we would have a Deep Write due to
            // the box being `needs_drop`, and that Deep Write would
            // touch `&mut` data in the box.
            ty::TyAdt(def, _) if def.is_box() => {
                // When/if we add a `&own T` type, this action would
                // be like running the destructor of the `&own T`.
                // (And the owner of backing storage referenced by the
                // `&own T` would be responsible for deallocating that
                // backing storage.)
                // we model dropping any content owned by the box by
                // recurring on box contents. This catches cases like
                // `Box<Box<ScribbleWhenDropped<&mut T>>>`, while
                // still restricting Write to *owned* content.
                let ty = erased_drop_place_ty.boxed_ty();
                let deref_place = drop_place.clone().deref();
                debug!("visit_terminator_drop drop-box-content deref_place: {:?} ty: {:?}",
                       deref_place, ty);
                let seen = prev_seen.cons(erased_drop_place_ty);
                self.visit_terminator_drop(
                    loc, term, flow_state, &deref_place, ty, span, seen);
            }
            _ => {
                // We have now refined the type of the value being
                // dropped (potentially) to just the type of a
                // subfield; so check whether that field's type still
                // "needs drop".
                if erased_drop_place_ty.needs_drop(gcx, self.param_env) {
                    // If so, we assume that the destructor may access
                    // any data it likes (i.e., a Deep Write).
                    self.access_place(
                        ContextKind::Drop.new(loc),
                        (drop_place, span),
                        (Deep, Write(WriteKind::StorageDeadOrDrop)),
                        LocalMutationIsAllowed::Yes,
                        flow_state,
                    );
                } else {
                    // If there is no destructor, we still include a
                    // *shallow* write.  This essentially ensures that
                    // borrows of the memory directly at `drop_place`
                    // cannot continue to be borrowed across the drop.
                    //
                    // If we were to use a Deep Write here, then any
                    // `&mut T` that is reachable from `drop_place`
                    // would get invalidated; fixing that is the
                    // essence of resolving issue #45696.
                    //
                    // * Note: In the compiler today, doing a Deep
                    //   Write here would not actually break
                    //   anything beyond #45696; for example it does not
                    //   break this example:
                    //
                    //   ```rust
                    //   fn reborrow(x: &mut i32) -> &mut i32 { &mut *x }
                    //   ```
                    //
                    //   Why? Because we do not schedule/emit
                    //   `Drop(x)` in the MIR unless `x` needs drop in
                    //   the first place.
                    //
                    // FIXME: Its possible this logic actually should
                    // be attached to the `StorageDead` statement
                    // rather than the `Drop`. See discussion on PR
                    // #52782.
                    self.access_place(
                        ContextKind::Drop.new(loc),
                        (drop_place, span),
                        (Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
                        LocalMutationIsAllowed::Yes,
                        flow_state,
                    );
                }
            }
        }
    }
fn visit_terminator_drop ( & mut self , loc : Location , term : & Terminator < ' tcx > , flow_state : & Flows < ' cx , ' gcx , ' tcx > , drop_place : & Place < ' tcx > , erased_drop_place_ty : ty :: Ty < ' gcx > , span : Span , prev_seen : SeenTy < ' _ , ' gcx > , ) { if prev_seen . have_seen ( erased_drop_place_ty ) { // if we have directly seen the input ty `T`, then we must // have had some *direct* ownership loop between `T` and // some directly-owned (as in, actually traversed by // recursive calls below) part that is also of type `T`. // // Note: in *all* such cases, the data in question cannot // be constructed (nor destructed) in finite time/space. // // Proper examples, some of which are statically rejected: // // * `struct A { field: A, ... }`: //   statically rejected as infinite size // // * `type B = (B, ...);`: //   statically rejected as cyclic // // * `struct C { field: Box<C>, ... }` // * `struct D { field: Box<(D, D)>, ... }`: //   *accepted*, though impossible to construct // // Here is *NOT* an example: // * `struct Z { field: Option<Box<Z>>, ... }`: //   Here, the type is both representable in finite space (due to the boxed indirection) //   and constructable in finite time (since the recursion can bottom out with `None`). //   This is an obvious instance of something the compiler must accept. // // Since some of the above impossible cases like `C` and // `D` are accepted by the compiler, we must take care not // to infinite-loop while processing them. But since such // cases cannot actually arise, it is sound for us to just // skip them during drop. If the developer uses unsafe // code to construct them, they should not be surprised by // weird drop behavior in their resulting code. debug ! ( " \
 " erased_drop_place_ty prev_seen ) ; return ; } let gcx = self . tcx . global_tcx ( ) ; let drop_field = | mir : & mut MirBorrowckCtxt < ' cx , ' gcx , ' tcx > , ( index , field ) : ( usize , ty :: Ty < ' gcx > ) | { let field_ty = gcx . normalize_erasing_regions ( mir . param_env , field ) ; let place = drop_place . clone ( ) . field ( Field :: new ( index ) , field_ty ) ; debug ! ( " " place field_ty ) ; let seen = prev_seen . cons ( erased_drop_place_ty ) ; mir . visit_terminator_drop ( loc , term , flow_state , & place , field_ty , span , seen ) ; } ; match erased_drop_place_ty . sty { // When a struct is being dropped, we need to check // whether it has a destructor, if it does, then we can // call it, if it does not then we need to check the // individual fields instead. This way if `foo` has a // destructor but `bar` does not, we will only check for // borrows of `x.foo` and not `x.bar`. See #47703. ty :: TyAdt ( def , substs ) if def . is_struct ( ) && ! def . has_dtor ( self . tcx ) => { def . all_fields ( ) . map ( | field | field . ty ( gcx , substs ) ) . enumerate ( ) . for_each ( | field | drop_field ( self , field ) ) ; } // Same as above, but for tuples. ty :: TyTuple ( tys ) => { tys . iter ( ) . cloned ( ) . enumerate ( ) . for_each ( | field | drop_field ( self , field ) ) ; } // Closures also have disjoint fields, but they are only // directly accessed in the body of the closure. ty :: TyClosure ( def , substs ) if * drop_place == Place :: Local ( Local :: new ( 1 ) ) && ! self . mir . upvar_decls . is_empty ( ) => { substs . upvar_tys ( def , self . tcx ) . enumerate ( ) . for_each ( | field | drop_field ( self , field ) ) ; } // Generators also have disjoint fields, but they are only // directly accessed in the body of the generator. ty :: TyGenerator ( def , substs , _ ) if * drop_place == Place :: Local ( Local :: new ( 1 ) ) && ! self . mir . upvar_decls . is_empty ( ) => { substs . upvar_tys ( def , self . tcx ) . enumerate ( ) . for_each ( | field | drop_field ( self , field ) ) ; } // #45696: special-case Box<T> by treating its dtor as // only deep *across owned content*. Namely, we know // dropping a box does not touch data behind any // references it holds; if we were to instead fall into // the base case below, we would have a Deep Write due to // the box being `needs_drop`, and that Deep Write would // touch `&mut` data in the box. ty :: TyAdt ( def , _ ) if def . is_box ( ) => { // When/if we add a `&own T` type, this action would // be like running the destructor of the `&own T`. // (And the owner of backing storage referenced by the // `&own T` would be responsible for deallocating that // backing storage.) // we model dropping any content owned by the box by // recurring on box contents. This catches cases like // `Box<Box<ScribbleWhenDropped<&mut T>>>`, while // still restricting Write to *owned* content. let ty = erased_drop_place_ty . boxed_ty ( ) ; let deref_place = drop_place . clone ( ) . deref ( ) ; debug ! ( " " deref_place ty ) ; let seen = prev_seen . cons ( erased_drop_place_ty ) ; self . visit_terminator_drop ( loc , term , flow_state , & deref_place , ty , span , seen ) ; } _ => { // We have now refined the type of the value being // dropped (potentially) to just the type of a // subfield; so check whether that field's type still // "needs drop". if erased_drop_place_ty . needs_drop ( gcx , self . param_env ) { // If so, we assume that the destructor may access // any data it likes (i.e., a Deep Write). self . access_place ( ContextKind :: Drop . new ( loc ) , ( drop_place , span ) , ( Deep , Write ( WriteKind :: StorageDeadOrDrop ) ) , LocalMutationIsAllowed :: Yes , flow_state , ) ; } else { // If there is no destructor, we still include a // *shallow* write.  This essentially ensures that // borrows of the memory directly at `drop_place` // cannot continue to be borrowed across the drop. // // If we were to use a Deep Write here, then any // `&mut T` that is reachable from `drop_place` // would get invalidated; fixing that is the // essence of resolving issue #45696. // // * Note: In the compiler today, doing a Deep //   Write here would not actually break //   anything beyond #45696; for example it does not //   break this example: // //   ```rust //   fn reborrow(x: &mut i32) -> &mut i32 { &mut *x } //   ``` // //   Why? Because we do not schedule/emit //   `Drop(x)` in the MIR unless `x` needs drop in //   the first place. // // FIXME: Its possible this logic actually should // be attached to the `StorageDead` statement // rather than the `Drop`. See discussion on PR // #52782. self . access_place ( ContextKind :: Drop . new ( loc ) , ( drop_place , span ) , ( Shallow ( None ) , Write ( WriteKind :: StorageDeadOrDrop ) ) , LocalMutationIsAllowed :: Yes , flow_state , ) ; } } } }
Invokes `access_place` as appropriate for dropping the value
at `drop_place`.
Invokes ` access_place ` as appropriate for dropping the value at ` drop_place ` .
&mut self
loc
Location
term
&Terminator<'tcx>
flow_state
&Flows<'cx, 'gcx, 'tcx>
drop_place
&Place<'tcx>
erased_drop_place_ty
ty::Ty<'gcx>
span
Span
prev_seen
SeenTy<'_, 'gcx>