about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-09-24 14:47:17 +0000
committerbors <bors@rust-lang.org>2018-09-24 14:47:17 +0000
commita072d1bca6282d1d2fbec103d12393527eb5a868 (patch)
tree75e68c7a54376c61581e00d6e8c10967e1e52b07
parente5c6575801028f5e089ef2e7720aa1af9d452334 (diff)
parentcfbd1a9a250ba3a82041c46605dcf16356f33727 (diff)
downloadrust-a072d1bca6282d1d2fbec103d12393527eb5a868.tar.gz
rust-a072d1bca6282d1d2fbec103d12393527eb5a868.zip
Auto merge of #54509 - matthewjasper:better-drop-access, r=pnkfelix
[NLL] Rework checking for borrows conflicting with drops

Previously, we would split the drop access into multiple checks for each
field of a struct/tuple/closure and through `Box` dereferences. This
changes this to check if the borrow is accessed by the drop in
`places_conflict`.

We also now handle enums containing `Drop` types.

Closes #53569

r? @nikomatsakis
cc @pnkfelix
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs108
-rw-r--r--src/librustc_mir/borrow_check/mod.rs274
-rw-r--r--src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs6
-rw-r--r--src/librustc_mir/borrow_check/nll/invalidation.rs136
-rw-r--r--src/librustc_mir/borrow_check/nll/mod.rs3
-rw-r--r--src/librustc_mir/borrow_check/path_utils.rs4
-rw-r--r--src/librustc_mir/borrow_check/places_conflict.rs28
-rw-r--r--src/test/ui/dropck/dropck-union.nll.stderr2
-rw-r--r--src/test/ui/generator/dropck.nll.stderr2
-rw-r--r--src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.migrate.stderr4
-rw-r--r--src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.nll.stderr4
-rw-r--r--src/test/ui/nll/enum-drop-access.rs51
-rw-r--r--src/test/ui/nll/enum-drop-access.stderr45
-rw-r--r--src/test/ui/span/borrowck-ref-into-rvalue.nll.stderr12
-rw-r--r--src/test/ui/span/dropck_arr_cycle_checked.nll.stderr4
-rw-r--r--src/test/ui/span/dropck_direct_cycle_with_drop.nll.stderr2
-rw-r--r--src/test/ui/span/dropck_vec_cycle_checked.nll.stderr4
-rw-r--r--src/test/ui/span/issue-29106.nll.stderr4
-rw-r--r--src/test/ui/span/issue28498-reject-ex1.nll.stderr1
-rw-r--r--src/test/ui/span/vec-must-not-hide-type-from-dropck.nll.stderr2
-rw-r--r--src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.nll.stderr2
21 files changed, 282 insertions, 416 deletions
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index 3fdb7d7f27d..1d91fa365d5 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -8,14 +8,14 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use borrow_check::{WriteKind, StorageDeadOrDrop};
+use borrow_check::WriteKind;
 use borrow_check::prefixes::IsPrefixOf;
 use borrow_check::nll::explain_borrow::BorrowExplanation;
 use rustc::middle::region::ScopeTree;
 use rustc::mir::{
     self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, FakeReadCause, Field, Local,
-    LocalDecl, LocalKind, Location, Operand, Place, ProjectionElem, Rvalue, Statement,
-    StatementKind, TerminatorKind, VarBindingForm,
+    LocalDecl, LocalKind, Location, Operand, Place, PlaceProjection, ProjectionElem, Rvalue,
+    Statement, StatementKind, TerminatorKind, VarBindingForm,
 };
 use rustc::hir;
 use rustc::hir::def_id::DefId;
@@ -452,13 +452,21 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         self.access_place_error_reported
             .insert((root_place.clone(), borrow_span));
 
-        if let Some(WriteKind::StorageDeadOrDrop(StorageDeadOrDrop::Destructor)) = kind {
+        if let StorageDeadOrDrop::Destructor(dropped_ty)
+            = self.classify_drop_access_kind(&borrow.borrowed_place)
+        {
             // If a borrow of path `B` conflicts with drop of `D` (and
             // we're not in the uninteresting case where `B` is a
             // prefix of `D`), then report this as a more interesting
             // destructor conflict.
             if !borrow.borrowed_place.is_prefix_of(place_span.0) {
-                self.report_borrow_conflicts_with_destructor(context, borrow, place_span, kind);
+                self.report_borrow_conflicts_with_destructor(
+                    context,
+                    borrow,
+                    place_span,
+                    kind,
+                    dropped_ty,
+                );
                 return;
             }
         }
@@ -566,6 +574,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         borrow: &BorrowData<'tcx>,
         (place, drop_span): (&Place<'tcx>, Span),
         kind: Option<WriteKind>,
+        dropped_ty: ty::Ty<'tcx>,
     ) {
         debug!(
             "report_borrow_conflicts_with_destructor(\
@@ -579,28 +588,19 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
 
         let mut err = self.infcx.tcx.cannot_borrow_across_destructor(borrow_span, Origin::Mir);
 
-        let (what_was_dropped, dropped_ty) = {
-            let desc = match self.describe_place(place) {
-                Some(name) => format!("`{}`", name.as_str()),
-                None => format!("temporary value"),
-            };
-            let ty = place.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx);
-            (desc, ty)
+        let what_was_dropped = match self.describe_place(place) {
+            Some(name) => format!("`{}`", name.as_str()),
+            None => format!("temporary value"),
         };
 
-        let label = match dropped_ty.sty {
-            ty::Adt(adt, _) if adt.has_dtor(self.infcx.tcx) && !adt.is_box() => {
-                match self.describe_place(&borrow.borrowed_place) {
-                    Some(borrowed) =>
-                        format!("here, drop of {D} needs exclusive access to `{B}`, \
-                                 because the type `{T}` implements the `Drop` trait",
-                                D=what_was_dropped, T=dropped_ty, B=borrowed),
-                    None =>
-                        format!("here is drop of {D}; whose type `{T}` implements the `Drop` trait",
-                                D=what_was_dropped, T=dropped_ty),
-                }
-            }
-            _ => format!("drop of {D} occurs here", D=what_was_dropped),
+        let label = match self.describe_place(&borrow.borrowed_place) {
+            Some(borrowed) =>
+                format!("here, drop of {D} needs exclusive access to `{B}`, \
+                         because the type `{T}` implements the `Drop` trait",
+                        D=what_was_dropped, T=dropped_ty, B=borrowed),
+            None =>
+                format!("here is drop of {D}; whose type `{T}` implements the `Drop` trait",
+                        D=what_was_dropped, T=dropped_ty),
         };
         err.span_label(drop_span, label);
 
@@ -880,6 +880,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
 
 pub(super) struct IncludingDowncast(bool);
 
+/// Which case a StorageDeadOrDrop is for.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum StorageDeadOrDrop<'tcx> {
+    LocalStorageDead,
+    BoxedStorageDead,
+    Destructor(ty::Ty<'tcx>),
+}
+
 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
     // End-user visible description of `place` if one can be found. If the
     // place is a temporary for instance, None will be returned.
@@ -1167,6 +1175,56 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         }
     }
 
+    fn classify_drop_access_kind(&self, place: &Place<'tcx>) -> StorageDeadOrDrop<'tcx> {
+        let tcx = self.infcx.tcx;
+        match place {
+            Place::Local(_)
+            | Place::Static(_)
+            | Place::Promoted(_) => StorageDeadOrDrop::LocalStorageDead,
+            Place::Projection(box PlaceProjection { base, elem }) => {
+                let base_access = self.classify_drop_access_kind(base);
+                match elem {
+                    ProjectionElem::Deref => {
+                        match base_access {
+                            StorageDeadOrDrop::LocalStorageDead
+                            | StorageDeadOrDrop::BoxedStorageDead => {
+                                assert!(base.ty(self.mir, tcx).to_ty(tcx).is_box(),
+                                        "Drop of value behind a reference or raw pointer");
+                                StorageDeadOrDrop::BoxedStorageDead
+                            }
+                            StorageDeadOrDrop::Destructor(_) => {
+                                base_access
+                            }
+                        }
+                    }
+                    ProjectionElem::Field(..)
+                    | ProjectionElem::Downcast(..) => {
+                        let base_ty = base.ty(self.mir, tcx).to_ty(tcx);
+                        match base_ty.sty {
+                            ty::Adt(def, _) if def.has_dtor(tcx) => {
+                                // Report the outermost adt with a destructor
+                                match base_access {
+                                    StorageDeadOrDrop::Destructor(_) => {
+                                        base_access
+                                    }
+                                    StorageDeadOrDrop::LocalStorageDead
+                                    | StorageDeadOrDrop::BoxedStorageDead => {
+                                        StorageDeadOrDrop::Destructor(base_ty)
+                                    }
+                                }
+                            }
+                            _ => base_access,
+                        }
+                    }
+
+                    ProjectionElem::ConstantIndex { .. }
+                    | ProjectionElem::Subslice { .. }
+                    | ProjectionElem::Index(_) => base_access,
+                }
+            }
+        }
+    }
+
     /// Annotate argument and return type of function and closure with (synthesized) lifetime for
     /// borrow of local value that does not live long enough.
     fn annotate_argument_and_return_for_borrow(
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 06884875598..769e1097bff 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -23,13 +23,12 @@ use rustc::mir::{ClearCrossCrate, Local, Location, Mir, Mutability, Operand, Pla
 use rustc::mir::{Field, Projection, ProjectionElem, Rvalue, Statement, StatementKind};
 use rustc::mir::{Terminator, TerminatorKind};
 use rustc::ty::query::Providers;
-use rustc::ty::{self, ParamEnv, TyCtxt, Ty};
+use rustc::ty::{self, TyCtxt};
 
 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, Level};
 use rustc_data_structures::bit_set::BitSet;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::graph::dominators::Dominators;
-use rustc_data_structures::indexed_vec::Idx;
 use smallvec::SmallVec;
 
 use std::rc::Rc;
@@ -251,7 +250,6 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
         mir,
         mir_def_id: def_id,
         move_data: &mdpe.move_data,
-        param_env: param_env,
         location_table,
         movable_generator,
         locals_are_invalidated_at_exit,
@@ -393,7 +391,6 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
     /// when MIR borrowck begins.
     location_table: &'cx LocationTable,
 
-    param_env: ParamEnv<'gcx>,
     movable_generator: bool,
     /// This keeps track of whether local variables are free-ed when the function
     /// exits even without a `StorageDead`, which appears to be the case for
@@ -574,8 +571,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
                 self.access_place(
                     ContextKind::StorageDead.new(location),
                     (&Place::Local(local), span),
-                    (Shallow(None), Write(WriteKind::StorageDeadOrDrop(
-                        StorageDeadOrDrop::LocalStorageDead))),
+                    (Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
                     LocalMutationIsAllowed::Yes,
                     flow_state,
                 );
@@ -630,8 +626,13 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
                         loc: {:?} term: {:?} drop_place: {:?} drop_place_ty: {:?} span: {:?}",
                        loc, term, drop_place, drop_place_ty, span);
 
-                self.visit_terminator_drop(
-                    loc, term, flow_state, drop_place, drop_place_ty, span, SeenTy(None));
+                self.access_place(
+                    ContextKind::Drop.new(loc),
+                    (drop_place, span),
+                    (AccessDepth::Drop, Write(WriteKind::StorageDeadOrDrop)),
+                    LocalMutationIsAllowed::Yes,
+                    flow_state,
+                );
             }
             TerminatorKind::DropAndReplace {
                 location: ref drop_place,
@@ -748,7 +749,7 @@ enum MutateMode {
 }
 
 use self::ReadOrWrite::{Activation, Read, Reservation, Write};
-use self::ShallowOrDeep::{Deep, Shallow};
+use self::AccessDepth::{Deep, Shallow};
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 enum ArtificialField {
@@ -757,7 +758,7 @@ enum ArtificialField {
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum ShallowOrDeep {
+enum AccessDepth {
     /// From the RFC: "A *shallow* access means that the immediate
     /// fields reached at P are accessed, but references or pointers
     /// found within are not dereferenced. Right now, the only access
@@ -769,6 +770,10 @@ enum ShallowOrDeep {
     /// through the given place may be invalidated or accesses by
     /// this action."
     Deep,
+
+    /// Access is Deep only when there is a Drop implementation that
+    /// can reach the data behind the reference.
+    Drop,
 }
 
 /// Kind of access to a value: read or write
@@ -803,21 +808,12 @@ enum ReadKind {
 /// (For informational purposes only)
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 enum WriteKind {
-    StorageDeadOrDrop(StorageDeadOrDrop),
+    StorageDeadOrDrop,
     MutableBorrow(BorrowKind),
     Mutate,
     Move,
 }
 
-/// Specify whether which case a StorageDeadOrDrop is in.
-/// (For informational purposes only)
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum StorageDeadOrDrop {
-    LocalStorageDead,
-    BoxedStorageDead,
-    Destructor,
-}
-
 /// When checking permissions for a place access, this flag is used to indicate that an immutable
 /// local place can be mutated.
 ///
@@ -868,231 +864,7 @@ impl InitializationRequiringAction {
     }
 }
 
-/// A simple linked-list threaded up the stack of recursive calls in `visit_terminator_drop`.
-#[derive(Copy, Clone, Debug)]
-struct SeenTy<'a, 'gcx: 'a>(Option<(Ty<'gcx>, &'a SeenTy<'a, 'gcx>)>);
-
-impl<'a, 'gcx> SeenTy<'a, 'gcx> {
-    /// Return a new list with `ty` prepended to the front of `self`.
-    fn cons(&'a self, ty: Ty<'gcx>) -> Self {
-        SeenTy(Some((ty, self)))
-    }
-
-    /// True if and only if `ty` occurs on the linked list `self`.
-    fn have_seen(self, ty: Ty) -> bool {
-        let mut this = self.0;
-        loop {
-            match this {
-                None => return false,
-                Some((seen_ty, recur)) => {
-                    if seen_ty == ty {
-                        return true;
-                    } else {
-                        this = recur.0;
-                        continue;
-                    }
-                }
-            }
-        }
-    }
-}
-
 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
-    /// 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.
-    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.infcx.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::Adt(def, substs) if def.is_struct() && !def.has_dtor(self.infcx.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::Tuple(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::Closure(def, substs)
-                if *drop_place == Place::Local(Local::new(1))
-                    && !self.mir.upvar_decls.is_empty() =>
-            {
-                substs
-                    .upvar_tys(def, self.infcx.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::Generator(def, substs, _)
-                if *drop_place == Place::Local(Local::new(1))
-                    && !self.mir.upvar_decls.is_empty() =>
-            {
-                substs
-                    .upvar_tys(def, self.infcx.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::Adt(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(
-                            StorageDeadOrDrop::Destructor))),
-                        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.
-                    self.access_place(
-                        ContextKind::Drop.new(loc),
-                        (drop_place, span),
-                        (Shallow(None), Write(WriteKind::StorageDeadOrDrop(
-                            // rust-lang/rust#52059: distinguish
-                            // between invaliding the backing storage
-                            // vs running a destructor.
-                            //
-                            // See also: rust-lang/rust#52782,
-                            // specifically #discussion_r206658948
-                            StorageDeadOrDrop::BoxedStorageDead))),
-                        LocalMutationIsAllowed::Yes,
-                        flow_state,
-                    );
-                }
-            }
-        }
-    }
-
     /// Checks an access to the given place to see if it is allowed. Examines the set of borrows
     /// that are in scope, as well as which paths have been initialized, to ensure that (a) the
     /// place is initialized and (b) it is not borrowed in some way that would prevent this
@@ -1103,7 +875,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         &mut self,
         context: Context,
         place_span: (&Place<'tcx>, Span),
-        kind: (ShallowOrDeep, ReadOrWrite),
+        kind: (AccessDepth, ReadOrWrite),
         is_local_mutation_allowed: LocalMutationIsAllowed,
         flow_state: &Flows<'cx, 'gcx, 'tcx>,
     ) {
@@ -1159,7 +931,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         &mut self,
         context: Context,
         place_span: (&Place<'tcx>, Span),
-        sd: ShallowOrDeep,
+        sd: AccessDepth,
         rw: ReadOrWrite,
         flow_state: &Flows<'cx, 'gcx, 'tcx>,
     ) -> bool {
@@ -1252,7 +1024,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                             error_reported = true;
                             this.report_conflicting_borrow(context, place_span, bk, &borrow)
                         }
-                        WriteKind::StorageDeadOrDrop(_) => {
+                        WriteKind::StorageDeadOrDrop => {
                             error_reported = true;
                             this.report_borrowed_value_does_not_live_long_enough(
                                 context,
@@ -1281,7 +1053,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         &mut self,
         context: Context,
         place_span: (&Place<'tcx>, Span),
-        kind: ShallowOrDeep,
+        kind: AccessDepth,
         mode: MutateMode,
         flow_state: &Flows<'cx, 'gcx, 'tcx>,
     ) {
@@ -1925,9 +1697,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
 
             Reservation(wk @ WriteKind::Move)
             | Write(wk @ WriteKind::Move)
-            | Reservation(wk @ WriteKind::StorageDeadOrDrop(_))
+            | Reservation(wk @ WriteKind::StorageDeadOrDrop)
             | Reservation(wk @ WriteKind::MutableBorrow(BorrowKind::Shared))
-            | Write(wk @ WriteKind::StorageDeadOrDrop(_))
+            | Write(wk @ WriteKind::StorageDeadOrDrop)
             | Write(wk @ WriteKind::MutableBorrow(BorrowKind::Shared)) => {
                 if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) {
                     if self.infcx.tcx.migrate_borrowck() {
@@ -1942,7 +1714,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                         error_access = match wk {
                             WriteKind::MutableBorrow(_) => AccessKind::MutableBorrow,
                             WriteKind::Move => AccessKind::Move,
-                            WriteKind::StorageDeadOrDrop(_) |
+                            WriteKind::StorageDeadOrDrop |
                             WriteKind::Mutate => AccessKind::Mutate,
                         };
                         self.report_mutability_error(
diff --git a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
index 755148b6992..f4052f94801 100644
--- a/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/explain_borrow/mod.rs
@@ -143,13 +143,15 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             Some(Cause::DropVar(local, location)) => match &mir.local_decls[local].name {
                 Some(local_name) => {
                     let mut should_note_order = false;
-                    if let Some((WriteKind::StorageDeadOrDrop(_), place)) = kind_place {
+                    if let Some((WriteKind::StorageDeadOrDrop, place)) = kind_place {
                         if let Place::Local(borrowed_local) = place {
                             let dropped_local_scope = mir.local_decls[local].visibility_scope;
                             let borrowed_local_scope =
                                 mir.local_decls[*borrowed_local].visibility_scope;
 
-                            if mir.is_sub_scope(borrowed_local_scope, dropped_local_scope) {
+                            if mir.is_sub_scope(borrowed_local_scope, dropped_local_scope)
+                                && local != *borrowed_local
+                            {
                                 should_note_order = true;
                             }
                         }
diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs
index cd760746c12..3d387963da9 100644
--- a/src/librustc_mir/borrow_check/nll/invalidation.rs
+++ b/src/librustc_mir/borrow_check/nll/invalidation.rs
@@ -11,32 +11,28 @@
 use borrow_check::borrow_set::BorrowSet;
 use borrow_check::location::LocationTable;
 use borrow_check::{JustWrite, WriteAndRead};
-use borrow_check::{ShallowOrDeep, Deep, Shallow};
+use borrow_check::{AccessDepth, Deep, Shallow};
 use borrow_check::{ReadOrWrite, Activation, Read, Reservation, Write};
 use borrow_check::{Context, ContextKind};
 use borrow_check::{LocalMutationIsAllowed, MutateMode};
 use borrow_check::ArtificialField;
-use borrow_check::{ReadKind, WriteKind, StorageDeadOrDrop};
+use borrow_check::{ReadKind, WriteKind};
 use borrow_check::nll::facts::AllFacts;
 use borrow_check::path_utils::*;
 use dataflow::move_paths::indexes::BorrowIndex;
-use rustc::hir::def_id::DefId;
-use rustc::infer::InferCtxt;
+use rustc::ty::TyCtxt;
 use rustc::mir::visit::Visitor;
-use rustc::mir::{BasicBlock, Location, Mir, Place, Rvalue, Local};
+use rustc::mir::{BasicBlock, Location, Mir, Place, Rvalue};
 use rustc::mir::{Statement, StatementKind};
 use rustc::mir::{Terminator, TerminatorKind};
-use rustc::mir::{Field, Operand, BorrowKind};
-use rustc::ty::{self, ParamEnv};
-use rustc_data_structures::indexed_vec::Idx;
+use rustc::mir::{Operand, BorrowKind};
 use rustc_data_structures::graph::dominators::Dominators;
 
 pub(super) fn generate_invalidates<'cx, 'gcx, 'tcx>(
-    infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
+    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
     all_facts: &mut Option<AllFacts>,
     location_table: &LocationTable,
     mir: &Mir<'tcx>,
-    mir_def_id: DefId,
     borrow_set: &BorrowSet<'tcx>,
 ) {
     if !all_facts.is_some() {
@@ -44,37 +40,32 @@ pub(super) fn generate_invalidates<'cx, 'gcx, 'tcx>(
         return;
     }
 
-    let param_env = infcx.tcx.param_env(mir_def_id);
-
     if let Some(all_facts) = all_facts {
         let dominators = mir.dominators();
         let mut ig = InvalidationGenerator {
             all_facts,
             borrow_set,
-            infcx,
+            tcx,
             location_table,
             mir,
             dominators,
-            param_env,
         };
         ig.visit_mir(mir);
     }
 }
 
-/// 'cg = the duration of the constraint generation process itself.
-struct InvalidationGenerator<'cg, 'cx: 'cg, 'tcx: 'cx, 'gcx: 'tcx> {
-    infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>,
-    all_facts: &'cg mut AllFacts,
-    location_table: &'cg LocationTable,
-    mir: &'cg Mir<'tcx>,
+struct InvalidationGenerator<'cx, 'tcx: 'cx, 'gcx: 'tcx> {
+    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
+    all_facts: &'cx mut AllFacts,
+    location_table: &'cx LocationTable,
+    mir: &'cx Mir<'tcx>,
     dominators: Dominators<BasicBlock>,
-    borrow_set: &'cg BorrowSet<'tcx>,
-    param_env: ParamEnv<'gcx>,
+    borrow_set: &'cx BorrowSet<'tcx>,
 }
 
 /// Visits the whole MIR and generates invalidates() facts
 /// Most of the code implementing this was stolen from borrow_check/mod.rs
-impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> {
+impl<'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx, 'gcx> {
     fn visit_statement(&mut self,
                        block: BasicBlock,
                        statement: &Statement<'tcx>,
@@ -154,8 +145,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tc
                 self.access_place(
                     ContextKind::StorageDead.new(location),
                     &Place::Local(local),
-                    (Shallow(None), Write(WriteKind::StorageDeadOrDrop(
-                        StorageDeadOrDrop::LocalStorageDead))),
+                    (Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
                     LocalMutationIsAllowed::Yes,
                 );
             }
@@ -184,12 +174,12 @@ impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tc
                 target: _,
                 unwind: _,
             } => {
-                let tcx = self.infcx.tcx;
-                let gcx = tcx.global_tcx();
-                let drop_place_ty = drop_place.ty(self.mir, tcx);
-                let drop_place_ty = tcx.erase_regions(&drop_place_ty).to_ty(tcx);
-                let drop_place_ty = gcx.lift(&drop_place_ty).unwrap();
-                self.visit_terminator_drop(location, terminator, drop_place, drop_place_ty);
+                self.access_place(
+                    ContextKind::Drop.new(location),
+                    drop_place,
+                    (AccessDepth::Drop, Write(WriteKind::StorageDeadOrDrop)),
+                    LocalMutationIsAllowed::Yes,
+                );
             }
             TerminatorKind::DropAndReplace {
                 location: ref drop_place,
@@ -286,83 +276,13 @@ impl<'cg, 'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cg, 'cx, 'tc
     }
 }
 
-impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> {
-    /// Simulates dropping of a variable
-    fn visit_terminator_drop(
-        &mut self,
-        loc: Location,
-        term: &Terminator<'tcx>,
-        drop_place: &Place<'tcx>,
-        erased_drop_place_ty: ty::Ty<'gcx>,
-    ) {
-        let gcx = self.infcx.tcx.global_tcx();
-        let drop_field = |
-            ig: &mut InvalidationGenerator<'cg, 'cx, 'gcx, 'tcx>,
-            (index, field): (usize, ty::Ty<'gcx>),
-        | {
-            let field_ty = gcx.normalize_erasing_regions(ig.param_env, field);
-            let place = drop_place.clone().field(Field::new(index), field_ty);
-
-            ig.visit_terminator_drop(loc, term, &place, field_ty);
-        };
-
-        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::Adt(def, substs) if def.is_struct() && !def.has_dtor(self.infcx.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::Tuple(tys) => {
-                tys.iter().cloned().enumerate()
-                    .for_each(|field| drop_field(self, field));
-            }
-            // Closures and generators also have disjoint fields, but they are only
-            // directly accessed in the body of the closure/generator.
-            ty::Generator(def, substs, ..)
-                if *drop_place == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty()
-            => {
-                substs.upvar_tys(def, self.infcx.tcx).enumerate()
-                    .for_each(|field| drop_field(self, field));
-            }
-            ty::Closure(def, substs)
-                if *drop_place == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty()
-                => {
-                    substs.upvar_tys(def, self.infcx.tcx).enumerate()
-                        .for_each(|field| drop_field(self, field));
-                }
-            _ => {
-                // 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 so, we assume that the destructor
-                // may access any data it likes (i.e., a Deep Write).
-                if erased_drop_place_ty.needs_drop(gcx, self.param_env) {
-                    self.access_place(
-                        ContextKind::Drop.new(loc),
-                        drop_place,
-                        (Deep, Write(WriteKind::StorageDeadOrDrop(
-                            StorageDeadOrDrop::Destructor))),
-                        LocalMutationIsAllowed::Yes,
-                    );
-                }
-            }
-        }
-    }
-
+impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cx, 'tcx, 'gcx> {
     /// Simulates mutation of a place
     fn mutate_place(
         &mut self,
         context: Context,
         place: &Place<'tcx>,
-        kind: ShallowOrDeep,
+        kind: AccessDepth,
         _mode: MutateMode,
     ) {
         self.access_place(
@@ -412,7 +332,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> {
                     BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
                     BorrowKind::Unique | BorrowKind::Mut { .. } => {
                         let wk = WriteKind::MutableBorrow(bk);
-                        if allow_two_phase_borrow(&self.infcx.tcx, bk) {
+                        if allow_two_phase_borrow(&self.tcx, bk) {
                             (Deep, Reservation(wk))
                         } else {
                             (Deep, Write(wk))
@@ -471,7 +391,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> {
         &mut self,
         context: Context,
         place: &Place<'tcx>,
-        kind: (ShallowOrDeep, ReadOrWrite),
+        kind: (AccessDepth, ReadOrWrite),
         _is_local_mutation_allowed: LocalMutationIsAllowed,
     ) {
         let (sd, rw) = kind;
@@ -483,7 +403,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> {
         &mut self,
         context: Context,
         place: &Place<'tcx>,
-        sd: ShallowOrDeep,
+        sd: AccessDepth,
         rw: ReadOrWrite,
     ) {
         debug!(
@@ -494,7 +414,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> {
             sd,
             rw,
         );
-        let tcx = self.infcx.tcx;
+        let tcx = self.tcx;
         let mir = self.mir;
         let borrow_set = self.borrow_set.clone();
         let indices = self.borrow_set.borrows.indices();
@@ -527,7 +447,7 @@ impl<'cg, 'cx, 'tcx, 'gcx> InvalidationGenerator<'cg, 'cx, 'tcx, 'gcx> {
                         // Reading from mere reservations of mutable-borrows is OK.
                         if !is_active(&this.dominators, borrow, context.loc) {
                             // If the borrow isn't active yet, reads don't invalidate it
-                            assert!(allow_two_phase_borrow(&this.infcx.tcx, borrow.kind));
+                            assert!(allow_two_phase_borrow(&this.tcx, borrow.kind));
                             return Control::Continue;
                         }
 
diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs
index 7cab0acb432..b9f22c7402a 100644
--- a/src/librustc_mir/borrow_check/nll/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/mod.rs
@@ -160,11 +160,10 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
 
     // Generate various additional constraints.
     invalidation::generate_invalidates(
-        infcx,
+        infcx.tcx,
         &mut all_facts,
         location_table,
         &mir,
-        def_id,
         borrow_set,
     );
 
diff --git a/src/librustc_mir/borrow_check/path_utils.rs b/src/librustc_mir/borrow_check/path_utils.rs
index 70b4e0ea2f0..67787d23db6 100644
--- a/src/librustc_mir/borrow_check/path_utils.rs
+++ b/src/librustc_mir/borrow_check/path_utils.rs
@@ -11,7 +11,7 @@
 use borrow_check::borrow_set::{BorrowSet, BorrowData, TwoPhaseActivation};
 use borrow_check::places_conflict;
 use borrow_check::Context;
-use borrow_check::ShallowOrDeep;
+use borrow_check::AccessDepth;
 use dataflow::indexes::BorrowIndex;
 use rustc::mir::{BasicBlock, Location, Mir, Place};
 use rustc::mir::{ProjectionElem, BorrowKind};
@@ -43,7 +43,7 @@ pub(super) fn each_borrow_involving_path<'a, 'tcx, 'gcx: 'tcx, F, I, S> (
     tcx: TyCtxt<'a, 'gcx, 'tcx>,
     mir: &Mir<'tcx>,
     _context: Context,
-    access_place: (ShallowOrDeep, &Place<'tcx>),
+    access_place: (AccessDepth, &Place<'tcx>),
     borrow_set: &BorrowSet<'tcx>,
     candidates: I,
     mut op: F,
diff --git a/src/librustc_mir/borrow_check/places_conflict.rs b/src/librustc_mir/borrow_check/places_conflict.rs
index 3f055283e0c..e371b34c480 100644
--- a/src/librustc_mir/borrow_check/places_conflict.rs
+++ b/src/librustc_mir/borrow_check/places_conflict.rs
@@ -10,7 +10,7 @@
 
 use borrow_check::ArtificialField;
 use borrow_check::Overlap;
-use borrow_check::{Deep, Shallow, ShallowOrDeep};
+use borrow_check::{Deep, Shallow, AccessDepth};
 use rustc::hir;
 use rustc::mir::{Mir, Place};
 use rustc::mir::{Projection, ProjectionElem};
@@ -22,7 +22,7 @@ pub(super) fn places_conflict<'gcx, 'tcx>(
     mir: &Mir<'tcx>,
     borrow_place: &Place<'tcx>,
     access_place: &Place<'tcx>,
-    access: ShallowOrDeep,
+    access: AccessDepth,
 ) -> bool {
     debug!(
         "places_conflict({:?},{:?},{:?})",
@@ -49,7 +49,7 @@ fn place_components_conflict<'gcx, 'tcx>(
     mir: &Mir<'tcx>,
     mut borrow_components: PlaceComponentsIter<'_, 'tcx>,
     mut access_components: PlaceComponentsIter<'_, 'tcx>,
-    access: ShallowOrDeep,
+    access: AccessDepth,
 ) -> bool {
     // The borrowck rules for proving disjointness are applied from the "root" of the
     // borrow forwards, iterating over "similar" projections in lockstep until
@@ -179,16 +179,26 @@ fn place_components_conflict<'gcx, 'tcx>(
                         return false;
                     }
                     (ProjectionElem::Deref, ty::Ref(_, _, hir::MutImmutable), _) => {
-                        // the borrow goes through a dereference of a shared reference.
-                        //
-                        // I'm not sure why we are tracking these borrows - shared
-                        // references can *always* be aliased, which means the
-                        // permission check already account for this borrow.
-                        debug!("places_conflict: behind a shared ref");
+                        // Shouldn't be tracked
+                        bug!("Tracking borrow behind shared reference.");
+                    }
+                    (ProjectionElem::Deref, ty::Ref(_, _, hir::MutMutable), AccessDepth::Drop) => {
+                        // Values behind a mutatble reference are not access either by Dropping a
+                        // value, or by StorageDead
+                        debug!("places_conflict: drop access behind ptr");
                         return false;
                     }
 
+                    (ProjectionElem::Field { .. }, ty::Adt(def, _), AccessDepth::Drop) => {
+                        // Drop can read/write arbitrary projections, so places
+                        // conflict regardless of further projections.
+                        if def.has_dtor(tcx) {
+                            return true;
+                        }
+                    }
+
                     (ProjectionElem::Deref, _, Deep)
+                    | (ProjectionElem::Deref, _, AccessDepth::Drop)
                     | (ProjectionElem::Field { .. }, _, _)
                     | (ProjectionElem::Index { .. }, _, _)
                     | (ProjectionElem::ConstantIndex { .. }, _, _)
diff --git a/src/test/ui/dropck/dropck-union.nll.stderr b/src/test/ui/dropck/dropck-union.nll.stderr
index 35d7ffc7879..ffb322b85dc 100644
--- a/src/test/ui/dropck/dropck-union.nll.stderr
+++ b/src/test/ui/dropck/dropck-union.nll.stderr
@@ -8,8 +8,6 @@ LL | }
    | |
    | `v` dropped here while still borrowed
    | borrow later used here, when `v` is dropped
-   |
-   = note: values in a scope are dropped in the opposite order they are defined
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/generator/dropck.nll.stderr b/src/test/ui/generator/dropck.nll.stderr
index b49bf817150..ef7e64ffd97 100644
--- a/src/test/ui/generator/dropck.nll.stderr
+++ b/src/test/ui/generator/dropck.nll.stderr
@@ -9,6 +9,8 @@ LL | }
    | |
    | `*cell` dropped here while still borrowed
    | borrow later used here, when `gen` is dropped
+   |
+   = note: values in a scope are dropped in the opposite order they are defined
 
 error[E0597]: `ref_` does not live long enough
   --> $DIR/dropck.rs:22:11
diff --git a/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.migrate.stderr b/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.migrate.stderr
index 70d819f0f46..5c753817e35 100644
--- a/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.migrate.stderr
+++ b/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.migrate.stderr
@@ -23,7 +23,7 @@ LL |     &mut *(*s).0 //[nll]~ ERROR borrow may still be in use when destructor
    |     ^^^^^^^^^^^^
 ...
 LL | }
-   | - here, drop of `*s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
+   | - here, drop of `s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
    |
 note: borrowed value must be valid for the lifetime 'a as defined on the function body at 72:20...
   --> $DIR/issue-45696-scribble-on-boxed-borrow.rs:72:20
@@ -41,7 +41,7 @@ LL |     &mut *(**s).0 //[nll]~ ERROR borrow may still be in use when destructor
    |     ^^^^^^^^^^^^^
 ...
 LL | }
-   | - here, drop of `**s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
+   | - here, drop of `s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
    |
 note: borrowed value must be valid for the lifetime 'a as defined on the function body at 82:26...
   --> $DIR/issue-45696-scribble-on-boxed-borrow.rs:82:26
diff --git a/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.nll.stderr b/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.nll.stderr
index 72ec5affb18..79a7c0631f4 100644
--- a/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.nll.stderr
+++ b/src/test/ui/issues/issue-45696-scribble-on-boxed-borrow.nll.stderr
@@ -20,7 +20,7 @@ LL |     &mut *(*s).0 //[nll]~ ERROR borrow may still be in use when destructor
    |     ^^^^^^^^^^^^
 ...
 LL | }
-   | - here, drop of `*s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
+   | - here, drop of `s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
    |
 note: borrowed value must be valid for the lifetime 'a as defined on the function body at 72:20...
   --> $DIR/issue-45696-scribble-on-boxed-borrow.rs:72:20
@@ -35,7 +35,7 @@ LL |     &mut *(**s).0 //[nll]~ ERROR borrow may still be in use when destructor
    |     ^^^^^^^^^^^^^
 ...
 LL | }
-   | - here, drop of `**s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
+   | - here, drop of `s` needs exclusive access to `*s.0`, because the type `Scribble<'_>` implements the `Drop` trait
    |
 note: borrowed value must be valid for the lifetime 'a as defined on the function body at 82:26...
   --> $DIR/issue-45696-scribble-on-boxed-borrow.rs:82:26
diff --git a/src/test/ui/nll/enum-drop-access.rs b/src/test/ui/nll/enum-drop-access.rs
new file mode 100644
index 00000000000..dc436d20fd8
--- /dev/null
+++ b/src/test/ui/nll/enum-drop-access.rs
@@ -0,0 +1,51 @@
+#![feature(nll)]
+
+enum DropOption<T> {
+    Some(T),
+    None,
+}
+
+impl<T> Drop for DropOption<T> {
+    fn drop(&mut self) {}
+}
+
+// Dropping opt could access the value behind the reference,
+fn drop_enum(opt: DropOption<&mut i32>) -> Option<&mut i32> {
+    match opt {
+        DropOption::Some(&mut ref mut r) => { //~ ERROR
+            Some(r)
+        },
+        DropOption::None => None,
+    }
+}
+
+fn optional_drop_enum(opt: Option<DropOption<&mut i32>>) -> Option<&mut i32> {
+    match opt {
+        Some(DropOption::Some(&mut ref mut r)) => { //~ ERROR
+            Some(r)
+        },
+        Some(DropOption::None) | None => None,
+    }
+}
+
+// Ok, dropping opt doesn't access the reference
+fn optional_tuple(opt: Option<(&mut i32, String)>) -> Option<&mut i32> {
+    match opt {
+        Some((&mut ref mut r, _)) => {
+            Some(r)
+        },
+        None => None,
+    }
+}
+
+// Ok, dropping res doesn't access the Ok case.
+fn different_variants(res: Result<&mut i32, String>) -> Option<&mut i32> {
+    match res {
+        Ok(&mut ref mut r) => {
+            Some(r)
+        },
+        Err(_) => None,
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/enum-drop-access.stderr b/src/test/ui/nll/enum-drop-access.stderr
new file mode 100644
index 00000000000..57daf26596d
--- /dev/null
+++ b/src/test/ui/nll/enum-drop-access.stderr
@@ -0,0 +1,45 @@
+error[E0713]: borrow may still be in use when destructor runs
+  --> $DIR/enum-drop-access.rs:15:31
+   |
+LL |         DropOption::Some(&mut ref mut r) => { //~ ERROR
+   |                               ^^^^^^^^^
+...
+LL | }
+   | - here, drop of `opt` needs exclusive access to `*opt.0`, because the type `DropOption<&mut i32>` implements the `Drop` trait
+   |
+note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 13:1...
+  --> $DIR/enum-drop-access.rs:13:1
+   |
+LL | / fn drop_enum(opt: DropOption<&mut i32>) -> Option<&mut i32> {
+LL | |     match opt {
+LL | |         DropOption::Some(&mut ref mut r) => { //~ ERROR
+LL | |             Some(r)
+...  |
+LL | |     }
+LL | | }
+   | |_^
+
+error[E0713]: borrow may still be in use when destructor runs
+  --> $DIR/enum-drop-access.rs:24:36
+   |
+LL |         Some(DropOption::Some(&mut ref mut r)) => { //~ ERROR
+   |                                    ^^^^^^^^^
+...
+LL | }
+   | - here, drop of `opt` needs exclusive access to `*opt.0.0`, because the type `DropOption<&mut i32>` implements the `Drop` trait
+   |
+note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 22:1...
+  --> $DIR/enum-drop-access.rs:22:1
+   |
+LL | / fn optional_drop_enum(opt: Option<DropOption<&mut i32>>) -> Option<&mut i32> {
+LL | |     match opt {
+LL | |         Some(DropOption::Some(&mut ref mut r)) => { //~ ERROR
+LL | |             Some(r)
+...  |
+LL | |     }
+LL | | }
+   | |_^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0713`.
diff --git a/src/test/ui/span/borrowck-ref-into-rvalue.nll.stderr b/src/test/ui/span/borrowck-ref-into-rvalue.nll.stderr
index c94558f12bb..c565842c2c0 100644
--- a/src/test/ui/span/borrowck-ref-into-rvalue.nll.stderr
+++ b/src/test/ui/span/borrowck-ref-into-rvalue.nll.stderr
@@ -1,11 +1,11 @@
-error[E0713]: borrow may still be in use when destructor runs
-  --> $DIR/borrowck-ref-into-rvalue.rs:14:14
+error[E0597]: borrowed value does not live long enough
+  --> $DIR/borrowck-ref-into-rvalue.rs:13:11
    |
-LL |         Some(ref m) => {
-   |              ^^^^^
+LL |     match Some("Hello".to_string()) {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^ temporary value does not live long enough
 ...
 LL |     }
-   |     - drop of temporary value occurs here
+   |     - temporary value only lives until here
 LL |     println!("{}", *msg);
    |                    ---- borrow later used here
    |
@@ -13,4 +13,4 @@ LL |     println!("{}", *msg);
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0713`.
+For more information about this error, try `rustc --explain E0597`.
diff --git a/src/test/ui/span/dropck_arr_cycle_checked.nll.stderr b/src/test/ui/span/dropck_arr_cycle_checked.nll.stderr
index 76a25fa661e..74db695e7e5 100644
--- a/src/test/ui/span/dropck_arr_cycle_checked.nll.stderr
+++ b/src/test/ui/span/dropck_arr_cycle_checked.nll.stderr
@@ -9,6 +9,8 @@ LL | }
    | |
    | `b2` dropped here while still borrowed
    | borrow later used here, when `b1` is dropped
+   |
+   = note: values in a scope are dropped in the opposite order they are defined
 
 error[E0597]: `b3` does not live long enough
   --> $DIR/dropck_arr_cycle_checked.rs:105:24
@@ -21,6 +23,8 @@ LL | }
    | |
    | `b3` dropped here while still borrowed
    | borrow later used here, when `b1` is dropped
+   |
+   = note: values in a scope are dropped in the opposite order they are defined
 
 error[E0597]: `b1` does not live long enough
   --> $DIR/dropck_arr_cycle_checked.rs:111:24
diff --git a/src/test/ui/span/dropck_direct_cycle_with_drop.nll.stderr b/src/test/ui/span/dropck_direct_cycle_with_drop.nll.stderr
index 2884b1818ba..baf3cef2ae8 100644
--- a/src/test/ui/span/dropck_direct_cycle_with_drop.nll.stderr
+++ b/src/test/ui/span/dropck_direct_cycle_with_drop.nll.stderr
@@ -23,8 +23,6 @@ LL | }
    | |
    | `d1` dropped here while still borrowed
    | borrow later used here, when `d1` is dropped
-   |
-   = note: values in a scope are dropped in the opposite order they are defined
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/span/dropck_vec_cycle_checked.nll.stderr b/src/test/ui/span/dropck_vec_cycle_checked.nll.stderr
index e6f43e0a71b..f7ff4e5169f 100644
--- a/src/test/ui/span/dropck_vec_cycle_checked.nll.stderr
+++ b/src/test/ui/span/dropck_vec_cycle_checked.nll.stderr
@@ -9,6 +9,8 @@ LL | }
    | |
    | `c2` dropped here while still borrowed
    | borrow later used here, when `c1` is dropped
+   |
+   = note: values in a scope are dropped in the opposite order they are defined
 
 error[E0597]: `c3` does not live long enough
   --> $DIR/dropck_vec_cycle_checked.rs:115:24
@@ -21,6 +23,8 @@ LL | }
    | |
    | `c3` dropped here while still borrowed
    | borrow later used here, when `c1` is dropped
+   |
+   = note: values in a scope are dropped in the opposite order they are defined
 
 error[E0597]: `c1` does not live long enough
   --> $DIR/dropck_vec_cycle_checked.rs:121:24
diff --git a/src/test/ui/span/issue-29106.nll.stderr b/src/test/ui/span/issue-29106.nll.stderr
index 2cf408d097b..a1451866e67 100644
--- a/src/test/ui/span/issue-29106.nll.stderr
+++ b/src/test/ui/span/issue-29106.nll.stderr
@@ -8,6 +8,8 @@ LL |     }
    |     |
    |     `x` dropped here while still borrowed
    |     borrow later used here, when `y` is dropped
+   |
+   = note: values in a scope are dropped in the opposite order they are defined
 
 error[E0597]: `x` does not live long enough
   --> $DIR/issue-29106.rs:33:25
@@ -19,6 +21,8 @@ LL |     }
    |     |
    |     `x` dropped here while still borrowed
    |     borrow later used here, when `y` is dropped
+   |
+   = note: values in a scope are dropped in the opposite order they are defined
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/span/issue28498-reject-ex1.nll.stderr b/src/test/ui/span/issue28498-reject-ex1.nll.stderr
index 1f72b78ebc7..0f6f6e381d8 100644
--- a/src/test/ui/span/issue28498-reject-ex1.nll.stderr
+++ b/src/test/ui/span/issue28498-reject-ex1.nll.stderr
@@ -11,7 +11,6 @@ LL | }
    | borrow later used here, when `foo` is dropped
    |
    = note: consider using a `let` binding to create a longer lived value
-   = note: values in a scope are dropped in the opposite order they are defined
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/span/vec-must-not-hide-type-from-dropck.nll.stderr b/src/test/ui/span/vec-must-not-hide-type-from-dropck.nll.stderr
index ee51304800d..292c007f512 100644
--- a/src/test/ui/span/vec-must-not-hide-type-from-dropck.nll.stderr
+++ b/src/test/ui/span/vec-must-not-hide-type-from-dropck.nll.stderr
@@ -9,6 +9,8 @@ LL | }
    | |
    | `c2` dropped here while still borrowed
    | borrow later used here, when `c1` is dropped
+   |
+   = note: values in a scope are dropped in the opposite order they are defined
 
 error[E0597]: `c1` does not live long enough
   --> $DIR/vec-must-not-hide-type-from-dropck.rs:129:24
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.nll.stderr b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.nll.stderr
index 8cda1e60ba9..afd90237d16 100644
--- a/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.nll.stderr
+++ b/src/test/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-1.nll.stderr
@@ -11,8 +11,6 @@ LL | }
    | |
    | `factorial` dropped here while still borrowed
    | borrow later used here, when `factorial` is dropped
-   |
-   = note: values in a scope are dropped in the opposite order they are defined
 
 error[E0506]: cannot assign to `factorial` because it is borrowed
   --> $DIR/unboxed-closures-failed-recursive-fn-1.rs:30:5