about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-10-17 09:13:51 +0000
committerbors <bors@rust-lang.org>2018-10-17 09:13:51 +0000
commitcbbd70d4f25bc255d80b6b9ba0a65f6c5957f2b7 (patch)
tree85fe0b9b58b570a6483c9dd5d6344f84b4b0518c /src
parent9d7f0da367bae7587ed0ef69bd4384392099fc9e (diff)
parent233fdb4b1486b3b98bd1bb80f83138924a8a734e (diff)
downloadrust-cbbd70d4f25bc255d80b6b9ba0a65f6c5957f2b7.tar.gz
rust-cbbd70d4f25bc255d80b6b9ba0a65f6c5957f2b7.zip
Auto merge of #54941 - pnkfelix:issue-21232-reject-partial-reinit, r=nikomatsakis
reject partial init and reinit of uninitialized data

Reject partial initialization of uninitialized structured types (i.e. structs and tuples) and also reject partial *reinitialization* of such types.

Fix #54986

Fix #54499

cc #21232
Diffstat (limited to 'src')
-rw-r--r--src/librustc/mir/mod.rs14
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs25
-rw-r--r--src/librustc_mir/borrow_check/mod.rs188
-rw-r--r--src/test/run-pass/issues/issue-26996.rs14
-rw-r--r--src/test/run-pass/issues/issue-27021.rs16
-rw-r--r--src/test/run-pass/issues/issue-49298.rs16
-rw-r--r--src/test/ui/borrowck/assign_mutable_fields.nll.stderr16
-rw-r--r--src/test/ui/borrowck/borrowck-field-sensitivity.nll.stderr28
-rw-r--r--src/test/ui/borrowck/borrowck-init-in-fru.ast.nll.stderr2
-rw-r--r--src/test/ui/borrowck/borrowck-init-in-fru.mir.stderr2
-rw-r--r--src/test/ui/borrowck/borrowck-uninit-field-access.ast.nll.stderr4
-rw-r--r--src/test/ui/borrowck/borrowck-uninit-field-access.mir.stderr4
-rw-r--r--src/test/ui/borrowck/borrowck-uninit-ref-chain.ast.nll.stderr36
-rw-r--r--src/test/ui/borrowck/borrowck-uninit-ref-chain.mir.stderr36
-rw-r--r--src/test/ui/borrowck/borrowck-uninit-ref-chain.rs16
-rw-r--r--src/test/ui/borrowck/borrowck-union-move-assign.nll.stderr6
-rw-r--r--src/test/ui/borrowck/borrowck-union-move.nll.stderr8
-rw-r--r--src/test/ui/borrowck/borrowck-union-uninitialized.nll.stderr16
-rw-r--r--src/test/ui/borrowck/borrowck-use-in-index-lvalue.ast.nll.stderr4
-rw-r--r--src/test/ui/borrowck/borrowck-use-in-index-lvalue.mir.stderr4
-rw-r--r--src/test/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.ast.nll.stderr2
-rw-r--r--src/test/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.mir.stderr2
-rw-r--r--src/test/ui/borrowck/borrowck-use-uninitialized-in-cast.ast.nll.stderr2
-rw-r--r--src/test/ui/borrowck/borrowck-use-uninitialized-in-cast.mir.stderr2
-rw-r--r--src/test/ui/borrowck/issue-54499-field-mutation-marks-mut-as-used.ast.stderr39
-rw-r--r--src/test/ui/borrowck/issue-54499-field-mutation-marks-mut-as-used.nll.stderr21
-rw-r--r--src/test/ui/borrowck/issue-54499-field-mutation-marks-mut-as-used.rs49
-rw-r--r--src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out-with-mut.ast.stderr69
-rw-r--r--src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out-with-mut.nll.stderr33
-rw-r--r--src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out-with-mut.rs43
-rw-r--r--src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out.ast.stderr124
-rw-r--r--src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out.nll.stderr88
-rw-r--r--src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out.rs64
-rw-r--r--src/test/ui/borrowck/issue-54499-field-mutation-of-never-init.ast.stderr91
-rw-r--r--src/test/ui/borrowck/issue-54499-field-mutation-of-never-init.nll.stderr21
-rw-r--r--src/test/ui/borrowck/issue-54499-field-mutation-of-never-init.rs55
-rw-r--r--src/test/ui/borrowck/reassignment_immutable_fields.nll.stderr41
-rw-r--r--src/test/ui/borrowck/reassignment_immutable_fields_overlapping.nll.stderr19
-rw-r--r--src/test/ui/borrowck/reassignment_immutable_fields_twice.nll.stderr29
-rw-r--r--src/test/ui/issues/issue-17385.nll.stderr2
-rw-r--r--src/test/ui/issues/issue-27282-move-match-input-into-guard.rs2
-rw-r--r--src/test/ui/issues/issue-27282-move-match-input-into-guard.stderr2
-rw-r--r--src/test/ui/liveness/liveness-use-after-move.nll.stderr2
-rw-r--r--src/test/ui/nll/issue-21232-partial-init-and-erroneous-use.rs66
-rw-r--r--src/test/ui/nll/issue-21232-partial-init-and-erroneous-use.stderr48
-rw-r--r--src/test/ui/nll/issue-21232-partial-init-and-use.rs311
-rw-r--r--src/test/ui/nll/issue-21232-partial-init-and-use.stderr186
-rw-r--r--src/test/ui/nll/issue-51512.rs2
-rw-r--r--src/test/ui/nll/issue-51512.stderr2
-rw-r--r--src/test/ui/union/union-borrow-move-parent-sibling.nll.stderr6
-rw-r--r--src/test/ui/use/use-after-move-self-based-on-type.nll.stderr13
-rw-r--r--src/test/ui/use/use-after-move-self.nll.stderr2
-rw-r--r--src/test/ui/walk-struct-literal-with.nll.stderr2
53 files changed, 1689 insertions, 206 deletions
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 48b2ccbcf87..9a0623ca539 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -1967,7 +1967,10 @@ impl<'tcx> Place<'tcx> {
         Place::Projection(Box::new(PlaceProjection { base: self, elem }))
     }
 
-    /// Find the innermost `Local` from this `Place`.
+    /// Find the innermost `Local` from this `Place`, *if* it is either a local itself or
+    /// a single deref of a local.
+    ///
+    /// FIXME: can we safely swap the semantics of `fn base_local` below in here instead?
     pub fn local(&self) -> Option<Local> {
         match self {
             Place::Local(local) |
@@ -1978,6 +1981,15 @@ impl<'tcx> Place<'tcx> {
             _ => None,
         }
     }
+
+    /// Find the innermost `Local` from this `Place`.
+    pub fn base_local(&self) -> Option<Local> {
+        match self {
+            Place::Local(local) => Some(*local),
+            Place::Projection(box Projection { base, elem: _ }) => base.base_local(),
+            Place::Promoted(..) | Place::Static(..) => None,
+        }
+    }
 }
 
 impl<'tcx> Debug for Place<'tcx> {
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index 53a190efb58..546746aa72e 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -51,16 +51,16 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         &mut self,
         context: Context,
         desired_action: InitializationRequiringAction,
-        (place, span): (&Place<'tcx>, Span),
+        (moved_place, used_place, span): (&Place<'tcx>, &Place<'tcx>, Span),
         mpi: MovePathIndex,
     ) {
         debug!(
-            "report_use_of_moved_or_uninitialized: context={:?} desired_action={:?} place={:?} \
-             span={:?} mpi={:?}",
-            context, desired_action, place, span, mpi
+            "report_use_of_moved_or_uninitialized: context={:?} desired_action={:?} \
+             moved_place={:?} used_place={:?} span={:?} mpi={:?}",
+            context, desired_action, moved_place, used_place, span, mpi
         );
 
-        let use_spans = self.move_spans(place, context.loc)
+        let use_spans = self.move_spans(moved_place, context.loc)
             .or_else(|| self.borrow_spans(span, context.loc));
         let span = use_spans.args_or_use();
 
@@ -75,7 +75,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             .collect();
 
         if move_out_indices.is_empty() {
-            let root_place = self.prefixes(&place, PrefixSet::All).last().unwrap();
+            let root_place = self.prefixes(&used_place, PrefixSet::All).last().unwrap();
 
             if self.uninitialized_error_reported
                 .contains(&root_place.clone())
@@ -89,14 +89,15 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
 
             self.uninitialized_error_reported.insert(root_place.clone());
 
-            let item_msg = match self.describe_place_with_options(place, IncludingDowncast(true)) {
+            let item_msg = match self.describe_place_with_options(used_place,
+                                                                  IncludingDowncast(true)) {
                 Some(name) => format!("`{}`", name),
                 None => "value".to_owned(),
             };
             let mut err = self.infcx.tcx.cannot_act_on_uninitialized_variable(
                 span,
                 desired_action.as_noun(),
-                &self.describe_place_with_options(place, IncludingDowncast(true))
+                &self.describe_place_with_options(moved_place, IncludingDowncast(true))
                     .unwrap_or("_".to_owned()),
                 Origin::Mir,
             );
@@ -111,7 +112,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         } else {
             if let Some((reported_place, _)) = self.move_error_reported.get(&move_out_indices) {
                 if self.prefixes(&reported_place, PrefixSet::All)
-                    .any(|p| p == place)
+                    .any(|p| p == used_place)
                 {
                     debug!(
                         "report_use_of_moved_or_uninitialized place: error suppressed \
@@ -128,7 +129,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 span,
                 desired_action.as_noun(),
                 msg,
-                self.describe_place_with_options(&place, IncludingDowncast(true)),
+                self.describe_place_with_options(&moved_place, IncludingDowncast(true)),
                 Origin::Mir,
             );
 
@@ -181,7 +182,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 );
             }
 
-            if let Some(ty) = self.retrieve_type_for_place(place) {
+            if let Some(ty) = self.retrieve_type_for_place(used_place) {
                 let needs_note = match ty.sty {
                     ty::Closure(id, _) => {
                         let tables = self.infcx.tcx.typeck_tables_of(id);
@@ -219,7 +220,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             }
 
             if let Some((_, mut old_err)) = self.move_error_reported
-                .insert(move_out_indices, (place.clone(), err))
+                .insert(move_out_indices, (used_place.clone(), err))
             {
                 // Cancel the old error so it doesn't ICE.
                 old_err.cancel();
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 9cbaf35acd3..1f8d077fb69 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -36,9 +36,8 @@ use std::collections::BTreeMap;
 
 use syntax_pos::Span;
 
-use dataflow::indexes::BorrowIndex;
-use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MoveError, MovePathIndex};
-use dataflow::move_paths::indexes::MoveOutIndex;
+use dataflow::indexes::{BorrowIndex, InitIndex, MoveOutIndex, MovePathIndex};
+use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MoveError};
 use dataflow::Borrows;
 use dataflow::DataflowResultsConsumer;
 use dataflow::FlowAtLocation;
@@ -853,6 +852,7 @@ enum InitializationRequiringAction {
     MatchOn,
     Use,
     Assignment,
+    PartialAssignment,
 }
 
 struct RootPlace<'d, 'tcx: 'd> {
@@ -868,6 +868,7 @@ impl InitializationRequiringAction {
             InitializationRequiringAction::MatchOn => "use", // no good noun
             InitializationRequiringAction::Use => "use",
             InitializationRequiringAction::Assignment => "assign",
+            InitializationRequiringAction::PartialAssignment => "assign to part",
         }
     }
 
@@ -878,6 +879,7 @@ impl InitializationRequiringAction {
             InitializationRequiringAction::MatchOn => "matched on",
             InitializationRequiringAction::Use => "used",
             InitializationRequiringAction::Assignment => "assigned",
+            InitializationRequiringAction::PartialAssignment => "partially assigned",
         }
     }
 }
@@ -1439,10 +1441,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         debug!("check_if_reassignment_to_immutable_state({:?})", local);
 
         // Check if any of the initializiations of `local` have happened yet:
-        let mpi = self.move_data.rev_lookup.find_local(local);
-        let init_indices = &self.move_data.init_path_map[mpi];
-        let first_init_index = init_indices.iter().find(|&ii| flow_state.ever_inits.contains(*ii));
-        if let Some(&init_index) = first_init_index {
+        if let Some(init_index) = self.is_local_ever_initialized(local, flow_state) {
             // And, if so, report an error.
             let init = &self.move_data.inits[init_index];
             let span = init.span(&self.mir);
@@ -1498,12 +1497,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
 
         debug!("check_if_full_path_is_moved place: {:?}", place_span.0);
         match self.move_path_closest_to(place_span.0) {
-            Ok(mpi) => {
+            Ok((prefix, mpi)) => {
                 if maybe_uninits.contains(mpi) {
                     self.report_use_of_moved_or_uninitialized(
                         context,
                         desired_action,
-                        place_span,
+                        (prefix, place_span.0, place_span.1),
                         mpi,
                     );
                     return; // don't bother finding other problems.
@@ -1561,7 +1560,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 self.report_use_of_moved_or_uninitialized(
                     context,
                     desired_action,
-                    place_span,
+                    (place_span.0, place_span.0, place_span.1),
                     child_mpi,
                 );
                 return; // don't bother finding other problems.
@@ -1579,14 +1578,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
     /// An Err result includes a tag indicated why the search failed.
     /// Currently this can only occur if the place is built off of a
     /// static variable, as we do not track those in the MoveData.
-    fn move_path_closest_to(
+    fn move_path_closest_to<'a>(
         &mut self,
-        place: &Place<'tcx>,
-    ) -> Result<MovePathIndex, NoMovePathFound> {
+        place: &'a Place<'tcx>,
+    ) -> Result<(&'a Place<'tcx>, MovePathIndex), NoMovePathFound> where 'cx: 'a {
         let mut last_prefix = place;
         for prefix in self.prefixes(place, PrefixSet::All) {
             if let Some(mpi) = self.move_path_for_place(prefix) {
-                return Ok(mpi);
+                return Ok((prefix, mpi));
             }
             last_prefix = prefix;
         }
@@ -1667,6 +1666,26 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                                     // recur further)
                                     break;
                                 }
+
+
+                                // Once `let s; s.x = V; read(s.x);`,
+                                // is allowed, remove this match arm.
+                                ty::Adt(..) | ty::Tuple(..) => {
+                                    check_parent_of_field(self, context, base, span, flow_state);
+
+                                    if let Some(local) = place.base_local() {
+                                        // rust-lang/rust#21232,
+                                        // #54499, #54986: during
+                                        // period where we reject
+                                        // partial initialization, do
+                                        // not complain about
+                                        // unnecessary `mut` on an
+                                        // attempt to do a partial
+                                        // initialization.
+                                        self.used_mut.insert(local);
+                                    }
+                                }
+
                                 _ => {}
                             }
                         }
@@ -1677,8 +1696,73 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 }
             }
         }
-    }
 
+        fn check_parent_of_field<'cx, 'gcx, 'tcx>(this: &mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>,
+                                                  context: Context,
+                                                  base: &Place<'tcx>,
+                                                  span: Span,
+                                                  flow_state: &Flows<'cx, 'gcx, 'tcx>)
+        {
+            // rust-lang/rust#21232: Until Rust allows reads from the
+            // initialized parts of partially initialized structs, we
+            // will, starting with the 2018 edition, reject attempts
+            // to write to structs that are not fully initialized.
+            //
+            // In other words, *until* we allow this:
+            //
+            // 1. `let mut s; s.x = Val; read(s.x);`
+            //
+            // we will for now disallow this:
+            //
+            // 2. `let mut s; s.x = Val;`
+            //
+            // and also this:
+            //
+            // 3. `let mut s = ...; drop(s); s.x=Val;`
+            //
+            // This does not use check_if_path_or_subpath_is_moved,
+            // because we want to *allow* reinitializations of fields:
+            // e.g. want to allow
+            //
+            // `let mut s = ...; drop(s.x); s.x=Val;`
+            //
+            // This does not use check_if_full_path_is_moved on
+            // `base`, because that would report an error about the
+            // `base` as a whole, but in this scenario we *really*
+            // want to report an error about the actual thing that was
+            // moved, which may be some prefix of `base`.
+
+            // Shallow so that we'll stop at any dereference; we'll
+            // report errors about issues with such bases elsewhere.
+            let maybe_uninits = &flow_state.uninits;
+
+            // Find the shortest uninitialized prefix you can reach
+            // without going over a Deref.
+            let mut shortest_uninit_seen = None;
+            for prefix in this.prefixes(base, PrefixSet::Shallow) {
+                let mpi = match this.move_path_for_place(prefix) {
+                    Some(mpi) => mpi, None => continue,
+                };
+
+                if maybe_uninits.contains(mpi) {
+                    debug!("check_parent_of_field updating shortest_uninit_seen from {:?} to {:?}",
+                           shortest_uninit_seen, Some((prefix, mpi)));
+                    shortest_uninit_seen = Some((prefix, mpi));
+                } else {
+                    debug!("check_parent_of_field {:?} is definitely initialized", (prefix, mpi));
+                }
+            }
+
+            if let Some((prefix, mpi)) = shortest_uninit_seen {
+                this.report_use_of_moved_or_uninitialized(
+                    context,
+                    InitializationRequiringAction::PartialAssignment,
+                    (prefix, base, span),
+                    mpi,
+                );
+            }
+        }
+    }
 
     /// Check the permissions for the given place and read or write kind
     ///
@@ -1692,13 +1776,23 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         location: Location,
     ) -> bool {
         debug!(
-            "check_access_permissions({:?}, {:?}, {:?})",
+            "check_access_permissions({:?}, {:?}, is_local_mutation_allowed: {:?})",
             place, kind, is_local_mutation_allowed
         );
 
         let error_access;
         let the_place_err;
 
+        // rust-lang/rust#21232, #54986: during period where we reject
+        // partial initialization, do not complain about mutability
+        // errors except for actual mutation (as opposed to an attempt
+        // to do a partial initialization).
+        let previously_initialized = if let Some(local) = place.base_local() {
+            self.is_local_ever_initialized(local, flow_state).is_some()
+        } else {
+            true
+        };
+
         match kind {
             Reservation(WriteKind::MutableBorrow(borrow_kind @ BorrowKind::Unique))
             | Reservation(WriteKind::MutableBorrow(borrow_kind @ BorrowKind::Mut { .. }))
@@ -1791,14 +1885,33 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         }
 
         // at this point, we have set up the error reporting state.
-        self.report_mutability_error(
-            place,
-            span,
-            the_place_err,
-            error_access,
-            location,
-        );
-        return true;
+        if previously_initialized {
+            self.report_mutability_error(
+                place,
+                span,
+                the_place_err,
+                error_access,
+                location,
+            );
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    fn is_local_ever_initialized(&self,
+                                 local: Local,
+                                 flow_state: &Flows<'cx, 'gcx, 'tcx>)
+                                 -> Option<InitIndex>
+    {
+        let mpi = self.move_data.rev_lookup.find_local(local);
+        let ii = &self.move_data.init_path_map[mpi];
+        for &index in ii {
+            if flow_state.ever_inits.contains(index) {
+                return Some(index);
+            }
+        }
+        return None;
     }
 
     /// Adds the place into the used mutable variables set
@@ -1812,18 +1925,13 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 place: Place::Local(local),
                 is_local_mutation_allowed,
             } => {
-                if is_local_mutation_allowed != LocalMutationIsAllowed::Yes {
-                    // If the local may be initialized, and it is now currently being
-                    // mutated, then it is justified to be annotated with the `mut`
-                    // keyword, since the mutation may be a possible reassignment.
-                    let mpi = self.move_data.rev_lookup.find_local(*local);
-                    let ii = &self.move_data.init_path_map[mpi];
-                    for &index in ii {
-                        if flow_state.ever_inits.contains(index) {
-                            self.used_mut.insert(*local);
-                            break;
-                        }
-                    }
+                // If the local may have been initialized, and it is now currently being
+                // mutated, then it is justified to be annotated with the `mut`
+                // keyword, since the mutation may be a possible reassignment.
+                if is_local_mutation_allowed != LocalMutationIsAllowed::Yes &&
+                    self.is_local_ever_initialized(*local, flow_state).is_some()
+                {
+                    self.used_mut.insert(*local);
                 }
             }
             RootPlace {
@@ -1849,7 +1957,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         }
     }
 
-    /// Whether this value be written or borrowed mutably.
+    /// Whether this value can be written or borrowed mutably.
     /// Returns the root place if the place passed in is a projection.
     fn is_mutable<'d>(
         &self,
@@ -1927,14 +2035,14 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                             ty::RawPtr(tnm) => {
                                 match tnm.mutbl {
                                     // `*const` raw pointers are not mutable
-                                    hir::MutImmutable => return Err(place),
+                                    hir::MutImmutable => Err(place),
                                     // `*mut` raw pointers are always mutable, regardless of
                                     // context. The users have to check by themselves.
                                     hir::MutMutable => {
-                                        return Ok(RootPlace {
+                                        Ok(RootPlace {
                                             place,
                                             is_local_mutation_allowed,
-                                        });
+                                        })
                                     }
                                 }
                             }
diff --git a/src/test/run-pass/issues/issue-26996.rs b/src/test/run-pass/issues/issue-26996.rs
index 8d53b739da2..83445c6657e 100644
--- a/src/test/run-pass/issues/issue-26996.rs
+++ b/src/test/run-pass/issues/issue-26996.rs
@@ -9,6 +9,20 @@
 // except according to those terms.
 
 // run-pass
+
+// This test is bogus (i.e. should be compile-fail) during the period
+// where #54986 is implemented and #54987 is *not* implemented. For
+// now: just ignore it under nll
+//
+// ignore-compare-mode-nll
+
+// This test is checking that the write to `c.0` (which has been moved out of)
+// won't overwrite the state in `c2`.
+//
+// That's a fine thing to test when this code is accepted by the
+// compiler, and this code is being transcribed accordingly into
+// the ui test issue-21232-partial-init-and-use.rs
+
 fn main() {
     let mut c = (1, "".to_owned());
     match c {
diff --git a/src/test/run-pass/issues/issue-27021.rs b/src/test/run-pass/issues/issue-27021.rs
index addeebe9def..dbad8556aeb 100644
--- a/src/test/run-pass/issues/issue-27021.rs
+++ b/src/test/run-pass/issues/issue-27021.rs
@@ -9,6 +9,22 @@
 // except according to those terms.
 
 // run-pass
+
+// This test is bogus (i.e. should be compile-fail) during the period
+// where #54986 is implemented and #54987 is *not* implemented. For
+// now: just ignore it under nll
+//
+// ignore-compare-mode-nll
+
+// These are variants of issue-26996.rs. In all cases we are writing
+// into a record field that has been moved out of, and ensuring that
+// such a write won't overwrite the state of the thing it was moved
+// into.
+//
+// That's a fine thing to test when this code is accepted by the
+// compiler, and this code is being transcribed accordingly into
+// the ui test issue-21232-partial-init-and-use.rs
+
 fn main() {
     let mut c = (1, (1, "".to_owned()));
     match c {
diff --git a/src/test/run-pass/issues/issue-49298.rs b/src/test/run-pass/issues/issue-49298.rs
index 6463adc48a8..db3c9792f35 100644
--- a/src/test/run-pass/issues/issue-49298.rs
+++ b/src/test/run-pass/issues/issue-49298.rs
@@ -12,6 +12,22 @@
 #![feature(test)]
 #![allow(unused_mut)] // under NLL we get warning about `x` below: rust-lang/rust#54499
 
+// This test is bogus (i.e. should be compile-fail) during the period
+// where #54986 is implemented and #54987 is *not* implemented. For
+// now: just ignore it under nll
+//
+// ignore-compare-mode-nll
+
+// This test is checking that the space allocated for `x.1` does not
+// overlap with `y`. (The reason why such a thing happened at one
+// point was because `x.0: Void` and thus the whole type of `x` was
+// uninhabited, and so the compiler thought it was safe to use the
+// space of `x.1` to hold `y`.)
+//
+// That's a fine thing to test when this code is accepted by the
+// compiler, and this code is being transcribed accordingly into
+// the ui test issue-21232-partial-init-and-use.rs
+
 extern crate test;
 
 enum Void {}
diff --git a/src/test/ui/borrowck/assign_mutable_fields.nll.stderr b/src/test/ui/borrowck/assign_mutable_fields.nll.stderr
index 51470753472..59af97d9f13 100644
--- a/src/test/ui/borrowck/assign_mutable_fields.nll.stderr
+++ b/src/test/ui/borrowck/assign_mutable_fields.nll.stderr
@@ -1,9 +1,15 @@
-error[E0381]: use of possibly uninitialized variable: `x`
-  --> $DIR/assign_mutable_fields.rs:29:10
+error[E0381]: assign to part of possibly uninitialized variable: `x`
+  --> $DIR/assign_mutable_fields.rs:19:5
    |
-LL |     drop(x); //~ ERROR
-   |          ^ use of possibly uninitialized `x`
+LL |     x.0 = 1;
+   |     ^^^^^^^ use of possibly uninitialized `x`
 
-error: aborting due to previous error
+error[E0381]: assign to part of possibly uninitialized variable: `x`
+  --> $DIR/assign_mutable_fields.rs:27:5
+   |
+LL |     x.0 = 1;
+   |     ^^^^^^^ use of possibly uninitialized `x`
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0381`.
diff --git a/src/test/ui/borrowck/borrowck-field-sensitivity.nll.stderr b/src/test/ui/borrowck/borrowck-field-sensitivity.nll.stderr
index 6ac5fef1416..81d9e958e28 100644
--- a/src/test/ui/borrowck/borrowck-field-sensitivity.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-field-sensitivity.nll.stderr
@@ -1,4 +1,4 @@
-error[E0382]: use of moved value: `*x.b`
+error[E0382]: use of moved value: `x.b`
   --> $DIR/borrowck-field-sensitivity.rs:18:10
    |
 LL |     drop(x.b);
@@ -6,7 +6,7 @@ LL |     drop(x.b);
 LL |     drop(*x.b); //~ ERROR use of moved value: `*x.b`
    |          ^^^^ value used here after move
 
-error[E0382]: use of moved value: `*x.b`
+error[E0382]: use of moved value: `x.b`
   --> $DIR/borrowck-field-sensitivity.rs:24:10
    |
 LL |     let y = A { a: 3, .. x };
@@ -104,7 +104,25 @@ LL |     let _z = A { a: 4, .. x };  //~ ERROR use of moved value: `x.b`
    |
    = note: move occurs because `x.b` has type `std::boxed::Box<isize>`, which does not implement the `Copy` trait
 
-error: aborting due to 11 previous errors
+error[E0381]: assign to part of possibly uninitialized variable: `x`
+  --> $DIR/borrowck-field-sensitivity.rs:91:5
+   |
+LL |     x.a = 1;
+   |     ^^^^^^^ use of possibly uninitialized `x`
+
+error[E0381]: assign to part of possibly uninitialized variable: `x`
+  --> $DIR/borrowck-field-sensitivity.rs:97:5
+   |
+LL |     x.a = 1;
+   |     ^^^^^^^ use of possibly uninitialized `x`
+
+error[E0381]: assign to part of possibly uninitialized variable: `x`
+  --> $DIR/borrowck-field-sensitivity.rs:104:5
+   |
+LL |     x.b = box 1;
+   |     ^^^ use of possibly uninitialized `x`
+
+error: aborting due to 14 previous errors
 
-Some errors occurred: E0382, E0499, E0505.
-For more information about an error, try `rustc --explain E0382`.
+Some errors occurred: E0381, E0382, E0499, E0505.
+For more information about an error, try `rustc --explain E0381`.
diff --git a/src/test/ui/borrowck/borrowck-init-in-fru.ast.nll.stderr b/src/test/ui/borrowck/borrowck-init-in-fru.ast.nll.stderr
index cac9d890dae..e6025978444 100644
--- a/src/test/ui/borrowck/borrowck-init-in-fru.ast.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-init-in-fru.ast.nll.stderr
@@ -1,4 +1,4 @@
-error[E0381]: use of possibly uninitialized variable: `origin.y`
+error[E0381]: use of possibly uninitialized variable: `origin`
   --> $DIR/borrowck-init-in-fru.rs:22:5
    |
 LL |     origin = point {x: 10,.. origin};
diff --git a/src/test/ui/borrowck/borrowck-init-in-fru.mir.stderr b/src/test/ui/borrowck/borrowck-init-in-fru.mir.stderr
index cac9d890dae..e6025978444 100644
--- a/src/test/ui/borrowck/borrowck-init-in-fru.mir.stderr
+++ b/src/test/ui/borrowck/borrowck-init-in-fru.mir.stderr
@@ -1,4 +1,4 @@
-error[E0381]: use of possibly uninitialized variable: `origin.y`
+error[E0381]: use of possibly uninitialized variable: `origin`
   --> $DIR/borrowck-init-in-fru.rs:22:5
    |
 LL |     origin = point {x: 10,.. origin};
diff --git a/src/test/ui/borrowck/borrowck-uninit-field-access.ast.nll.stderr b/src/test/ui/borrowck/borrowck-uninit-field-access.ast.nll.stderr
index 9c7dbd7e778..97eb83d0f14 100644
--- a/src/test/ui/borrowck/borrowck-uninit-field-access.ast.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-uninit-field-access.ast.nll.stderr
@@ -1,10 +1,10 @@
-error[E0381]: use of possibly uninitialized variable: `a.x`
+error[E0381]: use of possibly uninitialized variable: `a`
   --> $DIR/borrowck-uninit-field-access.rs:34:13
    |
 LL |     let _ = a.x + 1; //[ast]~ ERROR use of possibly uninitialized variable: `a.x`
    |             ^^^ use of possibly uninitialized `a.x`
 
-error[E0382]: use of moved value: `line1.origin.x`
+error[E0382]: use of moved value: `line1.origin`
   --> $DIR/borrowck-uninit-field-access.rs:39:13
    |
 LL |     let _moved = line1.origin;
diff --git a/src/test/ui/borrowck/borrowck-uninit-field-access.mir.stderr b/src/test/ui/borrowck/borrowck-uninit-field-access.mir.stderr
index 9c7dbd7e778..97eb83d0f14 100644
--- a/src/test/ui/borrowck/borrowck-uninit-field-access.mir.stderr
+++ b/src/test/ui/borrowck/borrowck-uninit-field-access.mir.stderr
@@ -1,10 +1,10 @@
-error[E0381]: use of possibly uninitialized variable: `a.x`
+error[E0381]: use of possibly uninitialized variable: `a`
   --> $DIR/borrowck-uninit-field-access.rs:34:13
    |
 LL |     let _ = a.x + 1; //[ast]~ ERROR use of possibly uninitialized variable: `a.x`
    |             ^^^ use of possibly uninitialized `a.x`
 
-error[E0382]: use of moved value: `line1.origin.x`
+error[E0382]: use of moved value: `line1.origin`
   --> $DIR/borrowck-uninit-field-access.rs:39:13
    |
 LL |     let _moved = line1.origin;
diff --git a/src/test/ui/borrowck/borrowck-uninit-ref-chain.ast.nll.stderr b/src/test/ui/borrowck/borrowck-uninit-ref-chain.ast.nll.stderr
index bd9836e3174..e9caf7d9e1e 100644
--- a/src/test/ui/borrowck/borrowck-uninit-ref-chain.ast.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-uninit-ref-chain.ast.nll.stderr
@@ -1,33 +1,45 @@
-error[E0381]: borrow of possibly uninitialized variable: `**x`
+error[E0381]: borrow of possibly uninitialized variable: `x`
   --> $DIR/borrowck-uninit-ref-chain.rs:21:14
    |
 LL |     let _y = &**x; //[ast]~ ERROR use of possibly uninitialized variable: `**x` [E0381]
    |              ^^^^ use of possibly uninitialized `**x`
 
-error[E0381]: borrow of possibly uninitialized variable: `**x`
+error[E0381]: borrow of possibly uninitialized variable: `x`
   --> $DIR/borrowck-uninit-ref-chain.rs:25:14
    |
 LL |     let _y = &**x; //[ast]~ ERROR use of possibly uninitialized variable: `**x` [E0381]
    |              ^^^^ use of possibly uninitialized `**x`
 
-error[E0381]: borrow of possibly uninitialized variable: `**x`
+error[E0381]: borrow of possibly uninitialized variable: `x`
   --> $DIR/borrowck-uninit-ref-chain.rs:29:14
    |
 LL |     let _y = &**x; //[ast]~ ERROR use of possibly uninitialized variable: `**x` [E0381]
    |              ^^^^ use of possibly uninitialized `**x`
 
-error[E0381]: borrow of possibly uninitialized variable: `a.y`
-  --> $DIR/borrowck-uninit-ref-chain.rs:46:14
+error[E0381]: assign to part of possibly uninitialized variable: `a`
+  --> $DIR/borrowck-uninit-ref-chain.rs:34:5
    |
-LL |     let _b = &a.y; //[ast]~ ERROR use of possibly uninitialized variable: `a.y` [E0381]
-   |              ^^^^ use of possibly uninitialized `a.y`
+LL |     a.x = 0;       //[mir]~ ERROR assign to part of possibly uninitialized variable: `a` [E0381]
+   |     ^^^^^^^ use of possibly uninitialized `a`
 
-error[E0381]: borrow of possibly uninitialized variable: `**a.y`
-  --> $DIR/borrowck-uninit-ref-chain.rs:51:14
+error[E0381]: assign to part of possibly uninitialized variable: `a`
+  --> $DIR/borrowck-uninit-ref-chain.rs:39:5
    |
-LL |     let _b = &**a.y; //[ast]~ ERROR use of possibly uninitialized variable: `**a.y` [E0381]
-   |              ^^^^^^ use of possibly uninitialized `**a.y`
+LL |     a.x = &&0;       //[mir]~ ERROR assign to part of possibly uninitialized variable: `a` [E0381]
+   |     ^^^^^^^^^ use of possibly uninitialized `a`
 
-error: aborting due to 5 previous errors
+error[E0381]: assign to part of possibly uninitialized variable: `a`
+  --> $DIR/borrowck-uninit-ref-chain.rs:45:5
+   |
+LL |     a.x = 0;       //[mir]~ ERROR assign to part of possibly uninitialized variable: `a` [E0381]
+   |     ^^^^^^^ use of possibly uninitialized `a`
+
+error[E0381]: assign to part of possibly uninitialized variable: `a`
+  --> $DIR/borrowck-uninit-ref-chain.rs:50:5
+   |
+LL |     a.x = &&0;       //[mir]~ assign to part of possibly uninitialized variable: `a` [E0381]
+   |     ^^^^^^^^^ use of possibly uninitialized `a`
+
+error: aborting due to 7 previous errors
 
 For more information about this error, try `rustc --explain E0381`.
diff --git a/src/test/ui/borrowck/borrowck-uninit-ref-chain.mir.stderr b/src/test/ui/borrowck/borrowck-uninit-ref-chain.mir.stderr
index bd9836e3174..e9caf7d9e1e 100644
--- a/src/test/ui/borrowck/borrowck-uninit-ref-chain.mir.stderr
+++ b/src/test/ui/borrowck/borrowck-uninit-ref-chain.mir.stderr
@@ -1,33 +1,45 @@
-error[E0381]: borrow of possibly uninitialized variable: `**x`
+error[E0381]: borrow of possibly uninitialized variable: `x`
   --> $DIR/borrowck-uninit-ref-chain.rs:21:14
    |
 LL |     let _y = &**x; //[ast]~ ERROR use of possibly uninitialized variable: `**x` [E0381]
    |              ^^^^ use of possibly uninitialized `**x`
 
-error[E0381]: borrow of possibly uninitialized variable: `**x`
+error[E0381]: borrow of possibly uninitialized variable: `x`
   --> $DIR/borrowck-uninit-ref-chain.rs:25:14
    |
 LL |     let _y = &**x; //[ast]~ ERROR use of possibly uninitialized variable: `**x` [E0381]
    |              ^^^^ use of possibly uninitialized `**x`
 
-error[E0381]: borrow of possibly uninitialized variable: `**x`
+error[E0381]: borrow of possibly uninitialized variable: `x`
   --> $DIR/borrowck-uninit-ref-chain.rs:29:14
    |
 LL |     let _y = &**x; //[ast]~ ERROR use of possibly uninitialized variable: `**x` [E0381]
    |              ^^^^ use of possibly uninitialized `**x`
 
-error[E0381]: borrow of possibly uninitialized variable: `a.y`
-  --> $DIR/borrowck-uninit-ref-chain.rs:46:14
+error[E0381]: assign to part of possibly uninitialized variable: `a`
+  --> $DIR/borrowck-uninit-ref-chain.rs:34:5
    |
-LL |     let _b = &a.y; //[ast]~ ERROR use of possibly uninitialized variable: `a.y` [E0381]
-   |              ^^^^ use of possibly uninitialized `a.y`
+LL |     a.x = 0;       //[mir]~ ERROR assign to part of possibly uninitialized variable: `a` [E0381]
+   |     ^^^^^^^ use of possibly uninitialized `a`
 
-error[E0381]: borrow of possibly uninitialized variable: `**a.y`
-  --> $DIR/borrowck-uninit-ref-chain.rs:51:14
+error[E0381]: assign to part of possibly uninitialized variable: `a`
+  --> $DIR/borrowck-uninit-ref-chain.rs:39:5
    |
-LL |     let _b = &**a.y; //[ast]~ ERROR use of possibly uninitialized variable: `**a.y` [E0381]
-   |              ^^^^^^ use of possibly uninitialized `**a.y`
+LL |     a.x = &&0;       //[mir]~ ERROR assign to part of possibly uninitialized variable: `a` [E0381]
+   |     ^^^^^^^^^ use of possibly uninitialized `a`
 
-error: aborting due to 5 previous errors
+error[E0381]: assign to part of possibly uninitialized variable: `a`
+  --> $DIR/borrowck-uninit-ref-chain.rs:45:5
+   |
+LL |     a.x = 0;       //[mir]~ ERROR assign to part of possibly uninitialized variable: `a` [E0381]
+   |     ^^^^^^^ use of possibly uninitialized `a`
+
+error[E0381]: assign to part of possibly uninitialized variable: `a`
+  --> $DIR/borrowck-uninit-ref-chain.rs:50:5
+   |
+LL |     a.x = &&0;       //[mir]~ assign to part of possibly uninitialized variable: `a` [E0381]
+   |     ^^^^^^^^^ use of possibly uninitialized `a`
+
+error: aborting due to 7 previous errors
 
 For more information about this error, try `rustc --explain E0381`.
diff --git a/src/test/ui/borrowck/borrowck-uninit-ref-chain.rs b/src/test/ui/borrowck/borrowck-uninit-ref-chain.rs
index c52b1f0bf64..dc9d8405102 100644
--- a/src/test/ui/borrowck/borrowck-uninit-ref-chain.rs
+++ b/src/test/ui/borrowck/borrowck-uninit-ref-chain.rs
@@ -31,23 +31,23 @@ fn main() {
 
 
     let mut a: S<i32, i32>;
-    a.x = 0;
+    a.x = 0;       //[mir]~ ERROR assign to part of possibly uninitialized variable: `a` [E0381]
     let _b = &a.x; //[ast]~ ERROR use of possibly uninitialized variable: `a.x` [E0381]
-                   // (deliberately *not* an error under MIR-borrowck)
+
 
     let mut a: S<&&i32, &&i32>;
-    a.x = &&0;
+    a.x = &&0;       //[mir]~ ERROR assign to part of possibly uninitialized variable: `a` [E0381]
     let _b = &**a.x; //[ast]~ ERROR use of possibly uninitialized variable: `**a.x` [E0381]
-                     // (deliberately *not* an error under MIR-borrowck)
+
 
 
     let mut a: S<i32, i32>;
-    a.x = 0;
+    a.x = 0;       //[mir]~ ERROR assign to part of possibly uninitialized variable: `a` [E0381]
     let _b = &a.y; //[ast]~ ERROR use of possibly uninitialized variable: `a.y` [E0381]
-                   //[mir]~^ ERROR [E0381]
+
 
     let mut a: S<&&i32, &&i32>;
-    a.x = &&0;
+    a.x = &&0;       //[mir]~ assign to part of possibly uninitialized variable: `a` [E0381]
     let _b = &**a.y; //[ast]~ ERROR use of possibly uninitialized variable: `**a.y` [E0381]
-                     //[mir]~^ ERROR [E0381]
+
 }
diff --git a/src/test/ui/borrowck/borrowck-union-move-assign.nll.stderr b/src/test/ui/borrowck/borrowck-union-move-assign.nll.stderr
index 2a877050e4c..423a44514db 100644
--- a/src/test/ui/borrowck/borrowck-union-move-assign.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-union-move-assign.nll.stderr
@@ -1,4 +1,4 @@
-error[E0382]: use of moved value: `u.a`
+error[E0382]: use of moved value: `u`
   --> $DIR/borrowck-union-move-assign.rs:27:21
    |
 LL |             let a = u.a;
@@ -8,7 +8,7 @@ LL |             let a = u.a; //~ ERROR use of moved value: `u.a`
    |
    = note: move occurs because `u` has type `U`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `u.a`
+error[E0382]: use of moved value: `u`
   --> $DIR/borrowck-union-move-assign.rs:33:21
    |
 LL |             let a = u.a;
@@ -19,7 +19,7 @@ LL |             let a = u.a; // OK
    |
    = note: move occurs because `u` has type `U`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `u.a`
+error[E0382]: use of moved value: `u`
   --> $DIR/borrowck-union-move-assign.rs:39:21
    |
 LL |             let a = u.a;
diff --git a/src/test/ui/borrowck/borrowck-union-move.nll.stderr b/src/test/ui/borrowck/borrowck-union-move.nll.stderr
index 6fd6a1c82a1..8597117dbcc 100644
--- a/src/test/ui/borrowck/borrowck-union-move.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-union-move.nll.stderr
@@ -1,4 +1,4 @@
-error[E0382]: use of moved value: `u.n1`
+error[E0382]: use of moved value: `u`
   --> $DIR/borrowck-union-move.rs:36:21
    |
 LL |             let a = u.n1;
@@ -18,7 +18,7 @@ LL |             let a = u; //~ ERROR use of partially moved value: `u`
    |
    = note: move occurs because `u` has type `Unn`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `u.n2`
+error[E0382]: use of moved value: `u`
   --> $DIR/borrowck-union-move.rs:46:21
    |
 LL |             let a = u.n1;
@@ -28,7 +28,7 @@ LL |             let a = u.n2; //~ ERROR use of moved value: `u.n2`
    |
    = note: move occurs because `u` has type `Unn`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `u.n`
+error[E0382]: use of moved value: `u`
   --> $DIR/borrowck-union-move.rs:73:21
    |
 LL |             let a = u.n;
@@ -38,7 +38,7 @@ LL |             let a = u.n; //~ ERROR use of moved value: `u.n`
    |
    = note: move occurs because `u` has type `Ucn`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `u.c`
+error[E0382]: use of moved value: `u`
   --> $DIR/borrowck-union-move.rs:78:21
    |
 LL |             let a = u.n;
diff --git a/src/test/ui/borrowck/borrowck-union-uninitialized.nll.stderr b/src/test/ui/borrowck/borrowck-union-uninitialized.nll.stderr
index 49bc6d32636..94a7a4866e6 100644
--- a/src/test/ui/borrowck/borrowck-union-uninitialized.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-union-uninitialized.nll.stderr
@@ -1,9 +1,15 @@
-error[E0381]: use of possibly uninitialized variable: `u.a`
-  --> $DIR/borrowck-union-uninitialized.rs:26:18
+error[E0381]: assign to part of possibly uninitialized variable: `s`
+  --> $DIR/borrowck-union-uninitialized.rs:23:9
    |
-LL |         let ua = u.a; //~ ERROR use of possibly uninitialized variable: `u.a`
-   |                  ^^^ use of possibly uninitialized `u.a`
+LL |         s.a = 0;
+   |         ^^^^^^^ use of possibly uninitialized `s`
 
-error: aborting due to previous error
+error[E0381]: assign to part of possibly uninitialized variable: `u`
+  --> $DIR/borrowck-union-uninitialized.rs:24:9
+   |
+LL |         u.a = 0;
+   |         ^^^^^^^ use of possibly uninitialized `u`
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0381`.
diff --git a/src/test/ui/borrowck/borrowck-use-in-index-lvalue.ast.nll.stderr b/src/test/ui/borrowck/borrowck-use-in-index-lvalue.ast.nll.stderr
index d69693eb035..502978f25be 100644
--- a/src/test/ui/borrowck/borrowck-use-in-index-lvalue.ast.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-use-in-index-lvalue.ast.nll.stderr
@@ -1,10 +1,10 @@
-error[E0381]: use of possibly uninitialized variable: `*w`
+error[E0381]: use of possibly uninitialized variable: `w`
   --> $DIR/borrowck-use-in-index-lvalue.rs:16:5
    |
 LL |     w[5] = 0; //[ast]~ ERROR use of possibly uninitialized variable: `*w` [E0381]
    |     ^^^^ use of possibly uninitialized `*w`
 
-error[E0381]: use of possibly uninitialized variable: `*w`
+error[E0381]: use of possibly uninitialized variable: `w`
   --> $DIR/borrowck-use-in-index-lvalue.rs:20:5
    |
 LL |     w[5] = 0; //[ast]~ ERROR use of possibly uninitialized variable: `*w` [E0381]
diff --git a/src/test/ui/borrowck/borrowck-use-in-index-lvalue.mir.stderr b/src/test/ui/borrowck/borrowck-use-in-index-lvalue.mir.stderr
index d69693eb035..502978f25be 100644
--- a/src/test/ui/borrowck/borrowck-use-in-index-lvalue.mir.stderr
+++ b/src/test/ui/borrowck/borrowck-use-in-index-lvalue.mir.stderr
@@ -1,10 +1,10 @@
-error[E0381]: use of possibly uninitialized variable: `*w`
+error[E0381]: use of possibly uninitialized variable: `w`
   --> $DIR/borrowck-use-in-index-lvalue.rs:16:5
    |
 LL |     w[5] = 0; //[ast]~ ERROR use of possibly uninitialized variable: `*w` [E0381]
    |     ^^^^ use of possibly uninitialized `*w`
 
-error[E0381]: use of possibly uninitialized variable: `*w`
+error[E0381]: use of possibly uninitialized variable: `w`
   --> $DIR/borrowck-use-in-index-lvalue.rs:20:5
    |
 LL |     w[5] = 0; //[ast]~ ERROR use of possibly uninitialized variable: `*w` [E0381]
diff --git a/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.ast.nll.stderr b/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.ast.nll.stderr
index b96379db650..22e95ca3ad0 100644
--- a/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.ast.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.ast.nll.stderr
@@ -1,4 +1,4 @@
-error[E0381]: borrow of possibly uninitialized variable: `*x`
+error[E0381]: borrow of possibly uninitialized variable: `x`
   --> $DIR/borrowck-use-uninitialized-in-cast-trait.rs:22:13
    |
 LL |     let y = x as *const Foo; //[ast]~ ERROR use of possibly uninitialized variable: `*x`
diff --git a/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.mir.stderr b/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.mir.stderr
index b96379db650..22e95ca3ad0 100644
--- a/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.mir.stderr
+++ b/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast-trait.mir.stderr
@@ -1,4 +1,4 @@
-error[E0381]: borrow of possibly uninitialized variable: `*x`
+error[E0381]: borrow of possibly uninitialized variable: `x`
   --> $DIR/borrowck-use-uninitialized-in-cast-trait.rs:22:13
    |
 LL |     let y = x as *const Foo; //[ast]~ ERROR use of possibly uninitialized variable: `*x`
diff --git a/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast.ast.nll.stderr b/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast.ast.nll.stderr
index 6eddbd934d5..11897f50910 100644
--- a/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast.ast.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast.ast.nll.stderr
@@ -1,4 +1,4 @@
-error[E0381]: borrow of possibly uninitialized variable: `*x`
+error[E0381]: borrow of possibly uninitialized variable: `x`
   --> $DIR/borrowck-use-uninitialized-in-cast.rs:20:13
    |
 LL |     let y = x as *const i32; //[ast]~ ERROR use of possibly uninitialized variable: `*x` [E0381]
diff --git a/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast.mir.stderr b/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast.mir.stderr
index 6eddbd934d5..11897f50910 100644
--- a/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast.mir.stderr
+++ b/src/test/ui/borrowck/borrowck-use-uninitialized-in-cast.mir.stderr
@@ -1,4 +1,4 @@
-error[E0381]: borrow of possibly uninitialized variable: `*x`
+error[E0381]: borrow of possibly uninitialized variable: `x`
   --> $DIR/borrowck-use-uninitialized-in-cast.rs:20:13
    |
 LL |     let y = x as *const i32; //[ast]~ ERROR use of possibly uninitialized variable: `*x` [E0381]
diff --git a/src/test/ui/borrowck/issue-54499-field-mutation-marks-mut-as-used.ast.stderr b/src/test/ui/borrowck/issue-54499-field-mutation-marks-mut-as-used.ast.stderr
new file mode 100644
index 00000000000..d72cc20971b
--- /dev/null
+++ b/src/test/ui/borrowck/issue-54499-field-mutation-marks-mut-as-used.ast.stderr
@@ -0,0 +1,39 @@
+error[E0381]: use of possibly uninitialized variable: `t.0`
+  --> $DIR/issue-54499-field-mutation-marks-mut-as-used.rs:25:31
+   |
+LL |         println!("{:?} {:?}", t.0, t.1);
+   |                               ^^^ use of possibly uninitialized `t.0`
+
+error[E0381]: use of possibly uninitialized variable: `t.1`
+  --> $DIR/issue-54499-field-mutation-marks-mut-as-used.rs:25:36
+   |
+LL |         println!("{:?} {:?}", t.0, t.1);
+   |                                    ^^^ use of possibly uninitialized `t.1`
+
+error[E0381]: use of possibly uninitialized variable: `u.0`
+  --> $DIR/issue-54499-field-mutation-marks-mut-as-used.rs:35:31
+   |
+LL |         println!("{:?} {:?}", u.0, u.1);
+   |                               ^^^ use of possibly uninitialized `u.0`
+
+error[E0381]: use of possibly uninitialized variable: `u.1`
+  --> $DIR/issue-54499-field-mutation-marks-mut-as-used.rs:35:36
+   |
+LL |         println!("{:?} {:?}", u.0, u.1);
+   |                                    ^^^ use of possibly uninitialized `u.1`
+
+error[E0381]: use of possibly uninitialized variable: `v.x`
+  --> $DIR/issue-54499-field-mutation-marks-mut-as-used.rs:45:31
+   |
+LL |         println!("{:?} {:?}", v.x, v.y);
+   |                               ^^^ use of possibly uninitialized `v.x`
+
+error[E0381]: use of possibly uninitialized variable: `v.y`
+  --> $DIR/issue-54499-field-mutation-marks-mut-as-used.rs:45:36
+   |
+LL |         println!("{:?} {:?}", v.x, v.y);
+   |                                    ^^^ use of possibly uninitialized `v.y`
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0381`.
diff --git a/src/test/ui/borrowck/issue-54499-field-mutation-marks-mut-as-used.nll.stderr b/src/test/ui/borrowck/issue-54499-field-mutation-marks-mut-as-used.nll.stderr
new file mode 100644
index 00000000000..ebc6c7fca62
--- /dev/null
+++ b/src/test/ui/borrowck/issue-54499-field-mutation-marks-mut-as-used.nll.stderr
@@ -0,0 +1,21 @@
+error[E0381]: assign to part of possibly uninitialized variable: `t`
+  --> $DIR/issue-54499-field-mutation-marks-mut-as-used.rs:22:9
+   |
+LL |         t.0 = S(1);
+   |         ^^^^^^^^^^ use of possibly uninitialized `t`
+
+error[E0381]: assign to part of possibly uninitialized variable: `u`
+  --> $DIR/issue-54499-field-mutation-marks-mut-as-used.rs:32:9
+   |
+LL |         u.0 = S(1);
+   |         ^^^^^^^^^^ use of possibly uninitialized `u`
+
+error[E0381]: assign to part of possibly uninitialized variable: `v`
+  --> $DIR/issue-54499-field-mutation-marks-mut-as-used.rs:42:9
+   |
+LL |         v.x = S(1);
+   |         ^^^^^^^^^^ use of possibly uninitialized `v`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0381`.
diff --git a/src/test/ui/borrowck/issue-54499-field-mutation-marks-mut-as-used.rs b/src/test/ui/borrowck/issue-54499-field-mutation-marks-mut-as-used.rs
new file mode 100644
index 00000000000..4358e8e4402
--- /dev/null
+++ b/src/test/ui/borrowck/issue-54499-field-mutation-marks-mut-as-used.rs
@@ -0,0 +1,49 @@
+// revisions: ast nll
+
+// Since we are testing nll migration explicitly as a separate
+// revision, don't worry about the --compare-mode=nll on this test.
+
+// ignore-compare-mode-nll
+
+//[ast]compile-flags: -Z borrowck=ast
+//[nll]compile-flags: -Z borrowck=migrate -Z two-phase-borrows
+
+#![warn(unused)]
+#[derive(Debug)]
+struct S(i32);
+
+type Tuple = (S, i32);
+struct Tpair(S, i32);
+struct Spair { x: S, y: i32 }
+
+fn main() {
+    {
+        let mut t: Tuple;
+        t.0 = S(1);
+        //[nll]~^ ERROR assign to part of possibly uninitialized variable: `t` [E0381]
+        t.1 = 2;
+        println!("{:?} {:?}", t.0, t.1);
+        //[ast]~^ ERROR use of possibly uninitialized variable: `t.0` [E0381]
+        //[ast]~| ERROR use of possibly uninitialized variable: `t.1` [E0381]
+    }
+
+    {
+        let mut u: Tpair;
+        u.0 = S(1);
+        //[nll]~^ ERROR assign to part of possibly uninitialized variable: `u` [E0381]
+        u.1 = 2;
+        println!("{:?} {:?}", u.0, u.1);
+        //[ast]~^ ERROR use of possibly uninitialized variable: `u.0` [E0381]
+        //[ast]~| ERROR use of possibly uninitialized variable: `u.1` [E0381]
+    }
+
+    {
+        let mut v: Spair;
+        v.x = S(1);
+        //[nll]~^ ERROR assign to part of possibly uninitialized variable: `v` [E0381]
+        v.y = 2;
+        println!("{:?} {:?}", v.x, v.y);
+        //[ast]~^ ERROR use of possibly uninitialized variable: `v.x` [E0381]
+        //[ast]~| ERROR use of possibly uninitialized variable: `v.y` [E0381]
+    }
+}
diff --git a/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out-with-mut.ast.stderr b/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out-with-mut.ast.stderr
new file mode 100644
index 00000000000..e3b5341d2bc
--- /dev/null
+++ b/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out-with-mut.ast.stderr
@@ -0,0 +1,69 @@
+error[E0382]: use of moved value: `t.0`
+  --> $DIR/issue-54499-field-mutation-of-moved-out-with-mut.rs:25:31
+   |
+LL |         drop(t);
+   |              - value moved here
+...
+LL |         println!("{:?} {:?}", t.0, t.1);
+   |                               ^^^ value used here after move
+   |
+   = note: move occurs because `t` has type `(S, i32)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `t.1`
+  --> $DIR/issue-54499-field-mutation-of-moved-out-with-mut.rs:25:36
+   |
+LL |         drop(t);
+   |              - value moved here
+...
+LL |         println!("{:?} {:?}", t.0, t.1);
+   |                                    ^^^ value used here after move
+   |
+   = note: move occurs because `t` has type `(S, i32)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `u.0`
+  --> $DIR/issue-54499-field-mutation-of-moved-out-with-mut.rs:33:31
+   |
+LL |         drop(u);
+   |              - value moved here
+...
+LL |         println!("{:?} {:?}", u.0, u.1);
+   |                               ^^^ value used here after move
+   |
+   = note: move occurs because `u` has type `Tpair`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `u.1`
+  --> $DIR/issue-54499-field-mutation-of-moved-out-with-mut.rs:33:36
+   |
+LL |         drop(u);
+   |              - value moved here
+...
+LL |         println!("{:?} {:?}", u.0, u.1);
+   |                                    ^^^ value used here after move
+   |
+   = note: move occurs because `u` has type `Tpair`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `v.x`
+  --> $DIR/issue-54499-field-mutation-of-moved-out-with-mut.rs:41:31
+   |
+LL |         drop(v);
+   |              - value moved here
+...
+LL |         println!("{:?} {:?}", v.x, v.y);
+   |                               ^^^ value used here after move
+   |
+   = note: move occurs because `v` has type `Spair`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `v.y`
+  --> $DIR/issue-54499-field-mutation-of-moved-out-with-mut.rs:41:36
+   |
+LL |         drop(v);
+   |              - value moved here
+...
+LL |         println!("{:?} {:?}", v.x, v.y);
+   |                                    ^^^ value used here after move
+   |
+   = note: move occurs because `v` has type `Spair`, which does not implement the `Copy` trait
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out-with-mut.nll.stderr b/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out-with-mut.nll.stderr
new file mode 100644
index 00000000000..001ed59059c
--- /dev/null
+++ b/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out-with-mut.nll.stderr
@@ -0,0 +1,33 @@
+error[E0382]: assign to part of moved value: `t`
+  --> $DIR/issue-54499-field-mutation-of-moved-out-with-mut.rs:23:9
+   |
+LL |         drop(t);
+   |              - value moved here
+LL |         t.0 = S(1);
+   |         ^^^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `t` has type `(S, i32)`, which does not implement the `Copy` trait
+
+error[E0382]: assign to part of moved value: `u`
+  --> $DIR/issue-54499-field-mutation-of-moved-out-with-mut.rs:31:9
+   |
+LL |         drop(u);
+   |              - value moved here
+LL |         u.0 = S(1);
+   |         ^^^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `u` has type `Tpair`, which does not implement the `Copy` trait
+
+error[E0382]: assign to part of moved value: `v`
+  --> $DIR/issue-54499-field-mutation-of-moved-out-with-mut.rs:39:9
+   |
+LL |         drop(v);
+   |              - value moved here
+LL |         v.x = S(1);
+   |         ^^^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `v` has type `Spair`, which does not implement the `Copy` trait
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out-with-mut.rs b/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out-with-mut.rs
new file mode 100644
index 00000000000..b6339c4a3c7
--- /dev/null
+++ b/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out-with-mut.rs
@@ -0,0 +1,43 @@
+// revisions: ast nll
+
+// Since we are testing nll migration explicitly as a separate
+// revision, don't worry about the --compare-mode=nll on this test.
+
+// ignore-compare-mode-nll
+
+//[ast]compile-flags: -Z borrowck=ast
+//[nll]compile-flags: -Z borrowck=migrate -Z two-phase-borrows
+
+#![warn(unused)]
+#[derive(Debug)]
+struct S(i32);
+
+type Tuple = (S, i32);
+struct Tpair(S, i32);
+struct Spair { x: S, y: i32 }
+
+fn main() {
+    {
+        let mut t: Tuple = (S(0), 0);
+        drop(t);
+        t.0 = S(1);
+        t.1 = 2;
+        println!("{:?} {:?}", t.0, t.1);
+    }
+
+    {
+        let mut u: Tpair = Tpair(S(0), 0);
+        drop(u);
+        u.0 = S(1);
+        u.1 = 2;
+        println!("{:?} {:?}", u.0, u.1);
+    }
+
+    {
+        let mut v: Spair = Spair { x: S(0), y: 0 };
+        drop(v);
+        v.x = S(1);
+        v.y = 2;
+        println!("{:?} {:?}", v.x, v.y);
+    }
+}
diff --git a/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out.ast.stderr b/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out.ast.stderr
new file mode 100644
index 00000000000..565272af390
--- /dev/null
+++ b/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out.ast.stderr
@@ -0,0 +1,124 @@
+error[E0594]: cannot assign to field `t.0` of immutable binding
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:23:9
+   |
+LL |         let t: Tuple = (S(0), 0);
+   |             - help: make this binding mutable: `mut t`
+LL |         drop(t);
+LL |         t.0 = S(1);
+   |         ^^^^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `t.1` of immutable binding
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:27:9
+   |
+LL |         let t: Tuple = (S(0), 0);
+   |             - help: make this binding mutable: `mut t`
+...
+LL |         t.1 = 2;
+   |         ^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `u.0` of immutable binding
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:38:9
+   |
+LL |         let u: Tpair = Tpair(S(0), 0);
+   |             - help: make this binding mutable: `mut u`
+LL |         drop(u);
+LL |         u.0 = S(1);
+   |         ^^^^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `u.1` of immutable binding
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:42:9
+   |
+LL |         let u: Tpair = Tpair(S(0), 0);
+   |             - help: make this binding mutable: `mut u`
+...
+LL |         u.1 = 2;
+   |         ^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `v.x` of immutable binding
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:53:9
+   |
+LL |         let v: Spair = Spair { x: S(0), y: 0 };
+   |             - help: make this binding mutable: `mut v`
+LL |         drop(v);
+LL |         v.x = S(1);
+   |         ^^^^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `v.y` of immutable binding
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:57:9
+   |
+LL |         let v: Spair = Spair { x: S(0), y: 0 };
+   |             - help: make this binding mutable: `mut v`
+...
+LL |         v.y = 2;
+   |         ^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0382]: use of moved value: `t.0`
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:30:31
+   |
+LL |         drop(t);
+   |              - value moved here
+...
+LL |         println!("{:?} {:?}", t.0, t.1);
+   |                               ^^^ value used here after move
+   |
+   = note: move occurs because `t` has type `(S, i32)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `t.1`
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:30:36
+   |
+LL |         drop(t);
+   |              - value moved here
+...
+LL |         println!("{:?} {:?}", t.0, t.1);
+   |                                    ^^^ value used here after move
+   |
+   = note: move occurs because `t` has type `(S, i32)`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `u.0`
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:45:31
+   |
+LL |         drop(u);
+   |              - value moved here
+...
+LL |         println!("{:?} {:?}", u.0, u.1);
+   |                               ^^^ value used here after move
+   |
+   = note: move occurs because `u` has type `Tpair`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `u.1`
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:45:36
+   |
+LL |         drop(u);
+   |              - value moved here
+...
+LL |         println!("{:?} {:?}", u.0, u.1);
+   |                                    ^^^ value used here after move
+   |
+   = note: move occurs because `u` has type `Tpair`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `v.x`
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:60:31
+   |
+LL |         drop(v);
+   |              - value moved here
+...
+LL |         println!("{:?} {:?}", v.x, v.y);
+   |                               ^^^ value used here after move
+   |
+   = note: move occurs because `v` has type `Spair`, which does not implement the `Copy` trait
+
+error[E0382]: use of moved value: `v.y`
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:60:36
+   |
+LL |         drop(v);
+   |              - value moved here
+...
+LL |         println!("{:?} {:?}", v.x, v.y);
+   |                                    ^^^ value used here after move
+   |
+   = note: move occurs because `v` has type `Spair`, which does not implement the `Copy` trait
+
+error: aborting due to 12 previous errors
+
+Some errors occurred: E0382, E0594.
+For more information about an error, try `rustc --explain E0382`.
diff --git a/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out.nll.stderr b/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out.nll.stderr
new file mode 100644
index 00000000000..d35d0058027
--- /dev/null
+++ b/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out.nll.stderr
@@ -0,0 +1,88 @@
+error[E0594]: cannot assign to `t.0`, as `t` is not declared as mutable
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:23:9
+   |
+LL |         let t: Tuple = (S(0), 0);
+   |             - help: consider changing this to be mutable: `mut t`
+LL |         drop(t);
+LL |         t.0 = S(1);
+   |         ^^^^^^^^^^ cannot assign
+
+error[E0382]: assign to part of moved value: `t`
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:23:9
+   |
+LL |         drop(t);
+   |              - value moved here
+LL |         t.0 = S(1);
+   |         ^^^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `t` has type `(S, i32)`, which does not implement the `Copy` trait
+
+error[E0594]: cannot assign to `t.1`, as `t` is not declared as mutable
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:27:9
+   |
+LL |         let t: Tuple = (S(0), 0);
+   |             - help: consider changing this to be mutable: `mut t`
+...
+LL |         t.1 = 2;
+   |         ^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `u.0`, as `u` is not declared as mutable
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:38:9
+   |
+LL |         let u: Tpair = Tpair(S(0), 0);
+   |             - help: consider changing this to be mutable: `mut u`
+LL |         drop(u);
+LL |         u.0 = S(1);
+   |         ^^^^^^^^^^ cannot assign
+
+error[E0382]: assign to part of moved value: `u`
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:38:9
+   |
+LL |         drop(u);
+   |              - value moved here
+LL |         u.0 = S(1);
+   |         ^^^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `u` has type `Tpair`, which does not implement the `Copy` trait
+
+error[E0594]: cannot assign to `u.1`, as `u` is not declared as mutable
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:42:9
+   |
+LL |         let u: Tpair = Tpair(S(0), 0);
+   |             - help: consider changing this to be mutable: `mut u`
+...
+LL |         u.1 = 2;
+   |         ^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `v.x`, as `v` is not declared as mutable
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:53:9
+   |
+LL |         let v: Spair = Spair { x: S(0), y: 0 };
+   |             - help: consider changing this to be mutable: `mut v`
+LL |         drop(v);
+LL |         v.x = S(1);
+   |         ^^^^^^^^^^ cannot assign
+
+error[E0382]: assign to part of moved value: `v`
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:53:9
+   |
+LL |         drop(v);
+   |              - value moved here
+LL |         v.x = S(1);
+   |         ^^^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `v` has type `Spair`, which does not implement the `Copy` trait
+
+error[E0594]: cannot assign to `v.y`, as `v` is not declared as mutable
+  --> $DIR/issue-54499-field-mutation-of-moved-out.rs:57:9
+   |
+LL |         let v: Spair = Spair { x: S(0), y: 0 };
+   |             - help: consider changing this to be mutable: `mut v`
+...
+LL |         v.y = 2;
+   |         ^^^^^^^ cannot assign
+
+error: aborting due to 9 previous errors
+
+Some errors occurred: E0382, E0594.
+For more information about an error, try `rustc --explain E0382`.
diff --git a/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out.rs b/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out.rs
new file mode 100644
index 00000000000..b19dcd65a6c
--- /dev/null
+++ b/src/test/ui/borrowck/issue-54499-field-mutation-of-moved-out.rs
@@ -0,0 +1,64 @@
+// revisions: ast nll
+
+// Since we are testing nll migration explicitly as a separate
+// revision, don't worry about the --compare-mode=nll on this test.
+
+// ignore-compare-mode-nll
+
+//[ast]compile-flags: -Z borrowck=ast
+//[nll]compile-flags: -Z borrowck=migrate -Z two-phase-borrows
+
+#![warn(unused)]
+#[derive(Debug)]
+struct S(i32);
+
+type Tuple = (S, i32);
+struct Tpair(S, i32);
+struct Spair { x: S, y: i32 }
+
+fn main() {
+    {
+        let t: Tuple = (S(0), 0);
+        drop(t);
+        t.0 = S(1);
+        //[ast]~^ ERROR cannot assign to field `t.0` of immutable binding [E0594]
+        //[nll]~^^ ERROR assign to part of moved value: `t` [E0382]
+        //[nll]~| ERROR cannot assign to `t.0`, as `t` is not declared as mutable [E0594]
+        t.1 = 2;
+        //[ast]~^ ERROR cannot assign to field `t.1` of immutable binding [E0594]
+        //[nll]~^^ ERROR cannot assign to `t.1`, as `t` is not declared as mutable [E0594]
+        println!("{:?} {:?}", t.0, t.1);
+        //[ast]~^ ERROR use of moved value: `t.0` [E0382]
+        //[ast]~| ERROR use of moved value: `t.1` [E0382]
+    }
+
+    {
+        let u: Tpair = Tpair(S(0), 0);
+        drop(u);
+        u.0 = S(1);
+        //[ast]~^ ERROR cannot assign to field `u.0` of immutable binding [E0594]
+        //[nll]~^^ ERROR assign to part of moved value: `u` [E0382]
+        //[nll]~| ERROR cannot assign to `u.0`, as `u` is not declared as mutable [E0594]
+        u.1 = 2;
+        //[ast]~^ ERROR cannot assign to field `u.1` of immutable binding [E0594]
+        //[nll]~^^ ERROR cannot assign to `u.1`, as `u` is not declared as mutable [E0594]
+        println!("{:?} {:?}", u.0, u.1);
+        //[ast]~^ ERROR use of moved value: `u.0` [E0382]
+        //[ast]~| ERROR use of moved value: `u.1` [E0382]
+    }
+
+    {
+        let v: Spair = Spair { x: S(0), y: 0 };
+        drop(v);
+        v.x = S(1);
+        //[ast]~^ ERROR cannot assign to field `v.x` of immutable binding [E0594]
+        //[nll]~^^ ERROR assign to part of moved value: `v` [E0382]
+        //[nll]~| ERROR cannot assign to `v.x`, as `v` is not declared as mutable [E0594]
+        v.y = 2;
+        //[ast]~^ ERROR cannot assign to field `v.y` of immutable binding [E0594]
+        //[nll]~^^ ERROR cannot assign to `v.y`, as `v` is not declared as mutable [E0594]
+        println!("{:?} {:?}", v.x, v.y);
+        //[ast]~^ ERROR use of moved value: `v.x` [E0382]
+        //[ast]~| ERROR use of moved value: `v.y` [E0382]
+    }
+}
diff --git a/src/test/ui/borrowck/issue-54499-field-mutation-of-never-init.ast.stderr b/src/test/ui/borrowck/issue-54499-field-mutation-of-never-init.ast.stderr
new file mode 100644
index 00000000000..ea6b63b7a29
--- /dev/null
+++ b/src/test/ui/borrowck/issue-54499-field-mutation-of-never-init.ast.stderr
@@ -0,0 +1,91 @@
+error[E0594]: cannot assign to field `t.0` of immutable binding
+  --> $DIR/issue-54499-field-mutation-of-never-init.rs:22:9
+   |
+LL |         let t: Tuple;
+   |             - help: make this binding mutable: `mut t`
+LL |         t.0 = S(1);
+   |         ^^^^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `t.1` of immutable binding
+  --> $DIR/issue-54499-field-mutation-of-never-init.rs:25:9
+   |
+LL |         let t: Tuple;
+   |             - help: make this binding mutable: `mut t`
+...
+LL |         t.1 = 2;
+   |         ^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `u.0` of immutable binding
+  --> $DIR/issue-54499-field-mutation-of-never-init.rs:34:9
+   |
+LL |         let u: Tpair;
+   |             - help: make this binding mutable: `mut u`
+LL |         u.0 = S(1);
+   |         ^^^^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `u.1` of immutable binding
+  --> $DIR/issue-54499-field-mutation-of-never-init.rs:37:9
+   |
+LL |         let u: Tpair;
+   |             - help: make this binding mutable: `mut u`
+...
+LL |         u.1 = 2;
+   |         ^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `v.x` of immutable binding
+  --> $DIR/issue-54499-field-mutation-of-never-init.rs:46:9
+   |
+LL |         let v: Spair;
+   |             - help: make this binding mutable: `mut v`
+LL |         v.x = S(1);
+   |         ^^^^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0594]: cannot assign to field `v.y` of immutable binding
+  --> $DIR/issue-54499-field-mutation-of-never-init.rs:49:9
+   |
+LL |         let v: Spair;
+   |             - help: make this binding mutable: `mut v`
+...
+LL |         v.y = 2;
+   |         ^^^^^^^ cannot mutably borrow field of immutable binding
+
+error[E0381]: use of possibly uninitialized variable: `t.0`
+  --> $DIR/issue-54499-field-mutation-of-never-init.rs:27:31
+   |
+LL |         println!("{:?} {:?}", t.0, t.1);
+   |                               ^^^ use of possibly uninitialized `t.0`
+
+error[E0381]: use of possibly uninitialized variable: `t.1`
+  --> $DIR/issue-54499-field-mutation-of-never-init.rs:27:36
+   |
+LL |         println!("{:?} {:?}", t.0, t.1);
+   |                                    ^^^ use of possibly uninitialized `t.1`
+
+error[E0381]: use of possibly uninitialized variable: `u.0`
+  --> $DIR/issue-54499-field-mutation-of-never-init.rs:39:31
+   |
+LL |         println!("{:?} {:?}", u.0, u.1);
+   |                               ^^^ use of possibly uninitialized `u.0`
+
+error[E0381]: use of possibly uninitialized variable: `u.1`
+  --> $DIR/issue-54499-field-mutation-of-never-init.rs:39:36
+   |
+LL |         println!("{:?} {:?}", u.0, u.1);
+   |                                    ^^^ use of possibly uninitialized `u.1`
+
+error[E0381]: use of possibly uninitialized variable: `v.x`
+  --> $DIR/issue-54499-field-mutation-of-never-init.rs:51:31
+   |
+LL |         println!("{:?} {:?}", v.x, v.y);
+   |                               ^^^ use of possibly uninitialized `v.x`
+
+error[E0381]: use of possibly uninitialized variable: `v.y`
+  --> $DIR/issue-54499-field-mutation-of-never-init.rs:51:36
+   |
+LL |         println!("{:?} {:?}", v.x, v.y);
+   |                                    ^^^ use of possibly uninitialized `v.y`
+
+error: aborting due to 12 previous errors
+
+Some errors occurred: E0381, E0594.
+For more information about an error, try `rustc --explain E0381`.
diff --git a/src/test/ui/borrowck/issue-54499-field-mutation-of-never-init.nll.stderr b/src/test/ui/borrowck/issue-54499-field-mutation-of-never-init.nll.stderr
new file mode 100644
index 00000000000..3dc2b5b3b8f
--- /dev/null
+++ b/src/test/ui/borrowck/issue-54499-field-mutation-of-never-init.nll.stderr
@@ -0,0 +1,21 @@
+error[E0381]: assign to part of possibly uninitialized variable: `t`
+  --> $DIR/issue-54499-field-mutation-of-never-init.rs:22:9
+   |
+LL |         t.0 = S(1);
+   |         ^^^^^^^^^^ use of possibly uninitialized `t`
+
+error[E0381]: assign to part of possibly uninitialized variable: `u`
+  --> $DIR/issue-54499-field-mutation-of-never-init.rs:34:9
+   |
+LL |         u.0 = S(1);
+   |         ^^^^^^^^^^ use of possibly uninitialized `u`
+
+error[E0381]: assign to part of possibly uninitialized variable: `v`
+  --> $DIR/issue-54499-field-mutation-of-never-init.rs:46:9
+   |
+LL |         v.x = S(1);
+   |         ^^^^^^^^^^ use of possibly uninitialized `v`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0381`.
diff --git a/src/test/ui/borrowck/issue-54499-field-mutation-of-never-init.rs b/src/test/ui/borrowck/issue-54499-field-mutation-of-never-init.rs
new file mode 100644
index 00000000000..03eb9621ee2
--- /dev/null
+++ b/src/test/ui/borrowck/issue-54499-field-mutation-of-never-init.rs
@@ -0,0 +1,55 @@
+// revisions: ast nll
+
+// Since we are testing nll migration explicitly as a separate
+// revision, don't worry about the --compare-mode=nll on this test.
+
+// ignore-compare-mode-nll
+
+//[ast]compile-flags: -Z borrowck=ast
+//[nll]compile-flags: -Z borrowck=migrate -Z two-phase-borrows
+
+#![warn(unused)]
+#[derive(Debug)]
+struct S(i32);
+
+type Tuple = (S, i32);
+struct Tpair(S, i32);
+struct Spair { x: S, y: i32 }
+
+fn main() {
+    {
+        let t: Tuple;
+        t.0 = S(1);
+        //[ast]~^ ERROR cannot assign to field `t.0` of immutable binding [E0594]
+        //[nll]~^^ ERROR assign to part of possibly uninitialized variable: `t` [E0381]
+        t.1 = 2;
+        //[ast]~^ ERROR cannot assign to field `t.1` of immutable binding [E0594]
+        println!("{:?} {:?}", t.0, t.1);
+        //[ast]~^ ERROR use of possibly uninitialized variable: `t.0` [E0381]
+        //[ast]~| ERROR use of possibly uninitialized variable: `t.1` [E0381]
+    }
+
+    {
+        let u: Tpair;
+        u.0 = S(1);
+        //[ast]~^ ERROR cannot assign to field `u.0` of immutable binding [E0594]
+        //[nll]~^^ ERROR assign to part of possibly uninitialized variable: `u` [E0381]
+        u.1 = 2;
+        //[ast]~^ ERROR cannot assign to field `u.1` of immutable binding [E0594]
+        println!("{:?} {:?}", u.0, u.1);
+        //[ast]~^ ERROR use of possibly uninitialized variable: `u.0` [E0381]
+        //[ast]~| ERROR use of possibly uninitialized variable: `u.1` [E0381]
+    }
+
+    {
+        let v: Spair;
+        v.x = S(1);
+        //[ast]~^ ERROR cannot assign to field `v.x` of immutable binding [E0594]
+        //[nll]~^^ ERROR assign to part of possibly uninitialized variable: `v` [E0381]
+        v.y = 2;
+        //[ast]~^ ERROR cannot assign to field `v.y` of immutable binding [E0594]
+        println!("{:?} {:?}", v.x, v.y);
+        //[ast]~^ ERROR use of possibly uninitialized variable: `v.x` [E0381]
+        //[ast]~| ERROR use of possibly uninitialized variable: `v.y` [E0381]
+    }
+}
diff --git a/src/test/ui/borrowck/reassignment_immutable_fields.nll.stderr b/src/test/ui/borrowck/reassignment_immutable_fields.nll.stderr
index 97f3bf5b81f..1f0938dd3d2 100644
--- a/src/test/ui/borrowck/reassignment_immutable_fields.nll.stderr
+++ b/src/test/ui/borrowck/reassignment_immutable_fields.nll.stderr
@@ -1,44 +1,15 @@
-error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable
+error[E0381]: assign to part of possibly uninitialized variable: `x`
   --> $DIR/reassignment_immutable_fields.rs:17:5
    |
-LL |     let x: (u32, u32);
-   |         - help: consider changing this to be mutable: `mut x`
 LL |     x.0 = 1; //~ ERROR
-   |     ^^^^^^^ cannot assign
+   |     ^^^^^^^ use of possibly uninitialized `x`
 
-error[E0594]: cannot assign to `x.1`, as `x` is not declared as mutable
-  --> $DIR/reassignment_immutable_fields.rs:18:5
-   |
-LL |     let x: (u32, u32);
-   |         - help: consider changing this to be mutable: `mut x`
-LL |     x.0 = 1; //~ ERROR
-LL |     x.1 = 22; //~ ERROR
-   |     ^^^^^^^^ cannot assign
-
-error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable
+error[E0381]: assign to part of possibly uninitialized variable: `x`
   --> $DIR/reassignment_immutable_fields.rs:25:5
    |
-LL |     let x: (u32, u32);
-   |         - help: consider changing this to be mutable: `mut x`
-LL |     x.0 = 1; //~ ERROR
-   |     ^^^^^^^ cannot assign
-
-error[E0594]: cannot assign to `x.1`, as `x` is not declared as mutable
-  --> $DIR/reassignment_immutable_fields.rs:26:5
-   |
-LL |     let x: (u32, u32);
-   |         - help: consider changing this to be mutable: `mut x`
 LL |     x.0 = 1; //~ ERROR
-LL |     x.1 = 22; //~ ERROR
-   |     ^^^^^^^^ cannot assign
-
-error[E0381]: use of possibly uninitialized variable: `x`
-  --> $DIR/reassignment_immutable_fields.rs:27:10
-   |
-LL |     drop(x); //~ ERROR
-   |          ^ use of possibly uninitialized `x`
+   |     ^^^^^^^ use of possibly uninitialized `x`
 
-error: aborting due to 5 previous errors
+error: aborting due to 2 previous errors
 
-Some errors occurred: E0381, E0594.
-For more information about an error, try `rustc --explain E0381`.
+For more information about this error, try `rustc --explain E0381`.
diff --git a/src/test/ui/borrowck/reassignment_immutable_fields_overlapping.nll.stderr b/src/test/ui/borrowck/reassignment_immutable_fields_overlapping.nll.stderr
index c433d6e25c9..7da9dbfc088 100644
--- a/src/test/ui/borrowck/reassignment_immutable_fields_overlapping.nll.stderr
+++ b/src/test/ui/borrowck/reassignment_immutable_fields_overlapping.nll.stderr
@@ -1,20 +1,9 @@
-error[E0594]: cannot assign to `x.a`, as `x` is not declared as mutable
+error[E0381]: assign to part of possibly uninitialized variable: `x`
   --> $DIR/reassignment_immutable_fields_overlapping.rs:22:5
    |
-LL |     let x: Foo;
-   |         - help: consider changing this to be mutable: `mut x`
 LL |     x.a = 1;  //~ ERROR
-   |     ^^^^^^^ cannot assign
+   |     ^^^^^^^ use of possibly uninitialized `x`
 
-error[E0594]: cannot assign to `x.b`, as `x` is not declared as mutable
-  --> $DIR/reassignment_immutable_fields_overlapping.rs:23:5
-   |
-LL |     let x: Foo;
-   |         - help: consider changing this to be mutable: `mut x`
-LL |     x.a = 1;  //~ ERROR
-LL |     x.b = 22; //~ ERROR
-   |     ^^^^^^^^ cannot assign
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0594`.
+For more information about this error, try `rustc --explain E0381`.
diff --git a/src/test/ui/borrowck/reassignment_immutable_fields_twice.nll.stderr b/src/test/ui/borrowck/reassignment_immutable_fields_twice.nll.stderr
index 2160ae20c42..96677be7241 100644
--- a/src/test/ui/borrowck/reassignment_immutable_fields_twice.nll.stderr
+++ b/src/test/ui/borrowck/reassignment_immutable_fields_twice.nll.stderr
@@ -7,32 +7,13 @@ LL |     x = (22, 44);
 LL |     x.0 = 1; //~ ERROR
    |     ^^^^^^^ cannot assign
 
-error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable
+error[E0381]: assign to part of possibly uninitialized variable: `x`
   --> $DIR/reassignment_immutable_fields_twice.rs:22:5
    |
-LL |     let x: (u32, u32);
-   |         - help: consider changing this to be mutable: `mut x`
-LL |     x.0 = 1; //~ ERROR
-   |     ^^^^^^^ cannot assign
-
-error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable
-  --> $DIR/reassignment_immutable_fields_twice.rs:23:5
-   |
-LL |     let x: (u32, u32);
-   |         - help: consider changing this to be mutable: `mut x`
 LL |     x.0 = 1; //~ ERROR
-LL |     x.0 = 22; //~ ERROR
-   |     ^^^^^^^^ cannot assign
-
-error[E0594]: cannot assign to `x.1`, as `x` is not declared as mutable
-  --> $DIR/reassignment_immutable_fields_twice.rs:24:5
-   |
-LL |     let x: (u32, u32);
-   |         - help: consider changing this to be mutable: `mut x`
-...
-LL |     x.1 = 44; //~ ERROR
-   |     ^^^^^^^^ cannot assign
+   |     ^^^^^^^ use of possibly uninitialized `x`
 
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0594`.
+Some errors occurred: E0381, E0594.
+For more information about an error, try `rustc --explain E0381`.
diff --git a/src/test/ui/issues/issue-17385.nll.stderr b/src/test/ui/issues/issue-17385.nll.stderr
index c7b0b57000f..cdc5a047c07 100644
--- a/src/test/ui/issues/issue-17385.nll.stderr
+++ b/src/test/ui/issues/issue-17385.nll.stderr
@@ -1,4 +1,4 @@
-error[E0382]: use of moved value: `foo.0`
+error[E0382]: use of moved value: `foo`
   --> $DIR/issue-17385.rs:29:11
    |
 LL |     drop(foo);
diff --git a/src/test/ui/issues/issue-27282-move-match-input-into-guard.rs b/src/test/ui/issues/issue-27282-move-match-input-into-guard.rs
index 909c369354b..769e5474e00 100644
--- a/src/test/ui/issues/issue-27282-move-match-input-into-guard.rs
+++ b/src/test/ui/issues/issue-27282-move-match-input-into-guard.rs
@@ -26,7 +26,7 @@ fn main() {
         _ if { (|| { let bar = b; *bar = false; })();
                      false } => { },
         &mut true => { println!("You might think we should get here"); },
-        //~^ ERROR use of moved value: `*b` [E0382]
+        //~^ ERROR use of moved value: `b` [E0382]
         _ => panic!("surely we could never get here, since rustc warns it is unreachable."),
     }
 }
diff --git a/src/test/ui/issues/issue-27282-move-match-input-into-guard.stderr b/src/test/ui/issues/issue-27282-move-match-input-into-guard.stderr
index 0b783e37615..5f3b07bd1e8 100644
--- a/src/test/ui/issues/issue-27282-move-match-input-into-guard.stderr
+++ b/src/test/ui/issues/issue-27282-move-match-input-into-guard.stderr
@@ -1,4 +1,4 @@
-error[E0382]: use of moved value: `*b`
+error[E0382]: use of moved value: `b`
   --> $DIR/issue-27282-move-match-input-into-guard.rs:28:14
    |
 LL |         _ if { (|| { let bar = b; *bar = false; })();
diff --git a/src/test/ui/liveness/liveness-use-after-move.nll.stderr b/src/test/ui/liveness/liveness-use-after-move.nll.stderr
index 62d9a8b115a..463a64f4bec 100644
--- a/src/test/ui/liveness/liveness-use-after-move.nll.stderr
+++ b/src/test/ui/liveness/liveness-use-after-move.nll.stderr
@@ -1,4 +1,4 @@
-error[E0382]: borrow of moved value: `*x`
+error[E0382]: borrow of moved value: `x`
   --> $DIR/liveness-use-after-move.rs:16:20
    |
 LL |     let y = x;
diff --git a/src/test/ui/nll/issue-21232-partial-init-and-erroneous-use.rs b/src/test/ui/nll/issue-21232-partial-init-and-erroneous-use.rs
new file mode 100644
index 00000000000..abafd330573
--- /dev/null
+++ b/src/test/ui/nll/issue-21232-partial-init-and-erroneous-use.rs
@@ -0,0 +1,66 @@
+// This test enumerates various cases of interest where a ADT or tuple is
+// partially initialized and then used in some way that is wrong *even*
+// after rust-lang/rust#54987 is implemented.
+//
+// See rust-lang/rust#21232, rust-lang/rust#54986, and rust-lang/rust#54987.
+//
+// See issue-21232-partial-init-and-use.rs for cases of tests that are
+// meant to compile and run successfully once rust-lang/rust#54987 is
+// implemented.
+
+#![feature(nll)]
+
+struct D {
+    x: u32,
+    s: S,
+}
+
+struct S {
+    y: u32,
+    z: u32,
+}
+
+
+impl Drop for D {
+    fn drop(&mut self) { }
+}
+
+fn cannot_partially_init_adt_with_drop() {
+    let d: D;
+    d.x = 10;
+    //~^ ERROR assign of possibly uninitialized variable: `d` [E0381]
+}
+
+fn cannot_partially_init_mutable_adt_with_drop() {
+    let mut d: D;
+    d.x = 10;
+    //~^ ERROR assign of possibly uninitialized variable: `d` [E0381]
+}
+
+fn cannot_partially_reinit_adt_with_drop() {
+    let mut d = D { x: 0, s: S{ y: 0, z: 0 } };
+    drop(d);
+    d.x = 10;
+    //~^ ERROR assign of moved value: `d` [E0382]
+}
+
+fn cannot_partially_init_inner_adt_via_outer_with_drop() {
+    let d: D;
+    d.s.y = 20;
+    //~^ ERROR assign to part of possibly uninitialized variable: `d` [E0381]
+}
+
+fn cannot_partially_init_inner_adt_via_mutable_outer_with_drop() {
+    let mut d: D;
+    d.s.y = 20;
+    //~^ ERROR assign to part of possibly uninitialized variable: `d` [E0381]
+}
+
+fn cannot_partially_reinit_inner_adt_via_outer_with_drop() {
+    let mut d = D { x: 0, s: S{ y: 0, z: 0} };
+    drop(d);
+    d.s.y = 20;
+    //~^ ERROR assign to part of moved value: `d` [E0382]
+}
+
+fn main() { }
diff --git a/src/test/ui/nll/issue-21232-partial-init-and-erroneous-use.stderr b/src/test/ui/nll/issue-21232-partial-init-and-erroneous-use.stderr
new file mode 100644
index 00000000000..e29c44760a9
--- /dev/null
+++ b/src/test/ui/nll/issue-21232-partial-init-and-erroneous-use.stderr
@@ -0,0 +1,48 @@
+error[E0381]: assign of possibly uninitialized variable: `d`
+  --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:30:5
+   |
+LL |     d.x = 10;
+   |     ^^^^^^^^ use of possibly uninitialized `d`
+
+error[E0381]: assign of possibly uninitialized variable: `d`
+  --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:36:5
+   |
+LL |     d.x = 10;
+   |     ^^^^^^^^ use of possibly uninitialized `d`
+
+error[E0382]: assign of moved value: `d`
+  --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:43:5
+   |
+LL |     drop(d);
+   |          - value moved here
+LL |     d.x = 10;
+   |     ^^^^^^^^ value assigned here after move
+   |
+   = note: move occurs because `d` has type `D`, which does not implement the `Copy` trait
+
+error[E0381]: assign to part of possibly uninitialized variable: `d`
+  --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:49:5
+   |
+LL |     d.s.y = 20;
+   |     ^^^^^^^^^^ use of possibly uninitialized `d.s`
+
+error[E0381]: assign to part of possibly uninitialized variable: `d`
+  --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:55:5
+   |
+LL |     d.s.y = 20;
+   |     ^^^^^^^^^^ use of possibly uninitialized `d.s`
+
+error[E0382]: assign to part of moved value: `d`
+  --> $DIR/issue-21232-partial-init-and-erroneous-use.rs:62:5
+   |
+LL |     drop(d);
+   |          - value moved here
+LL |     d.s.y = 20;
+   |     ^^^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `d` has type `D`, which does not implement the `Copy` trait
+
+error: aborting due to 6 previous errors
+
+Some errors occurred: E0381, E0382.
+For more information about an error, try `rustc --explain E0381`.
diff --git a/src/test/ui/nll/issue-21232-partial-init-and-use.rs b/src/test/ui/nll/issue-21232-partial-init-and-use.rs
new file mode 100644
index 00000000000..e3ae4c0dcbe
--- /dev/null
+++ b/src/test/ui/nll/issue-21232-partial-init-and-use.rs
@@ -0,0 +1,311 @@
+// This test enumerates various cases of interest for partial
+// [re]initialization of ADTs and tuples.
+//
+// See rust-lang/rust#21232, rust-lang/rust#54986, and rust-lang/rust#54987.
+//
+// All of tests in this file are expected to change from being
+// rejected, at least under NLL (by rust-lang/rust#54986) to being
+// **accepted** when rust-lang/rust#54987 is implemented.
+// (That's why there are assertions in the code.)
+//
+// See issue-21232-partial-init-and-erroneous-use.rs for cases of
+// tests that are meant to continue failing to compile once
+// rust-lang/rust#54987 is implemented.
+
+#![feature(nll)]
+
+struct S<Y> {
+    x: u32,
+
+    // Note that even though `y` may implement `Drop`, under #54987 we
+    // will still allow partial initialization of `S` itself.
+    y: Y,
+}
+
+enum Void { }
+
+type B = Box<u32>;
+
+impl S<B> { fn new() -> Self { S { x: 0, y: Box::new(0) } } }
+
+fn borrow_s(s: &S<B>) { assert_eq!(s.x, 10); assert_eq!(*s.y, 20); }
+fn move_s(s: S<B>) {  assert_eq!(s.x, 10); assert_eq!(*s.y, 20); }
+fn borrow_field(x: &u32) { assert_eq!(*x, 10); }
+
+type T = (u32, B);
+type Tvoid = (u32, Void);
+
+fn borrow_t(t: &T) { assert_eq!(t.0, 10); assert_eq!(*t.1, 20); }
+fn move_t(t: T) {  assert_eq!(t.0, 10); assert_eq!(*t.1, 20); }
+
+struct Q<F> {
+    v: u32,
+    r: R<F>,
+}
+
+struct R<F> {
+    w: u32,
+    f: F,
+}
+
+impl<F> Q<F> { fn new(f: F) -> Self { Q { v: 0, r: R::new(f) } } }
+impl<F> R<F> { fn new(f: F) -> Self { R { w: 0, f } } }
+
+// Axes to cover:
+// * local/field: Is the structure in a local or a field
+// * fully/partial/void: Are we fully initializing it before using any part?
+//                       Is whole type empty due to a void component?
+// * init/reinit: First initialization, or did we previously inititalize and then move out?
+// * struct/tuple: Is this a struct or a (X, Y).
+//
+// As a shorthand for the cases above, adding a numeric summary to
+// each test's fn name to denote each point on each axis.
+//
+// E.g. 1000 = field fully init struct; 0211 = local void reinit tuple
+
+// It got pretty monotonous writing the same code over and over, and I
+// feared I would forget details. So I abstracted some desiderata into
+// macros. But I left the initialization code inline, because that's
+// where the errors for #54986 will be emited.
+
+macro_rules! use_fully {
+    (struct $s:expr) => { {
+        borrow_field(& $s.x );
+        borrow_s(& $s );
+        move_s( $s );
+    } };
+
+    (tuple $t:expr) => { {
+        borrow_field(& $t.0 );
+        borrow_t(& $t );
+        move_t( $t );
+    } }
+}
+
+macro_rules! use_part {
+    (struct $s:expr) => { {
+        borrow_field(& $s.x );
+        match $s { S { ref x, y: _ } => { borrow_field(x); } }
+    } };
+
+    (tuple $t:expr) => { {
+        borrow_field(& $t.0 );
+        match $t { (ref x, _) => { borrow_field(x); } }
+    } }
+}
+
+fn test_0000_local_fully_init_and_use_struct() {
+    let s: S<B>;
+    s.x = 10; s.y = Box::new(20);
+    //~^ ERROR assign to part of possibly uninitialized variable: `s` [E0381]
+    use_fully!(struct s);
+}
+
+fn test_0001_local_fully_init_and_use_tuple() {
+    let t: T;
+    t.0 = 10; t.1 = Box::new(20);
+    //~^ ERROR assign to part of possibly uninitialized variable: `t` [E0381]
+    use_fully!(tuple t);
+}
+
+fn test_0010_local_fully_reinit_and_use_struct() {
+    let mut s: S<B> = S::new(); drop(s);
+    s.x = 10; s.y = Box::new(20);
+    //~^ ERROR assign to part of moved value: `s` [E0382]
+    use_fully!(struct s);
+}
+
+fn test_0011_local_fully_reinit_and_use_tuple() {
+    let mut t: T = (0, Box::new(0)); drop(t);
+    t.0 = 10; t.1 = Box::new(20);
+    //~^ ERROR assign to part of moved value: `t` [E0382]
+    use_fully!(tuple t);
+}
+
+fn test_0100_local_partial_init_and_use_struct() {
+    let s: S<B>;
+    s.x = 10;
+    //~^ ERROR assign to part of possibly uninitialized variable: `s` [E0381]
+    use_part!(struct s);
+}
+
+fn test_0101_local_partial_init_and_use_tuple() {
+    let t: T;
+    t.0 = 10;
+    //~^ ERROR assign to part of possibly uninitialized variable: `t` [E0381]
+    use_part!(tuple t);
+}
+
+fn test_0110_local_partial_reinit_and_use_struct() {
+    let mut s: S<B> = S::new(); drop(s);
+    s.x = 10;
+    //~^ ERROR assign to part of moved value: `s` [E0382]
+    use_part!(struct s);
+}
+
+fn test_0111_local_partial_reinit_and_use_tuple() {
+    let mut t: T = (0, Box::new(0)); drop(t);
+    t.0 = 10;
+    //~^ ERROR assign to part of moved value: `t` [E0382]
+    use_part!(tuple t);
+}
+
+fn test_0200_local_void_init_and_use_struct() {
+    let s: S<Void>;
+    s.x = 10;
+    //~^ ERROR assign to part of possibly uninitialized variable: `s` [E0381]
+    use_part!(struct s);
+}
+
+fn test_0201_local_void_init_and_use_tuple() {
+    let t: Tvoid;
+    t.0 = 10;
+    //~^ ERROR assign to part of possibly uninitialized variable: `t` [E0381]
+    use_part!(tuple t);
+}
+
+// NOTE: uniform structure of tests here makes n21n (aka combining
+// Void with Reinit) an (even more) senseless case, as we cannot
+// safely create initial instance containing Void to move out of and
+// then reinitialize. While I was tempted to sidestep this via some
+// unsafe code (eek), lets just instead not encode such tests.
+
+// fn test_0210_local_void_reinit_and_use_struct() { unimplemented!() }
+// fn test_0211_local_void_reinit_and_use_tuple() { unimplemented!() }
+
+fn test_1000_field_fully_init_and_use_struct() {
+    let q: Q<S<B>>;
+    q.r.f.x = 10; q.r.f.y = Box::new(20);
+    //~^ ERROR assign to part of possibly uninitialized variable: `q` [E0381]
+    use_fully!(struct q.r.f);
+}
+
+fn test_1001_field_fully_init_and_use_tuple() {
+    let q: Q<T>;
+    q.r.f.0 = 10; q.r.f.1 = Box::new(20);
+    //~^ ERROR assign to part of possibly uninitialized variable: `q` [E0381]
+    use_fully!(tuple q.r.f);
+}
+
+fn test_1010_field_fully_reinit_and_use_struct() {
+    let mut q: Q<S<B>> = Q::new(S::new()); drop(q.r);
+    q.r.f.x = 10; q.r.f.y = Box::new(20);
+    //~^ ERROR assign to part of moved value: `q.r` [E0382]
+    use_fully!(struct q.r.f);
+}
+
+fn test_1011_field_fully_reinit_and_use_tuple() {
+    let mut q: Q<T> = Q::new((0, Box::new(0))); drop(q.r);
+    q.r.f.0 = 10; q.r.f.1 = Box::new(20);
+    //~^ ERROR assign to part of moved value: `q.r` [E0382]
+    use_fully!(tuple q.r.f);
+}
+
+fn test_1100_field_partial_init_and_use_struct() {
+    let q: Q<S<B>>;
+    q.r.f.x = 10;
+    //~^ ERROR assign to part of possibly uninitialized variable: `q` [E0381]
+    use_part!(struct q.r.f);
+}
+
+fn test_1101_field_partial_init_and_use_tuple() {
+    let q: Q<T>;
+    q.r.f.0 = 10;
+    //~^ ERROR assign to part of possibly uninitialized variable: `q` [E0381]
+    use_part!(tuple q.r.f);
+}
+
+fn test_1110_field_partial_reinit_and_use_struct() {
+    let mut q: Q<S<B>> = Q::new(S::new()); drop(q.r);
+    q.r.f.x = 10;
+    //~^ ERROR assign to part of moved value: `q.r` [E0382]
+    use_part!(struct q.r.f);
+}
+
+fn test_1111_field_partial_reinit_and_use_tuple() {
+    let mut q: Q<T> = Q::new((0, Box::new(0))); drop(q.r);
+    q.r.f.0 = 10;
+    //~^ ERROR assign to part of moved value: `q.r` [E0382]
+    use_part!(tuple q.r.f);
+}
+
+fn test_1200_field_void_init_and_use_struct() {
+    let mut q: Q<S<Void>>;
+    q.r.f.x = 10;
+    //~^ ERROR assign to part of possibly uninitialized variable: `q` [E0381]
+    use_part!(struct q.r.f);
+}
+
+fn test_1201_field_void_init_and_use_tuple() {
+    let mut q: Q<Tvoid>;
+    q.r.f.0 = 10;
+    //~^ ERROR assign to part of possibly uninitialized variable: `q` [E0381]
+    use_part!(tuple q.r.f);
+}
+
+// See NOTE abve.
+
+// fn test_1210_field_void_reinit_and_use_struct() { unimplemented!() }
+// fn test_1211_field_void_reinit_and_use_tuple() { unimplemented!() }
+
+// The below are some additional cases of interest that have been
+// transcribed from other bugs based on old erroneous codegen when we
+// encountered partial writes.
+
+fn issue_26996() {
+    let mut c = (1, "".to_owned());
+    match c {
+        c2 => {
+            c.0 = 2; //~ ERROR assign to part of moved value
+            assert_eq!(c2.0, 1);
+        }
+    }
+}
+
+fn issue_27021() {
+    let mut c = (1, (1, "".to_owned()));
+    match c {
+        c2 => {
+            (c.1).0 = 2; //~ ERROR assign to part of moved value
+            assert_eq!((c2.1).0, 1);
+        }
+    }
+
+    let mut c = (1, (1, (1, "".to_owned())));
+    match c.1 {
+        c2 => {
+            ((c.1).1).0 = 3; //~ ERROR assign to part of moved value
+            assert_eq!((c2.1).0, 1);
+        }
+    }
+}
+
+fn main() {
+    test_0000_local_fully_init_and_use_struct();
+    test_0001_local_fully_init_and_use_tuple();
+    test_0010_local_fully_reinit_and_use_struct();
+    test_0011_local_fully_reinit_and_use_tuple();
+    test_0100_local_partial_init_and_use_struct();
+    test_0101_local_partial_init_and_use_tuple();
+    test_0110_local_partial_reinit_and_use_struct();
+    test_0111_local_partial_reinit_and_use_tuple();
+    test_0200_local_void_init_and_use_struct();
+    test_0201_local_void_init_and_use_tuple();
+    // test_0210_local_void_reinit_and_use_struct();
+    // test_0211_local_void_reinit_and_use_tuple();
+    test_1000_field_fully_init_and_use_struct();
+    test_1001_field_fully_init_and_use_tuple();
+    test_1010_field_fully_reinit_and_use_struct();
+    test_1011_field_fully_reinit_and_use_tuple();
+    test_1100_field_partial_init_and_use_struct();
+    test_1101_field_partial_init_and_use_tuple();
+    test_1110_field_partial_reinit_and_use_struct();
+    test_1111_field_partial_reinit_and_use_tuple();
+    test_1200_field_void_init_and_use_struct();
+    test_1201_field_void_init_and_use_tuple();
+    // test_1210_field_void_reinit_and_use_struct();
+    // test_1211_field_void_reinit_and_use_tuple();
+
+    issue_26996();
+    issue_27021();
+}
diff --git a/src/test/ui/nll/issue-21232-partial-init-and-use.stderr b/src/test/ui/nll/issue-21232-partial-init-and-use.stderr
new file mode 100644
index 00000000000..aec7f676fce
--- /dev/null
+++ b/src/test/ui/nll/issue-21232-partial-init-and-use.stderr
@@ -0,0 +1,186 @@
+error[E0381]: assign to part of possibly uninitialized variable: `s`
+  --> $DIR/issue-21232-partial-init-and-use.rs:99:5
+   |
+LL |     s.x = 10; s.y = Box::new(20);
+   |     ^^^^^^^^ use of possibly uninitialized `s`
+
+error[E0381]: assign to part of possibly uninitialized variable: `t`
+  --> $DIR/issue-21232-partial-init-and-use.rs:106:5
+   |
+LL |     t.0 = 10; t.1 = Box::new(20);
+   |     ^^^^^^^^ use of possibly uninitialized `t`
+
+error[E0382]: assign to part of moved value: `s`
+  --> $DIR/issue-21232-partial-init-and-use.rs:113:5
+   |
+LL |     let mut s: S<B> = S::new(); drop(s);
+   |                                      - value moved here
+LL |     s.x = 10; s.y = Box::new(20);
+   |     ^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `s` has type `S<std::boxed::Box<u32>>`, which does not implement the `Copy` trait
+
+error[E0382]: assign to part of moved value: `t`
+  --> $DIR/issue-21232-partial-init-and-use.rs:120:5
+   |
+LL |     let mut t: T = (0, Box::new(0)); drop(t);
+   |                                           - value moved here
+LL |     t.0 = 10; t.1 = Box::new(20);
+   |     ^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `t` has type `(u32, std::boxed::Box<u32>)`, which does not implement the `Copy` trait
+
+error[E0381]: assign to part of possibly uninitialized variable: `s`
+  --> $DIR/issue-21232-partial-init-and-use.rs:127:5
+   |
+LL |     s.x = 10;
+   |     ^^^^^^^^ use of possibly uninitialized `s`
+
+error[E0381]: assign to part of possibly uninitialized variable: `t`
+  --> $DIR/issue-21232-partial-init-and-use.rs:134:5
+   |
+LL |     t.0 = 10;
+   |     ^^^^^^^^ use of possibly uninitialized `t`
+
+error[E0382]: assign to part of moved value: `s`
+  --> $DIR/issue-21232-partial-init-and-use.rs:141:5
+   |
+LL |     let mut s: S<B> = S::new(); drop(s);
+   |                                      - value moved here
+LL |     s.x = 10;
+   |     ^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `s` has type `S<std::boxed::Box<u32>>`, which does not implement the `Copy` trait
+
+error[E0382]: assign to part of moved value: `t`
+  --> $DIR/issue-21232-partial-init-and-use.rs:148:5
+   |
+LL |     let mut t: T = (0, Box::new(0)); drop(t);
+   |                                           - value moved here
+LL |     t.0 = 10;
+   |     ^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `t` has type `(u32, std::boxed::Box<u32>)`, which does not implement the `Copy` trait
+
+error[E0381]: assign to part of possibly uninitialized variable: `s`
+  --> $DIR/issue-21232-partial-init-and-use.rs:155:5
+   |
+LL |     s.x = 10;
+   |     ^^^^^^^^ use of possibly uninitialized `s`
+
+error[E0381]: assign to part of possibly uninitialized variable: `t`
+  --> $DIR/issue-21232-partial-init-and-use.rs:162:5
+   |
+LL |     t.0 = 10;
+   |     ^^^^^^^^ use of possibly uninitialized `t`
+
+error[E0381]: assign to part of possibly uninitialized variable: `q`
+  --> $DIR/issue-21232-partial-init-and-use.rs:178:5
+   |
+LL |     q.r.f.x = 10; q.r.f.y = Box::new(20);
+   |     ^^^^^^^^^^^^ use of possibly uninitialized `q.r.f`
+
+error[E0381]: assign to part of possibly uninitialized variable: `q`
+  --> $DIR/issue-21232-partial-init-and-use.rs:185:5
+   |
+LL |     q.r.f.0 = 10; q.r.f.1 = Box::new(20);
+   |     ^^^^^^^^^^^^ use of possibly uninitialized `q.r.f`
+
+error[E0382]: assign to part of moved value: `q.r`
+  --> $DIR/issue-21232-partial-init-and-use.rs:192:5
+   |
+LL |     let mut q: Q<S<B>> = Q::new(S::new()); drop(q.r);
+   |                                                 --- value moved here
+LL |     q.r.f.x = 10; q.r.f.y = Box::new(20);
+   |     ^^^^^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `q.r` has type `R<S<std::boxed::Box<u32>>>`, which does not implement the `Copy` trait
+
+error[E0382]: assign to part of moved value: `q.r`
+  --> $DIR/issue-21232-partial-init-and-use.rs:199:5
+   |
+LL |     let mut q: Q<T> = Q::new((0, Box::new(0))); drop(q.r);
+   |                                                      --- value moved here
+LL |     q.r.f.0 = 10; q.r.f.1 = Box::new(20);
+   |     ^^^^^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `q.r` has type `R<(u32, std::boxed::Box<u32>)>`, which does not implement the `Copy` trait
+
+error[E0381]: assign to part of possibly uninitialized variable: `q`
+  --> $DIR/issue-21232-partial-init-and-use.rs:206:5
+   |
+LL |     q.r.f.x = 10;
+   |     ^^^^^^^^^^^^ use of possibly uninitialized `q.r.f`
+
+error[E0381]: assign to part of possibly uninitialized variable: `q`
+  --> $DIR/issue-21232-partial-init-and-use.rs:213:5
+   |
+LL |     q.r.f.0 = 10;
+   |     ^^^^^^^^^^^^ use of possibly uninitialized `q.r.f`
+
+error[E0382]: assign to part of moved value: `q.r`
+  --> $DIR/issue-21232-partial-init-and-use.rs:220:5
+   |
+LL |     let mut q: Q<S<B>> = Q::new(S::new()); drop(q.r);
+   |                                                 --- value moved here
+LL |     q.r.f.x = 10;
+   |     ^^^^^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `q.r` has type `R<S<std::boxed::Box<u32>>>`, which does not implement the `Copy` trait
+
+error[E0382]: assign to part of moved value: `q.r`
+  --> $DIR/issue-21232-partial-init-and-use.rs:227:5
+   |
+LL |     let mut q: Q<T> = Q::new((0, Box::new(0))); drop(q.r);
+   |                                                      --- value moved here
+LL |     q.r.f.0 = 10;
+   |     ^^^^^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `q.r` has type `R<(u32, std::boxed::Box<u32>)>`, which does not implement the `Copy` trait
+
+error[E0381]: assign to part of possibly uninitialized variable: `q`
+  --> $DIR/issue-21232-partial-init-and-use.rs:234:5
+   |
+LL |     q.r.f.x = 10;
+   |     ^^^^^^^^^^^^ use of possibly uninitialized `q.r.f`
+
+error[E0381]: assign to part of possibly uninitialized variable: `q`
+  --> $DIR/issue-21232-partial-init-and-use.rs:241:5
+   |
+LL |     q.r.f.0 = 10;
+   |     ^^^^^^^^^^^^ use of possibly uninitialized `q.r.f`
+
+error[E0382]: assign to part of moved value: `c`
+  --> $DIR/issue-21232-partial-init-and-use.rs:259:13
+   |
+LL |         c2 => {
+   |         -- value moved here
+LL |             c.0 = 2; //~ ERROR assign to part of moved value
+   |             ^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `c` has type `(i32, std::string::String)`, which does not implement the `Copy` trait
+
+error[E0382]: assign to part of moved value: `c`
+  --> $DIR/issue-21232-partial-init-and-use.rs:269:13
+   |
+LL |         c2 => {
+   |         -- value moved here
+LL |             (c.1).0 = 2; //~ ERROR assign to part of moved value
+   |             ^^^^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `c` has type `(i32, (i32, std::string::String))`, which does not implement the `Copy` trait
+
+error[E0382]: assign to part of moved value: `c.1`
+  --> $DIR/issue-21232-partial-init-and-use.rs:277:13
+   |
+LL |         c2 => {
+   |         -- value moved here
+LL |             ((c.1).1).0 = 3; //~ ERROR assign to part of moved value
+   |             ^^^^^^^^^^^^^^^ value partially assigned here after move
+   |
+   = note: move occurs because `c.1` has type `(i32, (i32, std::string::String))`, which does not implement the `Copy` trait
+
+error: aborting due to 23 previous errors
+
+Some errors occurred: E0381, E0382.
+For more information about an error, try `rustc --explain E0381`.
diff --git a/src/test/ui/nll/issue-51512.rs b/src/test/ui/nll/issue-51512.rs
index 4543d2ba638..43debec69b5 100644
--- a/src/test/ui/nll/issue-51512.rs
+++ b/src/test/ui/nll/issue-51512.rs
@@ -15,5 +15,5 @@ fn main() {
     let range = 0..1;
     let r = range;
     let x = range.start;
-    //~^ ERROR use of moved value: `range.start` [E0382]
+    //~^ ERROR use of moved value: `range` [E0382]
 }
diff --git a/src/test/ui/nll/issue-51512.stderr b/src/test/ui/nll/issue-51512.stderr
index 102de43e5d7..49c419998a4 100644
--- a/src/test/ui/nll/issue-51512.stderr
+++ b/src/test/ui/nll/issue-51512.stderr
@@ -1,4 +1,4 @@
-error[E0382]: use of moved value: `range.start`
+error[E0382]: use of moved value: `range`
   --> $DIR/issue-51512.rs:17:13
    |
 LL |     let r = range;
diff --git a/src/test/ui/union/union-borrow-move-parent-sibling.nll.stderr b/src/test/ui/union/union-borrow-move-parent-sibling.nll.stderr
index 1ca2a56e757..a4f5e41b529 100644
--- a/src/test/ui/union/union-borrow-move-parent-sibling.nll.stderr
+++ b/src/test/ui/union/union-borrow-move-parent-sibling.nll.stderr
@@ -1,4 +1,4 @@
-error[E0382]: use of moved value: `u.y`
+error[E0382]: use of moved value: `u`
   --> $DIR/union-borrow-move-parent-sibling.rs:29:13
    |
 LL |     let a = u.x.0;
@@ -8,7 +8,7 @@ LL |     let a = u.y; //~ ERROR use of moved value: `u.y`
    |
    = note: move occurs because `u` has type `U`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `u.y`
+error[E0382]: use of moved value: `u`
   --> $DIR/union-borrow-move-parent-sibling.rs:41:13
    |
 LL |     let a = (u.x.0).0;
@@ -18,7 +18,7 @@ LL |     let a = u.y; //~ ERROR use of moved value: `u.y`
    |
    = note: move occurs because `u` has type `U`, which does not implement the `Copy` trait
 
-error[E0382]: use of moved value: `u.x`
+error[E0382]: use of moved value: `u`
   --> $DIR/union-borrow-move-parent-sibling.rs:53:13
    |
 LL |     let a = *u.y;
diff --git a/src/test/ui/use/use-after-move-self-based-on-type.nll.stderr b/src/test/ui/use/use-after-move-self-based-on-type.nll.stderr
new file mode 100644
index 00000000000..f13324955fa
--- /dev/null
+++ b/src/test/ui/use/use-after-move-self-based-on-type.nll.stderr
@@ -0,0 +1,13 @@
+error[E0382]: use of moved value: `self`
+  --> $DIR/use-after-move-self-based-on-type.rs:22:16
+   |
+LL |         self.bar();
+   |         ---- value moved here
+LL |         return self.x;  //~ ERROR use of moved value: `self.x`
+   |                ^^^^^^ value used here after move
+   |
+   = note: move occurs because `self` has type `S`, which does not implement the `Copy` trait
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/src/test/ui/use/use-after-move-self.nll.stderr b/src/test/ui/use/use-after-move-self.nll.stderr
index 3b19e96d12c..241dc32bb05 100644
--- a/src/test/ui/use/use-after-move-self.nll.stderr
+++ b/src/test/ui/use/use-after-move-self.nll.stderr
@@ -1,4 +1,4 @@
-error[E0382]: use of moved value: `*self.x`
+error[E0382]: use of moved value: `self`
   --> $DIR/use-after-move-self.rs:20:16
    |
 LL |         self.bar();
diff --git a/src/test/ui/walk-struct-literal-with.nll.stderr b/src/test/ui/walk-struct-literal-with.nll.stderr
index 668473882ab..e2c4a19debe 100644
--- a/src/test/ui/walk-struct-literal-with.nll.stderr
+++ b/src/test/ui/walk-struct-literal-with.nll.stderr
@@ -1,4 +1,4 @@
-error[E0382]: borrow of moved value: `start.test`
+error[E0382]: borrow of moved value: `start`
   --> $DIR/walk-struct-literal-with.rs:26:20
    |
 LL |     let end = Mine{other_val:1, ..start.make_string_bar()};