about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-01-13 17:08:24 +0000
committerbors <bors@rust-lang.org>2019-01-13 17:08:24 +0000
commit2cf736f76563f054aecd84207b39114c6fceb8ed (patch)
tree3700724bbe6c6438f2aef5999ed54084c9c17018
parent1c561d9b55aabc7df484f8faba24430421aee998 (diff)
parente2311b31c64e56c3d3218f6101c22ffe573bb687 (diff)
downloadrust-2cf736f76563f054aecd84207b39114c6fceb8ed.tar.gz
rust-2cf736f76563f054aecd84207b39114c6fceb8ed.zip
Auto merge of #57577 - Centril:rollup, r=Centril
Rollup of 4 pull requests

Successful merges:

 - #57004 (Make `TokenStream` less recursive.)
 - #57102 (NLL: Add union justifications to conflicting borrows.)
 - #57337 (rustc: Place wasm linker args first instead of last)
 - #57549 (Add #[must_use] message to Iterator and Future)

Failed merges:

r? @ghost
-rw-r--r--src/libcore/future/future.rs2
-rw-r--r--src/libcore/iter/iterator.rs2
-rw-r--r--src/libcore/iter/mod.rs44
-rw-r--r--src/librustc_borrowck/borrowck/check_loans.rs8
-rw-r--r--src/librustc_codegen_ssa/back/linker.rs122
-rw-r--r--src/librustc_mir/borrow_check/error_reporting.rs126
-rw-r--r--src/librustc_mir/util/borrowck_errors.rs43
-rw-r--r--src/libsyntax/attr/mod.rs8
-rw-r--r--src/libsyntax/ext/quote.rs2
-rw-r--r--src/libsyntax/ext/tt/transcribe.rs6
-rw-r--r--src/libsyntax/parse/lexer/tokentrees.rs8
-rw-r--r--src/libsyntax/parse/parser.rs2
-rw-r--r--src/libsyntax/tokenstream.rs450
-rw-r--r--src/libsyntax_ext/proc_macro_server.rs13
-rw-r--r--src/test/ui/borrowck/borrowck-borrow-from-owned-ptr.stderr2
-rw-r--r--src/test/ui/borrowck/borrowck-box-insensitivity.ast.stderr4
-rw-r--r--src/test/ui/borrowck/borrowck-box-insensitivity.rs4
-rw-r--r--src/test/ui/borrowck/borrowck-union-borrow.nll.stderr (renamed from src/test/ui/borrowck/borrowck-union-borrow.ast.nll.stderr)85
-rw-r--r--src/test/ui/borrowck/borrowck-union-borrow.rs38
-rw-r--r--src/test/ui/borrowck/borrowck-union-borrow.stderr (renamed from src/test/ui/borrowck/borrowck-union-borrow.ast.stderr)64
-rw-r--r--src/test/ui/issues/issue-17263.ast.stderr2
-rw-r--r--src/test/ui/issues/issue-45157.rs5
-rw-r--r--src/test/ui/issues/issue-45157.stderr12
-rw-r--r--src/test/ui/nll/issue-57100.rs69
-rw-r--r--src/test/ui/nll/issue-57100.stderr31
-rw-r--r--src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs2
-rw-r--r--src/test/ui/union/union-borrow-move-parent-sibling.nll.stderr24
-rw-r--r--src/test/ui/union/union-borrow-move-parent-sibling.stderr2
28 files changed, 601 insertions, 579 deletions
diff --git a/src/libcore/future/future.rs b/src/libcore/future/future.rs
index 0bc8a0fd26a..539b07fc21e 100644
--- a/src/libcore/future/future.rs
+++ b/src/libcore/future/future.rs
@@ -23,7 +23,7 @@ use task::{Poll, LocalWaker};
 ///
 /// When using a future, you generally won't call `poll` directly, but instead
 /// `await!` the value.
-#[must_use]
+#[must_use = "futures do nothing unless polled"]
 pub trait Future {
     /// The result of the `Future`.
     type Output;
diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs
index 1ea500858ed..640af748172 100644
--- a/src/libcore/iter/iterator.rs
+++ b/src/libcore/iter/iterator.rs
@@ -88,7 +88,7 @@ fn _assert_is_object_safe(_: &dyn Iterator<Item=()>) {}
     message="`{Self}` is not an iterator"
 )]
 #[doc(spotlight)]
-#[must_use]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 pub trait Iterator {
     /// The type of the elements being iterated over.
     #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs
index 03369d6c8f3..1ef5428a789 100644
--- a/src/libcore/iter/mod.rs
+++ b/src/libcore/iter/mod.rs
@@ -243,7 +243,7 @@
 //! using it. The compiler will warn us about this kind of behavior:
 //!
 //! ```text
-//! warning: unused result that must be used: iterator adaptors are lazy and
+//! warning: unused result that must be used: iterators are lazy and
 //! do nothing unless consumed
 //! ```
 //!
@@ -404,7 +404,7 @@ impl<R: Try> LoopState<R::Ok, R> {
 /// [`rev`]: trait.Iterator.html#method.rev
 /// [`Iterator`]: trait.Iterator.html
 #[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Rev<T> {
     iter: T
@@ -505,7 +505,7 @@ unsafe impl<I> TrustedLen for Rev<I>
 /// [`copied`]: trait.Iterator.html#method.copied
 /// [`Iterator`]: trait.Iterator.html
 #[unstable(feature = "iter_copied", issue = "57127")]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[derive(Clone, Debug)]
 pub struct Copied<I> {
     it: I,
@@ -605,7 +605,7 @@ unsafe impl<'a, I, T: 'a> TrustedLen for Copied<I>
 /// [`cloned`]: trait.Iterator.html#method.cloned
 /// [`Iterator`]: trait.Iterator.html
 #[stable(feature = "iter_cloned", since = "1.1.0")]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[derive(Clone, Debug)]
 pub struct Cloned<I> {
     it: I,
@@ -717,7 +717,7 @@ unsafe impl<'a, I, T: 'a> TrustedLen for Cloned<I>
 /// [`cycle`]: trait.Iterator.html#method.cycle
 /// [`Iterator`]: trait.Iterator.html
 #[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Cycle<I> {
     orig: I,
@@ -757,7 +757,7 @@ impl<I> FusedIterator for Cycle<I> where I: Clone + Iterator {}
 ///
 /// [`step_by`]: trait.Iterator.html#method.step_by
 /// [`Iterator`]: trait.Iterator.html
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "iterator_step_by", since = "1.28.0")]
 #[derive(Clone, Debug)]
 pub struct StepBy<I> {
@@ -849,7 +849,7 @@ impl<I> ExactSizeIterator for StepBy<I> where I: ExactSizeIterator {}
 /// [`chain`]: trait.Iterator.html#method.chain
 /// [`Iterator`]: trait.Iterator.html
 #[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Chain<A, B> {
     a: A,
@@ -1100,7 +1100,7 @@ unsafe impl<A, B> TrustedLen for Chain<A, B>
 /// [`zip`]: trait.Iterator.html#method.zip
 /// [`Iterator`]: trait.Iterator.html
 #[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Zip<A, B> {
     a: A,
@@ -1400,7 +1400,7 @@ unsafe impl<A, B> TrustedLen for Zip<A, B>
 ///     println!("{:?}", pair);
 /// }
 /// ```
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[derive(Clone)]
 pub struct Map<I, F> {
@@ -1511,7 +1511,7 @@ unsafe impl<B, I, F> TrustedRandomAccess for Map<I, F>
 ///
 /// [`filter`]: trait.Iterator.html#method.filter
 /// [`Iterator`]: trait.Iterator.html
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[derive(Clone)]
 pub struct Filter<I, P> {
@@ -1643,7 +1643,7 @@ impl<I: FusedIterator, P> FusedIterator for Filter<I, P>
 ///
 /// [`filter_map`]: trait.Iterator.html#method.filter_map
 /// [`Iterator`]: trait.Iterator.html
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[derive(Clone)]
 pub struct FilterMap<I, F> {
@@ -1754,7 +1754,7 @@ impl<B, I: FusedIterator, F> FusedIterator for FilterMap<I, F>
 /// [`enumerate`]: trait.Iterator.html#method.enumerate
 /// [`Iterator`]: trait.Iterator.html
 #[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Enumerate<I> {
     iter: I,
@@ -1915,7 +1915,7 @@ unsafe impl<I> TrustedLen for Enumerate<I>
 /// [`peekable`]: trait.Iterator.html#method.peekable
 /// [`Iterator`]: trait.Iterator.html
 #[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Peekable<I: Iterator> {
     iter: I,
@@ -2066,7 +2066,7 @@ impl<I: Iterator> Peekable<I> {
 ///
 /// [`skip_while`]: trait.Iterator.html#method.skip_while
 /// [`Iterator`]: trait.Iterator.html
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[derive(Clone)]
 pub struct SkipWhile<I, P> {
@@ -2149,7 +2149,7 @@ impl<I, P> FusedIterator for SkipWhile<I, P>
 ///
 /// [`take_while`]: trait.Iterator.html#method.take_while
 /// [`Iterator`]: trait.Iterator.html
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[derive(Clone)]
 pub struct TakeWhile<I, P> {
@@ -2233,7 +2233,7 @@ impl<I, P> FusedIterator for TakeWhile<I, P>
 /// [`skip`]: trait.Iterator.html#method.skip
 /// [`Iterator`]: trait.Iterator.html
 #[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Skip<I> {
     iter: I,
@@ -2371,7 +2371,7 @@ impl<I> FusedIterator for Skip<I> where I: FusedIterator {}
 /// [`take`]: trait.Iterator.html#method.take
 /// [`Iterator`]: trait.Iterator.html
 #[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Take<I> {
     iter: I,
@@ -2458,7 +2458,7 @@ unsafe impl<I: TrustedLen> TrustedLen for Take<I> {}
 ///
 /// [`scan`]: trait.Iterator.html#method.scan
 /// [`Iterator`]: trait.Iterator.html
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[derive(Clone)]
 pub struct Scan<I, St, F> {
@@ -2518,7 +2518,7 @@ impl<B, I, St, F> Iterator for Scan<I, St, F> where
 ///
 /// [`flat_map`]: trait.Iterator.html#method.flat_map
 /// [`Iterator`]: trait.Iterator.html
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct FlatMap<I, U: IntoIterator, F> {
     inner: FlattenCompat<Map<I, F>, <U as IntoIterator>::IntoIter>
@@ -2603,7 +2603,7 @@ impl<I, U, F> FusedIterator for FlatMap<I, U, F>
 ///
 /// [`flatten`]: trait.Iterator.html#method.flatten
 /// [`Iterator`]: trait.Iterator.html
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "iterator_flatten", since = "1.29.0")]
 pub struct Flatten<I: Iterator>
 where I::Item: IntoIterator {
@@ -2832,7 +2832,7 @@ impl<I, U> DoubleEndedIterator for FlattenCompat<I, U>
 /// [`fuse`]: trait.Iterator.html#method.fuse
 /// [`Iterator`]: trait.Iterator.html
 #[derive(Clone, Debug)]
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Fuse<I> {
     iter: I,
@@ -3056,7 +3056,7 @@ impl<I> ExactSizeIterator for Fuse<I> where I: ExactSizeIterator {
 ///
 /// [`inspect`]: trait.Iterator.html#method.inspect
 /// [`Iterator`]: trait.Iterator.html
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[derive(Clone)]
 pub struct Inspect<I, F> {
diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs
index 04085387c37..cafb29ed99a 100644
--- a/src/librustc_borrowck/borrowck/check_loans.rs
+++ b/src/librustc_borrowck/borrowck/check_loans.rs
@@ -557,12 +557,8 @@ impl<'a, 'tcx> CheckLoanCtxt<'a, 'tcx> {
                 if new_loan.loan_path.has_fork(&old_loan.loan_path) && common.is_some() {
                     let nl = self.bccx.loan_path_to_string(&common.unwrap());
                     let ol = nl.clone();
-                    let new_loan_msg = format!(" (via `{}`)",
-                                               self.bccx.loan_path_to_string(
-                                                   &new_loan.loan_path));
-                    let old_loan_msg = format!(" (via `{}`)",
-                                               self.bccx.loan_path_to_string(
-                                                   &old_loan.loan_path));
+                    let new_loan_msg = self.bccx.loan_path_to_string(&new_loan.loan_path);
+                    let old_loan_msg = self.bccx.loan_path_to_string(&old_loan.loan_path);
                     (nl, ol, new_loan_msg, old_loan_msg)
                 } else {
                     (self.bccx.loan_path_to_string(&new_loan.loan_path),
diff --git a/src/librustc_codegen_ssa/back/linker.rs b/src/librustc_codegen_ssa/back/linker.rs
index 270387572f1..06d4f940436 100644
--- a/src/librustc_codegen_ssa/back/linker.rs
+++ b/src/librustc_codegen_ssa/back/linker.rs
@@ -81,11 +81,7 @@ impl LinkerInfo {
             }
 
             LinkerFlavor::Lld(LldFlavor::Wasm) => {
-                Box::new(WasmLd {
-                    cmd,
-                    sess,
-                    info: self
-                }) as Box<dyn Linker>
+                Box::new(WasmLd::new(cmd, sess, self)) as Box<dyn Linker>
             }
         }
     }
@@ -876,6 +872,67 @@ pub struct WasmLd<'a> {
     info: &'a LinkerInfo,
 }
 
+impl<'a> WasmLd<'a> {
+    fn new(mut cmd: Command, sess: &'a Session, info: &'a LinkerInfo) -> WasmLd<'a> {
+        // There have been reports in the wild (rustwasm/wasm-bindgen#119) of
+        // using threads causing weird hangs and bugs. Disable it entirely as
+        // this isn't yet the bottleneck of compilation at all anyway.
+        cmd.arg("--no-threads");
+
+        // By default LLD only gives us one page of stack (64k) which is a
+        // little small. Default to a larger stack closer to other PC platforms
+        // (1MB) and users can always inject their own link-args to override this.
+        cmd.arg("-z").arg("stack-size=1048576");
+
+        // By default LLD's memory layout is:
+        //
+        // 1. First, a blank page
+        // 2. Next, all static data
+        // 3. Finally, the main stack (which grows down)
+        //
+        // This has the unfortunate consequence that on stack overflows you
+        // corrupt static data and can cause some exceedingly weird bugs. To
+        // help detect this a little sooner we instead request that the stack is
+        // placed before static data.
+        //
+        // This means that we'll generate slightly larger binaries as references
+        // to static data will take more bytes in the ULEB128 encoding, but
+        // stack overflow will be guaranteed to trap as it underflows instead of
+        // corrupting static data.
+        cmd.arg("--stack-first");
+
+        // FIXME we probably shouldn't pass this but instead pass an explicit
+        // whitelist of symbols we'll allow to be undefined. Unfortunately
+        // though we can't handle symbols like `log10` that LLVM injects at a
+        // super late date without actually parsing object files. For now let's
+        // stick to this and hopefully fix it before stabilization happens.
+        cmd.arg("--allow-undefined");
+
+        // For now we just never have an entry symbol
+        cmd.arg("--no-entry");
+
+        // Make the default table accessible
+        cmd.arg("--export-table");
+
+        // Rust code should never have warnings, and warnings are often
+        // indicative of bugs, let's prevent them.
+        cmd.arg("--fatal-warnings");
+
+        // The symbol visibility story is a bit in flux right now with LLD.
+        // It's... not entirely clear to me what's going on, but this looks to
+        // make everything work when `export_symbols` isn't otherwise called for
+        // things like executables.
+        cmd.arg("--export-dynamic");
+
+        // LLD only implements C++-like demangling, which doesn't match our own
+        // mangling scheme. Tell LLD to not demangle anything and leave it up to
+        // us to demangle these symbols later.
+        cmd.arg("--no-demangle");
+
+        WasmLd { cmd, sess, info }
+    }
+}
+
 impl<'a> Linker for WasmLd<'a> {
     fn link_dylib(&mut self, lib: &str) {
         self.cmd.arg("-l").arg(lib);
@@ -982,61 +1039,6 @@ impl<'a> Linker for WasmLd<'a> {
     }
 
     fn finalize(&mut self) -> Command {
-        // There have been reports in the wild (rustwasm/wasm-bindgen#119) of
-        // using threads causing weird hangs and bugs. Disable it entirely as
-        // this isn't yet the bottleneck of compilation at all anyway.
-        self.cmd.arg("--no-threads");
-
-        // By default LLD only gives us one page of stack (64k) which is a
-        // little small. Default to a larger stack closer to other PC platforms
-        // (1MB) and users can always inject their own link-args to override this.
-        self.cmd.arg("-z").arg("stack-size=1048576");
-
-        // By default LLD's memory layout is:
-        //
-        // 1. First, a blank page
-        // 2. Next, all static data
-        // 3. Finally, the main stack (which grows down)
-        //
-        // This has the unfortunate consequence that on stack overflows you
-        // corrupt static data and can cause some exceedingly weird bugs. To
-        // help detect this a little sooner we instead request that the stack is
-        // placed before static data.
-        //
-        // This means that we'll generate slightly larger binaries as references
-        // to static data will take more bytes in the ULEB128 encoding, but
-        // stack overflow will be guaranteed to trap as it underflows instead of
-        // corrupting static data.
-        self.cmd.arg("--stack-first");
-
-        // FIXME we probably shouldn't pass this but instead pass an explicit
-        // whitelist of symbols we'll allow to be undefined. Unfortunately
-        // though we can't handle symbols like `log10` that LLVM injects at a
-        // super late date without actually parsing object files. For now let's
-        // stick to this and hopefully fix it before stabilization happens.
-        self.cmd.arg("--allow-undefined");
-
-        // For now we just never have an entry symbol
-        self.cmd.arg("--no-entry");
-
-        // Make the default table accessible
-        self.cmd.arg("--export-table");
-
-        // Rust code should never have warnings, and warnings are often
-        // indicative of bugs, let's prevent them.
-        self.cmd.arg("--fatal-warnings");
-
-        // The symbol visibility story is a bit in flux right now with LLD.
-        // It's... not entirely clear to me what's going on, but this looks to
-        // make everything work when `export_symbols` isn't otherwise called for
-        // things like executables.
-        self.cmd.arg("--export-dynamic");
-
-        // LLD only implements C++-like demangling, which doesn't match our own
-        // mangling scheme. Tell LLD to not demangle anything and leave it up to
-        // us to demangle these symbols later.
-        self.cmd.arg("--no-demangle");
-
         ::std::mem::replace(&mut self.cmd, Command::new(""))
     }
 
diff --git a/src/librustc_mir/borrow_check/error_reporting.rs b/src/librustc_mir/borrow_check/error_reporting.rs
index 7f7d168ac03..233db12b030 100644
--- a/src/librustc_mir/borrow_check/error_reporting.rs
+++ b/src/librustc_mir/borrow_check/error_reporting.rs
@@ -327,10 +327,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             "closure"
         };
 
-        let desc_place = self.describe_place(place).unwrap_or_else(|| "_".to_owned());
-        let tcx = self.infcx.tcx;
-
-        let first_borrow_desc;
+        let (desc_place, msg_place, msg_borrow, union_type_name) =
+            self.describe_place_for_conflicting_borrow(place, &issued_borrow.borrowed_place);
 
         let explanation = self.explain_why_borrow_contains_point(context, issued_borrow, None);
         let second_borrow_desc = if explanation.is_explained() {
@@ -340,6 +338,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
         };
 
         // FIXME: supply non-"" `opt_via` when appropriate
+        let tcx = self.infcx.tcx;
+        let first_borrow_desc;
         let mut err = match (
             gen_borrow_kind,
             "immutable",
@@ -353,12 +353,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 tcx.cannot_reborrow_already_borrowed(
                     span,
                     &desc_place,
-                    "",
+                    &msg_place,
                     lft,
                     issued_span,
                     "it",
                     rgt,
-                    "",
+                    &msg_borrow,
                     None,
                     Origin::Mir,
                 )
@@ -368,12 +368,12 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 tcx.cannot_reborrow_already_borrowed(
                     span,
                     &desc_place,
-                    "",
+                    &msg_place,
                     lft,
                     issued_span,
                     "it",
                     rgt,
-                    "",
+                    &msg_borrow,
                     None,
                     Origin::Mir,
                 )
@@ -384,9 +384,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
                 tcx.cannot_mutably_borrow_multiply(
                     span,
                     &desc_place,
-                    "",
+                    &msg_place,
                     issued_span,
-                    "",
+                    &msg_borrow,
                     None,
                     Origin::Mir,
                 )
@@ -510,12 +510,118 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
             );
         }
 
+        if union_type_name != "" {
+            err.note(&format!(
+                "`{}` is a field of the union `{}`, so it overlaps the field `{}`",
+                msg_place, union_type_name, msg_borrow,
+            ));
+        }
+
         explanation
             .add_explanation_to_diagnostic(self.infcx.tcx, self.mir, &mut err, first_borrow_desc);
 
         err.buffer(&mut self.errors_buffer);
     }
 
+    /// Returns the description of the root place for a conflicting borrow and the full
+    /// descriptions of the places that caused the conflict.
+    ///
+    /// In the simplest case, where there are no unions involved, if a mutable borrow of `x` is
+    /// attempted while a shared borrow is live, then this function will return:
+    ///
+    ///     ("x", "", "")
+    ///
+    /// In the simple union case, if a mutable borrow of a union field `x.z` is attempted while
+    /// a shared borrow of another field `x.y`, then this function will return:
+    ///
+    ///     ("x", "x.z", "x.y")
+    ///
+    /// In the more complex union case, where the union is a field of a struct, then if a mutable
+    /// borrow of a union field in a struct `x.u.z` is attempted while a shared borrow of
+    /// another field `x.u.y`, then this function will return:
+    ///
+    ///     ("x.u", "x.u.z", "x.u.y")
+    ///
+    /// This is used when creating error messages like below:
+    ///
+    /// >  cannot borrow `a.u` (via `a.u.z.c`) as immutable because it is also borrowed as
+    /// >  mutable (via `a.u.s.b`) [E0502]
+    pub(super) fn describe_place_for_conflicting_borrow(
+        &self,
+        first_borrowed_place: &Place<'tcx>,
+        second_borrowed_place: &Place<'tcx>,
+    ) -> (String, String, String, String) {
+        // Define a small closure that we can use to check if the type of a place
+        // is a union.
+        let is_union = |place: &Place<'tcx>| -> bool {
+            place.ty(self.mir, self.infcx.tcx)
+                .to_ty(self.infcx.tcx)
+                .ty_adt_def()
+                .map(|adt| adt.is_union())
+                .unwrap_or(false)
+        };
+
+        // Start with an empty tuple, so we can use the functions on `Option` to reduce some
+        // code duplication (particularly around returning an empty description in the failure
+        // case).
+        Some(())
+            .filter(|_| {
+                // If we have a conflicting borrow of the same place, then we don't want to add
+                // an extraneous "via x.y" to our diagnostics, so filter out this case.
+                first_borrowed_place != second_borrowed_place
+            })
+            .and_then(|_| {
+                // We're going to want to traverse the first borrowed place to see if we can find
+                // field access to a union. If we find that, then we will keep the place of the
+                // union being accessed and the field that was being accessed so we can check the
+                // second borrowed place for the same union and a access to a different field.
+                let mut current = first_borrowed_place;
+                while let Place::Projection(box PlaceProjection { base, elem }) = current {
+                    match elem {
+                        ProjectionElem::Field(field, _) if is_union(base) => {
+                            return Some((base, field));
+                        },
+                        _ => current = base,
+                    }
+                }
+                None
+            })
+            .and_then(|(target_base, target_field)| {
+                // With the place of a union and a field access into it, we traverse the second
+                // borrowed place and look for a access to a different field of the same union.
+                let mut current = second_borrowed_place;
+                while let Place::Projection(box PlaceProjection { base, elem }) = current {
+                    match elem {
+                        ProjectionElem::Field(field, _) if {
+                            is_union(base) && field != target_field && base == target_base
+                        } => {
+                            let desc_base = self.describe_place(base)
+                                .unwrap_or_else(|| "_".to_owned());
+                            let desc_first = self.describe_place(first_borrowed_place)
+                                .unwrap_or_else(|| "_".to_owned());
+                            let desc_second = self.describe_place(second_borrowed_place)
+                                .unwrap_or_else(|| "_".to_owned());
+
+                            // Also compute the name of the union type, eg. `Foo` so we
+                            // can add a helpful note with it.
+                            let ty = base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx);
+
+                            return Some((desc_base, desc_first, desc_second, ty.to_string()));
+                        },
+                        _ => current = base,
+                    }
+                }
+                None
+            })
+            .unwrap_or_else(|| {
+                // If we didn't find a field access into a union, or both places match, then
+                // only return the description of the first place.
+                let desc_place = self.describe_place(first_borrowed_place)
+                    .unwrap_or_else(|| "_".to_owned());
+                (desc_place, "".to_string(), "".to_string(), "".to_string())
+            })
+    }
+
     /// Reports StorageDeadOrDrop of `place` conflicts with `borrow`.
     ///
     /// This means that some data referenced by `borrow` needs to live
diff --git a/src/librustc_mir/util/borrowck_errors.rs b/src/librustc_mir/util/borrowck_errors.rs
index 5e55bb4a9b6..7ad73aaa3f9 100644
--- a/src/librustc_mir/util/borrowck_errors.rs
+++ b/src/librustc_mir/util/borrowck_errors.rs
@@ -138,13 +138,15 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
         old_load_end_span: Option<Span>,
         o: Origin,
     ) -> DiagnosticBuilder<'cx> {
+        let via = |msg: &str|
+            if msg.is_empty() { msg.to_string() } else { format!(" (via `{}`)", msg) };
         let mut err = struct_span_err!(
             self,
             new_loan_span,
             E0499,
             "cannot borrow `{}`{} as mutable more than once at a time{OGN}",
             desc,
-            opt_via,
+            via(opt_via),
             OGN = o
         );
         if old_loan_span == new_loan_span {
@@ -164,11 +166,11 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
         } else {
             err.span_label(
                 old_loan_span,
-                format!("first mutable borrow occurs here{}", old_opt_via),
+                format!("first mutable borrow occurs here{}", via(old_opt_via)),
             );
             err.span_label(
                 new_loan_span,
-                format!("second mutable borrow occurs here{}", opt_via),
+                format!("second mutable borrow occurs here{}", via(opt_via)),
             );
             if let Some(old_load_end_span) = old_load_end_span {
                 err.span_label(old_load_end_span, "first borrow ends here");
@@ -292,27 +294,46 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {
         old_load_end_span: Option<Span>,
         o: Origin,
     ) -> DiagnosticBuilder<'cx> {
+        let via = |msg: &str|
+            if msg.is_empty() { msg.to_string() } else { format!(" (via `{}`)", msg) };
         let mut err = struct_span_err!(
             self,
             span,
             E0502,
-            "cannot borrow `{}`{} as {} because {} is also borrowed as {}{}{OGN}",
+            "cannot borrow `{}`{} as {} because {} is also borrowed \
+             as {}{}{OGN}",
             desc_new,
-            msg_new,
+            via(msg_new),
             kind_new,
             noun_old,
             kind_old,
-            msg_old,
+            via(msg_old),
             OGN = o
         );
-        err.span_label(span, format!("{} borrow occurs here{}", kind_new, msg_new));
-        err.span_label(
-            old_span,
-            format!("{} borrow occurs here{}", kind_old, msg_old),
-        );
+
+        if msg_new == "" {
+            // If `msg_new` is empty, then this isn't a borrow of a union field.
+            err.span_label(span, format!("{} borrow occurs here", kind_new));
+            err.span_label(old_span, format!("{} borrow occurs here", kind_old));
+        } else {
+            // If `msg_new` isn't empty, then this a borrow of a union field.
+            err.span_label(
+                span,
+                format!(
+                    "{} borrow of `{}` -- which overlaps with `{}` -- occurs here",
+                    kind_new, msg_new, msg_old,
+                )
+            );
+            err.span_label(
+                old_span,
+                format!("{} borrow occurs here{}", kind_old, via(msg_old)),
+            );
+        }
+
         if let Some(old_load_end_span) = old_load_end_span {
             err.span_label(old_load_end_span, format!("{} borrow ends here", kind_old));
         }
+
         self.cancel_if_wrong_origin(err, o)
     }
 
diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs
index a309775a1a4..d03563f8891 100644
--- a/src/libsyntax/attr/mod.rs
+++ b/src/libsyntax/attr/mod.rs
@@ -472,7 +472,7 @@ impl MetaItem {
                                          Token::from_ast_ident(segment.ident)).into());
             last_pos = segment.ident.span.hi();
         }
-        idents.push(self.node.tokens(self.span));
+        self.node.tokens(self.span).append_to_tree_and_joint_vec(&mut idents);
         TokenStream::new(idents)
     }
 
@@ -529,7 +529,9 @@ impl MetaItemKind {
         match *self {
             MetaItemKind::Word => TokenStream::empty(),
             MetaItemKind::NameValue(ref lit) => {
-                TokenStream::new(vec![TokenTree::Token(span, Token::Eq).into(), lit.tokens()])
+                let mut vec = vec![TokenTree::Token(span, Token::Eq).into()];
+                lit.tokens().append_to_tree_and_joint_vec(&mut vec);
+                TokenStream::new(vec)
             }
             MetaItemKind::List(ref list) => {
                 let mut tokens = Vec::new();
@@ -537,7 +539,7 @@ impl MetaItemKind {
                     if i > 0 {
                         tokens.push(TokenTree::Token(span, Token::Comma).into());
                     }
-                    tokens.push(item.node.tokens());
+                    item.node.tokens().append_to_tree_and_joint_vec(&mut tokens);
                 }
                 TokenTree::Delimited(
                     DelimSpan::from_single(span),
diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs
index ad8668aca70..c3124144009 100644
--- a/src/libsyntax/ext/quote.rs
+++ b/src/libsyntax/ext/quote.rs
@@ -233,7 +233,7 @@ pub mod rt {
                     self.span, token::Token::from_ast_ident(segment.ident)
                 ).into());
             }
-            inner.push(self.tokens.clone());
+            self.tokens.clone().append_to_tree_and_joint_vec(&mut inner);
 
             let delim_span = DelimSpan::from_single(self.span);
             r.push(TokenTree::Delimited(
diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs
index 31d87508c6b..0ef2d3b749d 100644
--- a/src/libsyntax/ext/tt/transcribe.rs
+++ b/src/libsyntax/ext/tt/transcribe.rs
@@ -7,7 +7,7 @@ use fold::noop_fold_tt;
 use parse::token::{self, Token, NtTT};
 use smallvec::SmallVec;
 use syntax_pos::DUMMY_SP;
-use tokenstream::{TokenStream, TokenTree, DelimSpan};
+use tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
@@ -63,7 +63,7 @@ pub fn transcribe(cx: &ExtCtxt,
     let mut stack: SmallVec<[Frame; 1]> = smallvec![Frame::new(src)];
     let interpolations = interp.unwrap_or_else(FxHashMap::default); /* just a convenience */
     let mut repeats = Vec::new();
-    let mut result: Vec<TokenStream> = Vec::new();
+    let mut result: Vec<TreeAndJoint> = Vec::new();
     let mut result_stack = Vec::new();
 
     loop {
@@ -78,7 +78,7 @@ pub fn transcribe(cx: &ExtCtxt,
                     if let Some(sep) = sep.clone() {
                         // repeat same span, I guess
                         let prev_span = match result.last() {
-                            Some(stream) => stream.trees().next().unwrap().span(),
+                            Some((tt, _)) => tt.span(),
                             None => DUMMY_SP,
                         };
                         result.push(TokenTree::Token(prev_span, sep).into());
diff --git a/src/libsyntax/parse/lexer/tokentrees.rs b/src/libsyntax/parse/lexer/tokentrees.rs
index 6c4e9e1c940..d219f29f06c 100644
--- a/src/libsyntax/parse/lexer/tokentrees.rs
+++ b/src/libsyntax/parse/lexer/tokentrees.rs
@@ -1,7 +1,7 @@
 use print::pprust::token_to_string;
 use parse::lexer::StringReader;
 use parse::{token, PResult};
-use tokenstream::{DelimSpan, IsJoint::*, TokenStream, TokenTree};
+use tokenstream::{DelimSpan, IsJoint::*, TokenStream, TokenTree, TreeAndJoint};
 
 impl<'a> StringReader<'a> {
     // Parse a stream of tokens into a list of `TokenTree`s, up to an `Eof`.
@@ -33,7 +33,7 @@ impl<'a> StringReader<'a> {
         }
     }
 
-    fn parse_token_tree(&mut self) -> PResult<'a, TokenStream> {
+    fn parse_token_tree(&mut self) -> PResult<'a, TreeAndJoint> {
         let sm = self.sess.source_map();
         match self.token {
             token::Eof => {
@@ -156,7 +156,7 @@ impl<'a> StringReader<'a> {
                 Ok(TokenTree::Delimited(
                     delim_span,
                     delim,
-                    tts.into(),
+                    tts.into()
                 ).into())
             },
             token::CloseDelim(_) => {
@@ -176,7 +176,7 @@ impl<'a> StringReader<'a> {
                 let raw = self.span_src_raw;
                 self.real_token();
                 let is_joint = raw.hi() == self.span_src_raw.lo() && token::is_op(&self.token);
-                Ok(TokenStream::Tree(tt, if is_joint { Joint } else { NonJoint }))
+                Ok((tt, if is_joint { Joint } else { NonJoint }))
             }
         }
     }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index b90eeaca54b..5c8ed94731a 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -2914,7 +2914,7 @@ impl<'a> Parser<'a> {
                 TokenTree::Delimited(
                     frame.span,
                     frame.delim,
-                    frame.tree_cursor.original_stream().into(),
+                    frame.tree_cursor.stream.into(),
                 )
             },
             token::CloseDelim(_) | token::Eof => unreachable!(),
diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs
index 4b33a715c5c..fb72ef9c956 100644
--- a/src/libsyntax/tokenstream.rs
+++ b/src/libsyntax/tokenstream.rs
@@ -147,9 +147,11 @@ impl TokenTree {
 pub enum TokenStream {
     Empty,
     Tree(TokenTree, IsJoint),
-    Stream(Lrc<Vec<TokenStream>>),
+    Stream(Lrc<Vec<TreeAndJoint>>),
 }
 
+pub type TreeAndJoint = (TokenTree, IsJoint);
+
 // `TokenStream` is used a lot. Make sure it doesn't unintentionally get bigger.
 #[cfg(target_arch = "x86_64")]
 static_assert!(MEM_SIZE_OF_TOKEN_STREAM: mem::size_of::<TokenStream>() == 32);
@@ -173,16 +175,14 @@ impl TokenStream {
             while let Some((pos, ts)) = iter.next() {
                 if let Some((_, next)) = iter.peek() {
                     let sp = match (&ts, &next) {
-                        (TokenStream::Tree(TokenTree::Token(_, token::Token::Comma), NonJoint), _) |
-                        (_, TokenStream::Tree(TokenTree::Token(_, token::Token::Comma), NonJoint))
-                          => continue,
-                        (TokenStream::Tree(TokenTree::Token(sp, _), NonJoint), _) => *sp,
-                        (TokenStream::Tree(TokenTree::Delimited(sp, ..), NonJoint), _) =>
-                            sp.entire(),
+                        ((TokenTree::Token(_, token::Token::Comma), NonJoint), _) |
+                        (_, (TokenTree::Token(_, token::Token::Comma), NonJoint)) => continue,
+                        ((TokenTree::Token(sp, _), NonJoint), _) => *sp,
+                        ((TokenTree::Delimited(sp, ..), NonJoint), _) => sp.entire(),
                         _ => continue,
                     };
                     let sp = sp.shrink_to_hi();
-                    let comma = TokenStream::Tree(TokenTree::Token(sp, token::Comma), NonJoint);
+                    let comma = (TokenTree::Token(sp, token::Comma), NonJoint);
                     suggestion = Some((pos, comma, sp));
                 }
             }
@@ -200,8 +200,14 @@ impl TokenStream {
 }
 
 impl From<TokenTree> for TokenStream {
-    fn from(tt: TokenTree) -> TokenStream {
-        TokenStream::Tree(tt, NonJoint)
+    fn from(tree: TokenTree) -> TokenStream {
+        TokenStream::Tree(tree, NonJoint)
+    }
+}
+
+impl From<TokenTree> for TreeAndJoint {
+    fn from(tree: TokenTree) -> TreeAndJoint {
+        (tree, NonJoint)
     }
 }
 
@@ -213,56 +219,7 @@ impl From<Token> for TokenStream {
 
 impl<T: Into<TokenStream>> iter::FromIterator<T> for TokenStream {
     fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
-        TokenStream::new(iter.into_iter().map(Into::into).collect::<Vec<_>>())
-    }
-}
-
-impl Extend<TokenStream> for TokenStream {
-    fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, iter: I) {
-        let iter = iter.into_iter();
-        let this = mem::replace(self, TokenStream::Empty);
-
-        // Vector of token streams originally in self.
-        let tts: Vec<TokenStream> = match this {
-            TokenStream::Empty => {
-                let mut vec = Vec::new();
-                vec.reserve(iter.size_hint().0);
-                vec
-            }
-            TokenStream::Tree(..) => {
-                let mut vec = Vec::new();
-                vec.reserve(1 + iter.size_hint().0);
-                vec.push(this);
-                vec
-            }
-            TokenStream::Stream(rc_vec) => match Lrc::try_unwrap(rc_vec) {
-                Ok(mut vec) => {
-                    // Extend in place using the existing capacity if possible.
-                    // This is the fast path for libraries like `quote` that
-                    // build a token stream.
-                    vec.reserve(iter.size_hint().0);
-                    vec
-                }
-                Err(rc_vec) => {
-                    // Self is shared so we need to copy and extend that.
-                    let mut vec = Vec::new();
-                    vec.reserve(rc_vec.len() + iter.size_hint().0);
-                    vec.extend_from_slice(&rc_vec);
-                    vec
-                }
-            }
-        };
-
-        // Perform the extend, joining tokens as needed along the way.
-        let mut builder = TokenStreamBuilder(tts);
-        for stream in iter {
-            builder.push(stream);
-        }
-
-        // Build the resulting token stream. If it contains more than one token,
-        // preserve capacity in the vector in anticipation of the caller
-        // performing additional calls to extend.
-        *self = TokenStream::new(builder.0);
+        TokenStream::from_streams(iter.into_iter().map(Into::into).collect::<Vec<_>>())
     }
 }
 
@@ -294,14 +251,43 @@ impl TokenStream {
         }
     }
 
-    pub fn new(mut streams: Vec<TokenStream>) -> TokenStream {
+    fn from_streams(mut streams: Vec<TokenStream>) -> TokenStream {
         match streams.len() {
             0 => TokenStream::empty(),
             1 => streams.pop().unwrap(),
+            _ => {
+                let mut vec = vec![];
+                for stream in streams {
+                    match stream {
+                        TokenStream::Empty => {},
+                        TokenStream::Tree(tree, is_joint) => vec.push((tree, is_joint)),
+                        TokenStream::Stream(stream2) => vec.extend(stream2.iter().cloned()),
+                    }
+                }
+                TokenStream::new(vec)
+            }
+        }
+    }
+
+    pub fn new(mut streams: Vec<TreeAndJoint>) -> TokenStream {
+        match streams.len() {
+            0 => TokenStream::empty(),
+            1 => {
+                let (tree, is_joint) = streams.pop().unwrap();
+                TokenStream::Tree(tree, is_joint)
+            }
             _ => TokenStream::Stream(Lrc::new(streams)),
         }
     }
 
+    pub fn append_to_tree_and_joint_vec(self, vec: &mut Vec<TreeAndJoint>) {
+        match self {
+            TokenStream::Empty => {}
+            TokenStream::Tree(tree, is_joint) => vec.push((tree, is_joint)),
+            TokenStream::Stream(stream) => vec.extend(stream.iter().cloned()),
+        }
+    }
+
     pub fn trees(&self) -> Cursor {
         self.clone().into_trees()
     }
@@ -362,54 +348,58 @@ impl TokenStream {
         t1.next().is_none() && t2.next().is_none()
     }
 
-    /// Precondition: `self` consists of a single token tree.
-    /// Returns true if the token tree is a joint operation w.r.t. `proc_macro::TokenNode`.
-    pub fn as_tree(self) -> (TokenTree, bool /* joint? */) {
-        match self {
-            TokenStream::Tree(tree, is_joint) => (tree, is_joint == Joint),
-            _ => unreachable!(),
-        }
-    }
-
     pub fn map_enumerated<F: FnMut(usize, TokenTree) -> TokenTree>(self, mut f: F) -> TokenStream {
-        let mut trees = self.into_trees();
-        let mut result = Vec::new();
-        let mut i = 0;
-        while let Some(stream) = trees.next_as_stream() {
-            result.push(match stream {
-                TokenStream::Tree(tree, is_joint) => TokenStream::Tree(f(i, tree), is_joint),
-                _ => unreachable!()
-            });
-            i += 1;
+        match self {
+            TokenStream::Empty => TokenStream::Empty,
+            TokenStream::Tree(tree, is_joint) => TokenStream::Tree(f(0, tree), is_joint),
+            TokenStream::Stream(stream) => TokenStream::Stream(Lrc::new(
+                stream
+                    .iter()
+                    .enumerate()
+                    .map(|(i, (tree, is_joint))| (f(i, tree.clone()), *is_joint))
+                    .collect()
+            )),
         }
-        TokenStream::new(result)
     }
 
     pub fn map<F: FnMut(TokenTree) -> TokenTree>(self, mut f: F) -> TokenStream {
-        let mut trees = self.into_trees();
-        let mut result = Vec::new();
-        while let Some(stream) = trees.next_as_stream() {
-            result.push(match stream {
-                TokenStream::Tree(tree, is_joint) => TokenStream::Tree(f(tree), is_joint),
-                _ => unreachable!()
-            });
+        match self {
+            TokenStream::Empty => TokenStream::Empty,
+            TokenStream::Tree(tree, is_joint) => TokenStream::Tree(f(tree), is_joint),
+            TokenStream::Stream(stream) => TokenStream::Stream(Lrc::new(
+                stream
+                    .iter()
+                    .map(|(tree, is_joint)| (f(tree.clone()), *is_joint))
+                    .collect()
+            )),
         }
-        TokenStream::new(result)
     }
 
     fn first_tree_and_joint(&self) -> Option<(TokenTree, IsJoint)> {
         match self {
             TokenStream::Empty => None,
             TokenStream::Tree(ref tree, is_joint) => Some((tree.clone(), *is_joint)),
-            TokenStream::Stream(ref stream) => stream.first().unwrap().first_tree_and_joint(),
+            TokenStream::Stream(ref stream) => Some(stream.first().unwrap().clone())
         }
     }
 
     fn last_tree_if_joint(&self) -> Option<TokenTree> {
         match self {
-            TokenStream::Empty | TokenStream::Tree(_, NonJoint) => None,
-            TokenStream::Tree(ref tree, Joint) => Some(tree.clone()),
-            TokenStream::Stream(ref stream) => stream.last().unwrap().last_tree_if_joint(),
+            TokenStream::Empty => None,
+            TokenStream::Tree(ref tree, is_joint) => {
+                if *is_joint == Joint {
+                    Some(tree.clone())
+                } else {
+                    None
+                }
+            }
+            TokenStream::Stream(ref stream) => {
+                if let (tree, Joint) = stream.last().unwrap() {
+                    Some(tree.clone())
+                } else {
+                    None
+                }
+            }
         }
     }
 }
@@ -442,13 +432,8 @@ impl TokenStreamBuilder {
         self.0.push(stream);
     }
 
-    pub fn add<T: Into<TokenStream>>(mut self, stream: T) -> Self {
-        self.push(stream);
-        self
-    }
-
     pub fn build(self) -> TokenStream {
-        TokenStream::new(self.0)
+        TokenStream::from_streams(self.0)
     }
 
     fn push_all_but_last_tree(&mut self, stream: &TokenStream) {
@@ -456,10 +441,9 @@ impl TokenStreamBuilder {
             let len = streams.len();
             match len {
                 1 => {}
-                2 => self.0.push(streams[0].clone().into()),
-                _ => self.0.push(TokenStream::new(streams[0 .. len - 1].to_vec())),
+                2 => self.0.push(TokenStream::Tree(streams[0].0.clone(), streams[0].1)),
+                _ => self.0.push(TokenStream::Stream(Lrc::new(streams[0 .. len - 1].to_vec()))),
             }
-            self.push_all_but_last_tree(&streams[len - 1])
         }
     }
 
@@ -468,154 +452,77 @@ impl TokenStreamBuilder {
             let len = streams.len();
             match len {
                 1 => {}
-                2 => self.0.push(streams[1].clone().into()),
-                _ => self.0.push(TokenStream::new(streams[1 .. len].to_vec())),
+                2 => self.0.push(TokenStream::Tree(streams[1].0.clone(), streams[1].1)),
+                _ => self.0.push(TokenStream::Stream(Lrc::new(streams[1 .. len].to_vec()))),
             }
-            self.push_all_but_first_tree(&streams[0])
         }
     }
 }
 
 #[derive(Clone)]
-pub struct Cursor(CursorKind);
-
-#[derive(Clone)]
-enum CursorKind {
-    Empty,
-    Tree(TokenTree, IsJoint, bool /* consumed? */),
-    Stream(StreamCursor),
-}
-
-#[derive(Clone)]
-struct StreamCursor {
-    stream: Lrc<Vec<TokenStream>>,
+pub struct Cursor {
+    pub stream: TokenStream,
     index: usize,
-    stack: Vec<(Lrc<Vec<TokenStream>>, usize)>,
-}
-
-impl StreamCursor {
-    fn new(stream: Lrc<Vec<TokenStream>>) -> Self {
-        StreamCursor { stream: stream, index: 0, stack: Vec::new() }
-    }
-
-    fn next_as_stream(&mut self) -> Option<TokenStream> {
-        loop {
-            if self.index < self.stream.len() {
-                self.index += 1;
-                let next = self.stream[self.index - 1].clone();
-                match next {
-                    TokenStream::Empty => {}
-                    TokenStream::Tree(..) => return Some(next),
-                    TokenStream::Stream(stream) => self.insert(stream),
-                }
-            } else if let Some((stream, index)) = self.stack.pop() {
-                self.stream = stream;
-                self.index = index;
-            } else {
-                return None;
-            }
-        }
-    }
-
-    fn insert(&mut self, stream: Lrc<Vec<TokenStream>>) {
-        self.stack.push((mem::replace(&mut self.stream, stream),
-                         mem::replace(&mut self.index, 0)));
-    }
 }
 
 impl Iterator for Cursor {
     type Item = TokenTree;
 
     fn next(&mut self) -> Option<TokenTree> {
-        self.next_as_stream().map(|stream| match stream {
-            TokenStream::Tree(tree, _) => tree,
-            _ => unreachable!()
-        })
+        self.next_with_joint().map(|(tree, _)| tree)
     }
 }
 
 impl Cursor {
     fn new(stream: TokenStream) -> Self {
-        Cursor(match stream {
-            TokenStream::Empty => CursorKind::Empty,
-            TokenStream::Tree(tree, is_joint) => CursorKind::Tree(tree, is_joint, false),
-            TokenStream::Stream(stream) => CursorKind::Stream(StreamCursor::new(stream)),
-        })
-    }
-
-    pub fn next_as_stream(&mut self) -> Option<TokenStream> {
-        let (stream, consumed) = match self.0 {
-            CursorKind::Tree(ref tree, ref is_joint, ref mut consumed @ false) =>
-                (TokenStream::Tree(tree.clone(), *is_joint), consumed),
-            CursorKind::Stream(ref mut cursor) => return cursor.next_as_stream(),
-            _ => return None,
-        };
-
-        *consumed = true;
-        Some(stream)
+        Cursor { stream, index: 0 }
     }
 
-    pub fn insert(&mut self, stream: TokenStream) {
-        match self.0 {
-            _ if stream.is_empty() => return,
-            CursorKind::Empty => *self = stream.trees(),
-            CursorKind::Tree(_, _, consumed) => {
-                *self = TokenStream::new(vec![self.original_stream(), stream]).trees();
-                if consumed {
-                    self.next();
+    pub fn next_with_joint(&mut self) -> Option<TreeAndJoint> {
+        match self.stream {
+            TokenStream::Empty => None,
+            TokenStream::Tree(ref tree, ref is_joint) => {
+                if self.index == 0 {
+                    self.index = 1;
+                    Some((tree.clone(), *is_joint))
+                } else {
+                    None
                 }
             }
-            CursorKind::Stream(ref mut cursor) => {
-                cursor.insert(ThinTokenStream::from(stream).0.unwrap());
+            TokenStream::Stream(ref stream) => {
+                if self.index < stream.len() {
+                    self.index += 1;
+                    Some(stream[self.index - 1].clone())
+                } else {
+                    None
+                }
             }
         }
     }
 
-    pub fn original_stream(&self) -> TokenStream {
-        match self.0 {
-            CursorKind::Empty => TokenStream::empty(),
-            CursorKind::Tree(ref tree, ref is_joint, _) =>
-                TokenStream::Tree(tree.clone(), *is_joint),
-            CursorKind::Stream(ref cursor) => TokenStream::Stream(
-                cursor.stack.get(0).cloned().map(|(stream, _)| stream)
-                    .unwrap_or_else(|| cursor.stream.clone())
-            ),
+    pub fn append(&mut self, new_stream: TokenStream) {
+        if new_stream.is_empty() {
+            return;
         }
+        let index = self.index;
+        let stream = mem::replace(&mut self.stream, TokenStream::Empty);
+        *self = TokenStream::from_streams(vec![stream, new_stream]).into_trees();
+        self.index = index;
     }
 
     pub fn look_ahead(&self, n: usize) -> Option<TokenTree> {
-        fn look_ahead(streams: &[TokenStream], mut n: usize) -> Result<TokenTree, usize> {
-            for stream in streams {
-                n = match stream {
-                    TokenStream::Tree(ref tree, _) if n == 0 => return Ok(tree.clone()),
-                    TokenStream::Tree(..) => n - 1,
-                    TokenStream::Stream(ref stream) => match look_ahead(stream, n) {
-                        Ok(tree) => return Ok(tree),
-                        Err(n) => n,
-                    },
-                    _ => n,
-                };
+        match self.stream {
+            TokenStream::Empty => None,
+            TokenStream::Tree(ref tree, _) => {
+                if n == 0 && self.index == 0 {
+                    Some(tree.clone())
+                } else {
+                    None
+                }
             }
-            Err(n)
+            TokenStream::Stream(ref stream) =>
+                stream[self.index ..].get(n).map(|(tree, _)| tree.clone()),
         }
-
-        match self.0 {
-            CursorKind::Empty |
-            CursorKind::Tree(_, _, true) => Err(n),
-            CursorKind::Tree(ref tree, _, false) => look_ahead(&[tree.clone().into()], n),
-            CursorKind::Stream(ref cursor) => {
-                look_ahead(&cursor.stream[cursor.index ..], n).or_else(|mut n| {
-                    for &(ref stream, index) in cursor.stack.iter().rev() {
-                        n = match look_ahead(&stream[index..], n) {
-                            Ok(tree) => return Ok(tree),
-                            Err(n) => n,
-                        }
-                    }
-
-                    Err(n)
-                })
-            }
-        }.ok()
     }
 }
 
@@ -623,7 +530,7 @@ impl Cursor {
 /// `ThinTokenStream` is smaller, but needs to allocate to represent a single `TokenTree`.
 /// We must use `ThinTokenStream` in `TokenTree::Delimited` to avoid infinite size due to recursion.
 #[derive(Debug, Clone)]
-pub struct ThinTokenStream(Option<Lrc<Vec<TokenStream>>>);
+pub struct ThinTokenStream(Option<Lrc<Vec<TreeAndJoint>>>);
 
 impl ThinTokenStream {
     pub fn stream(&self) -> TokenStream {
@@ -635,7 +542,7 @@ impl From<TokenStream> for ThinTokenStream {
     fn from(stream: TokenStream) -> ThinTokenStream {
         ThinTokenStream(match stream {
             TokenStream::Empty => None,
-            TokenStream::Tree(..) => Some(Lrc::new(vec![stream])),
+            TokenStream::Tree(tree, is_joint) => Some(Lrc::new(vec![(tree, is_joint)])),
             TokenStream::Stream(stream) => Some(stream),
         })
     }
@@ -742,7 +649,7 @@ mod tests {
             let test_res = string_to_ts("foo::bar::baz");
             let test_fst = string_to_ts("foo::bar");
             let test_snd = string_to_ts("::baz");
-            let eq_res = TokenStream::new(vec![test_fst, test_snd]);
+            let eq_res = TokenStream::from_streams(vec![test_fst, test_snd]);
             assert_eq!(test_res.trees().count(), 5);
             assert_eq!(eq_res.trees().count(), 5);
             assert_eq!(test_res.eq_unspanned(&eq_res), true);
@@ -827,107 +734,4 @@ mod tests {
         assert!(stream.eq_unspanned(&string_to_ts("...")));
         assert_eq!(stream.trees().count(), 1);
     }
-
-    #[test]
-    fn test_extend_empty() {
-        with_globals(|| {
-            // Append a token onto an empty token stream.
-            let mut stream = TokenStream::empty();
-            stream.extend(vec![string_to_ts("t")]);
-
-            let expected = string_to_ts("t");
-            assert!(stream.eq_unspanned(&expected));
-        });
-    }
-
-    #[test]
-    fn test_extend_nothing() {
-        with_globals(|| {
-            // Append nothing onto a token stream containing one token.
-            let mut stream = string_to_ts("t");
-            stream.extend(vec![]);
-
-            let expected = string_to_ts("t");
-            assert!(stream.eq_unspanned(&expected));
-        });
-    }
-
-    #[test]
-    fn test_extend_single() {
-        with_globals(|| {
-            // Append a token onto token stream containing a single token.
-            let mut stream = string_to_ts("t1");
-            stream.extend(vec![string_to_ts("t2")]);
-
-            let expected = string_to_ts("t1 t2");
-            assert!(stream.eq_unspanned(&expected));
-        });
-    }
-
-    #[test]
-    fn test_extend_in_place() {
-        with_globals(|| {
-            // Append a token onto token stream containing a reference counted
-            // vec of tokens. The token stream has a reference count of 1 so
-            // this can happen in place.
-            let mut stream = string_to_ts("t1 t2");
-            stream.extend(vec![string_to_ts("t3")]);
-
-            let expected = string_to_ts("t1 t2 t3");
-            assert!(stream.eq_unspanned(&expected));
-        });
-    }
-
-    #[test]
-    fn test_extend_copy() {
-        with_globals(|| {
-            // Append a token onto token stream containing a reference counted
-            // vec of tokens. The token stream is shared so the extend takes
-            // place on a copy.
-            let mut stream = string_to_ts("t1 t2");
-            let _incref = stream.clone();
-            stream.extend(vec![string_to_ts("t3")]);
-
-            let expected = string_to_ts("t1 t2 t3");
-            assert!(stream.eq_unspanned(&expected));
-        });
-    }
-
-    #[test]
-    fn test_extend_no_join() {
-        with_globals(|| {
-            let first = TokenTree::Token(DUMMY_SP, Token::Dot);
-            let second = TokenTree::Token(DUMMY_SP, Token::Dot);
-
-            // Append a dot onto a token stream containing a dot, but do not
-            // join them.
-            let mut stream = TokenStream::from(first);
-            stream.extend(vec![TokenStream::from(second)]);
-
-            let expected = string_to_ts(". .");
-            assert!(stream.eq_unspanned(&expected));
-
-            let unexpected = string_to_ts("..");
-            assert!(!stream.eq_unspanned(&unexpected));
-        });
-    }
-
-    #[test]
-    fn test_extend_join() {
-        with_globals(|| {
-            let first = TokenTree::Token(DUMMY_SP, Token::Dot).joint();
-            let second = TokenTree::Token(DUMMY_SP, Token::Dot);
-
-            // Append a dot onto a token stream containing a dot, forming a
-            // dotdot.
-            let mut stream = first;
-            stream.extend(vec![TokenStream::from(second)]);
-
-            let expected = string_to_ts("..");
-            assert!(stream.eq_unspanned(&expected));
-
-            let unexpected = string_to_ts(". .");
-            assert!(!stream.eq_unspanned(&unexpected));
-        });
-    }
 }
diff --git a/src/libsyntax_ext/proc_macro_server.rs b/src/libsyntax_ext/proc_macro_server.rs
index afd86a4f746..158cbc791ef 100644
--- a/src/libsyntax_ext/proc_macro_server.rs
+++ b/src/libsyntax_ext/proc_macro_server.rs
@@ -11,7 +11,7 @@ use syntax::ast;
 use syntax::ext::base::ExtCtxt;
 use syntax::parse::lexer::comments;
 use syntax::parse::{self, token, ParseSess};
-use syntax::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream};
+use syntax::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint};
 use syntax_pos::hygiene::{SyntaxContext, Transparency};
 use syntax_pos::symbol::{keywords, Symbol};
 use syntax_pos::{BytePos, FileName, MultiSpan, Pos, SourceFile, Span};
@@ -46,13 +46,14 @@ impl ToInternal<token::DelimToken> for Delimiter {
     }
 }
 
-impl FromInternal<(TokenStream, &'_ ParseSess, &'_ mut Vec<Self>)>
+impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec<Self>)>
     for TokenTree<Group, Punct, Ident, Literal>
 {
-    fn from_internal((stream, sess, stack): (TokenStream, &ParseSess, &mut Vec<Self>)) -> Self {
+    fn from_internal(((tree, is_joint), sess, stack): (TreeAndJoint, &ParseSess, &mut Vec<Self>))
+                    -> Self {
         use syntax::parse::token::*;
 
-        let (tree, joint) = stream.as_tree();
+        let joint = is_joint == Joint;
         let (span, token) = match tree {
             tokenstream::TokenTree::Delimited(span, delim, tts) => {
                 let delimiter = Delimiter::from_internal(delim);
@@ -450,7 +451,7 @@ impl server::TokenStreamIter for Rustc<'_> {
     ) -> Option<TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>> {
         loop {
             let tree = iter.stack.pop().or_else(|| {
-                let next = iter.cursor.next_as_stream()?;
+                let next = iter.cursor.next_with_joint()?;
                 Some(TokenTree::from_internal((next, self.sess, &mut iter.stack)))
             })?;
             // HACK: The condition "dummy span + group with empty delimiter" represents an AST
@@ -461,7 +462,7 @@ impl server::TokenStreamIter for Rustc<'_> {
             // and not doing the roundtrip through AST.
             if let TokenTree::Group(ref group) = tree {
                 if group.delimiter == Delimiter::None && group.span.entire().is_dummy() {
-                    iter.cursor.insert(group.stream.clone());
+                    iter.cursor.append(group.stream.clone());
                     continue;
                 }
             }
diff --git a/src/test/ui/borrowck/borrowck-borrow-from-owned-ptr.stderr b/src/test/ui/borrowck/borrowck-borrow-from-owned-ptr.stderr
index 79dc55bbbae..e116cb70c1c 100644
--- a/src/test/ui/borrowck/borrowck-borrow-from-owned-ptr.stderr
+++ b/src/test/ui/borrowck/borrowck-borrow-from-owned-ptr.stderr
@@ -138,7 +138,7 @@ error[E0502]: cannot borrow `foo` (via `foo.bar2`) as immutable because `foo` is
 LL |     let bar1 = &mut foo.bar1;
    |                     -------- mutable borrow occurs here (via `foo.bar1`)
 LL |     let _foo1 = &foo.bar2; //~ ERROR cannot borrow
-   |                  ^^^^^^^^ immutable borrow occurs here (via `foo.bar2`)
+   |                  ^^^^^^^^ immutable borrow of `foo.bar2` -- which overlaps with `foo.bar1` -- occurs here
 LL |     *bar1;
 LL | }
    | - mutable borrow ends here
diff --git a/src/test/ui/borrowck/borrowck-box-insensitivity.ast.stderr b/src/test/ui/borrowck/borrowck-box-insensitivity.ast.stderr
index 95b26a5724a..236064da3e8 100644
--- a/src/test/ui/borrowck/borrowck-box-insensitivity.ast.stderr
+++ b/src/test/ui/borrowck/borrowck-box-insensitivity.ast.stderr
@@ -61,7 +61,7 @@ error[E0502]: cannot borrow `a` (via `a.y`) as immutable because `a` is also bor
 LL |     let _x = &mut a.x;
    |                   --- mutable borrow occurs here (via `a.x`)
 LL |     let _y = &a.y; //[ast]~ ERROR cannot borrow
-   |               ^^^ immutable borrow occurs here (via `a.y`)
+   |               ^^^ immutable borrow of `a.y` -- which overlaps with `a.x` -- occurs here
 ...
 LL | }
    | - mutable borrow ends here
@@ -72,7 +72,7 @@ error[E0502]: cannot borrow `a` (via `a.y`) as mutable because `a` is also borro
 LL |     let _x = &a.x;
    |               --- immutable borrow occurs here (via `a.x`)
 LL |     let _y = &mut a.y; //[ast]~ ERROR cannot borrow
-   |                   ^^^ mutable borrow occurs here (via `a.y`)
+   |                   ^^^ mutable borrow of `a.y` -- which overlaps with `a.x` -- occurs here
 ...
 LL | }
    | - immutable borrow ends here
diff --git a/src/test/ui/borrowck/borrowck-box-insensitivity.rs b/src/test/ui/borrowck/borrowck-box-insensitivity.rs
index 2af97a9fc1d..e72048d0ea4 100644
--- a/src/test/ui/borrowck/borrowck-box-insensitivity.rs
+++ b/src/test/ui/borrowck/borrowck-box-insensitivity.rs
@@ -83,14 +83,14 @@ fn borrow_after_mut_borrow() {
     let mut a: Box<_> = box A { x: box 0, y: 1 };
     let _x = &mut a.x;
     let _y = &a.y; //[ast]~ ERROR cannot borrow
-    //[ast]~^ immutable borrow occurs here (via `a.y`)
+    //[ast]~^ immutable borrow of `a.y` -- which overlaps with `a.x` -- occurs here
     use_mut(_x);
 }
 fn mut_borrow_after_borrow() {
     let mut a: Box<_> = box A { x: box 0, y: 1 };
     let _x = &a.x;
     let _y = &mut a.y; //[ast]~ ERROR cannot borrow
-    //[ast]~^ mutable borrow occurs here (via `a.y`)
+    //[ast]~^ mutable borrow of `a.y` -- which overlaps with `a.x` -- occurs here
     use_imm(_x);
 }
 fn copy_after_move_nested() {
diff --git a/src/test/ui/borrowck/borrowck-union-borrow.ast.nll.stderr b/src/test/ui/borrowck/borrowck-union-borrow.nll.stderr
index 1a2433c8f6a..5cba30b43b8 100644
--- a/src/test/ui/borrowck/borrowck-union-borrow.ast.nll.stderr
+++ b/src/test/ui/borrowck/borrowck-union-borrow.nll.stderr
@@ -1,132 +1,127 @@
 error[E0502]: cannot borrow `u.a` as mutable because it is also borrowed as immutable
-  --> $DIR/borrowck-union-borrow.rs:27:23
+  --> $DIR/borrowck-union-borrow.rs:25:23
    |
 LL |             let ra = &u.a;
    |                      ---- immutable borrow occurs here
-LL |             let rma = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable
+LL |             let rma = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable
    |                       ^^^^^^^^ mutable borrow occurs here
-LL |                                 //[mir]~^ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable
 LL |             drop(ra);
    |                  -- immutable borrow later used here
 
 error[E0506]: cannot assign to `u.a` because it is borrowed
-  --> $DIR/borrowck-union-borrow.rs:33:13
+  --> $DIR/borrowck-union-borrow.rs:30:13
    |
 LL |             let ra = &u.a;
    |                      ---- borrow of `u.a` occurs here
-LL |             u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed
+LL |             u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed
    |             ^^^^^^^ assignment to borrowed `u.a` occurs here
-LL |                      //[mir]~^ ERROR cannot assign to `u.a` because it is borrowed
 LL |             drop(ra);
    |                  -- borrow later used here
 
-error[E0502]: cannot borrow `u.b` as mutable because it is also borrowed as immutable
-  --> $DIR/borrowck-union-borrow.rs:50:23
+error[E0502]: cannot borrow `u` (via `u.b`) as mutable because it is also borrowed as immutable (via `u.a`)
+  --> $DIR/borrowck-union-borrow.rs:46:23
    |
 LL |             let ra = &u.a;
-   |                      ---- immutable borrow occurs here
-LL |             let rmb = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`)
-   |                       ^^^^^^^^ mutable borrow occurs here
-LL |                                 //[mir]~^ ERROR cannot borrow `u.b` as mutable because it is also borrowed as immutable
+   |                      ---- immutable borrow occurs here (via `u.a`)
+LL |             let rmb = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`)
+   |                       ^^^^^^^^ mutable borrow of `u.b` -- which overlaps with `u.a` -- occurs here
 LL |             drop(ra);
    |                  -- immutable borrow later used here
+   |
+   = note: `u.b` is a field of the union `U`, so it overlaps the field `u.a`
 
 error[E0506]: cannot assign to `u.b` because it is borrowed
-  --> $DIR/borrowck-union-borrow.rs:56:13
+  --> $DIR/borrowck-union-borrow.rs:51:13
    |
 LL |             let ra = &u.a;
    |                      ---- borrow of `u.b` occurs here
-LL |             u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed
+LL |             u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed
    |             ^^^^^^^ assignment to borrowed `u.b` occurs here
-LL |                      //[mir]~^ ERROR cannot assign to `u.b` because it is borrowed
 LL |             drop(ra);
    |                  -- borrow later used here
 
 error[E0502]: cannot borrow `u.a` as immutable because it is also borrowed as mutable
-  --> $DIR/borrowck-union-borrow.rs:63:22
+  --> $DIR/borrowck-union-borrow.rs:57:22
    |
 LL |             let rma = &mut u.a;
    |                       -------- mutable borrow occurs here
-LL |             let ra = &u.a; //[ast]~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable
+LL |             let ra = &u.a; //~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable
    |                      ^^^^ immutable borrow occurs here
-LL |                          //[mir]~^ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable
 LL |             drop(rma);
    |                  --- mutable borrow later used here
 
 error[E0503]: cannot use `u.a` because it was mutably borrowed
-  --> $DIR/borrowck-union-borrow.rs:69:21
+  --> $DIR/borrowck-union-borrow.rs:62:21
    |
 LL |             let ra = &mut u.a;
    |                      -------- borrow of `u.a` occurs here
-LL |             let a = u.a; //[ast]~ ERROR cannot use `u.a` because it was mutably borrowed
+LL |             let a = u.a; //~ ERROR cannot use `u.a` because it was mutably borrowed
    |                     ^^^ use of borrowed `u.a`
-LL |                          //[mir]~^ ERROR cannot use `u.a` because it was mutably borrowed
 LL |             drop(ra);
    |                  -- borrow later used here
 
 error[E0499]: cannot borrow `u.a` as mutable more than once at a time
-  --> $DIR/borrowck-union-borrow.rs:75:24
+  --> $DIR/borrowck-union-borrow.rs:67:24
    |
 LL |             let rma = &mut u.a;
    |                       -------- first mutable borrow occurs here
-LL |             let rma2 = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable more than once at a time
+LL |             let rma2 = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable more than once at a time
    |                        ^^^^^^^^ second mutable borrow occurs here
-LL |                                  //[mir]~^ ERROR cannot borrow `u.a` as mutable more than once at a time
 LL |             drop(rma);
    |                  --- first borrow later used here
 
 error[E0506]: cannot assign to `u.a` because it is borrowed
-  --> $DIR/borrowck-union-borrow.rs:81:13
+  --> $DIR/borrowck-union-borrow.rs:72:13
    |
 LL |             let rma = &mut u.a;
    |                       -------- borrow of `u.a` occurs here
-LL |             u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed
+LL |             u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed
    |             ^^^^^^^ assignment to borrowed `u.a` occurs here
-LL |                      //[mir]~^ ERROR cannot assign to `u.a` because it is borrowed
 LL |             drop(rma);
    |                  --- borrow later used here
 
-error[E0502]: cannot borrow `u.b` as immutable because it is also borrowed as mutable
-  --> $DIR/borrowck-union-borrow.rs:88:22
+error[E0502]: cannot borrow `u` (via `u.b`) as immutable because it is also borrowed as mutable (via `u.a`)
+  --> $DIR/borrowck-union-borrow.rs:78:22
    |
 LL |             let rma = &mut u.a;
-   |                       -------- mutable borrow occurs here
-LL |             let rb = &u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`)
-   |                      ^^^^ immutable borrow occurs here
-LL |                            //[mir]~^ ERROR cannot borrow `u.b` as immutable because it is also borrowed as mutable
+   |                       -------- mutable borrow occurs here (via `u.a`)
+LL |             let rb = &u.b; //~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`)
+   |                      ^^^^ immutable borrow of `u.b` -- which overlaps with `u.a` -- occurs here
 LL |             drop(rma);
    |                  --- mutable borrow later used here
+   |
+   = note: `u.b` is a field of the union `U`, so it overlaps the field `u.a`
 
 error[E0503]: cannot use `u.b` because it was mutably borrowed
-  --> $DIR/borrowck-union-borrow.rs:94:21
+  --> $DIR/borrowck-union-borrow.rs:83:21
    |
 LL |             let ra = &mut u.a;
    |                      -------- borrow of `u.a` occurs here
-LL |             let b = u.b; //[ast]~ ERROR cannot use `u.b` because it was mutably borrowed
+LL |             let b = u.b; //~ ERROR cannot use `u.b` because it was mutably borrowed
    |                     ^^^ use of borrowed `u.a`
-...
+LL | 
 LL |             drop(ra);
    |                  -- borrow later used here
 
-error[E0499]: cannot borrow `u.b` as mutable more than once at a time
-  --> $DIR/borrowck-union-borrow.rs:101:24
+error[E0499]: cannot borrow `u` (via `u.b`) as mutable more than once at a time
+  --> $DIR/borrowck-union-borrow.rs:89:24
    |
 LL |             let rma = &mut u.a;
-   |                       -------- first mutable borrow occurs here
-LL |             let rmb2 = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time
-   |                        ^^^^^^^^ second mutable borrow occurs here
-LL |                                  //[mir]~^ ERROR cannot borrow `u.b` as mutable more than once at a time
+   |                       -------- first mutable borrow occurs here (via `u.a`)
+LL |             let rmb2 = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time
+   |                        ^^^^^^^^ second mutable borrow occurs here (via `u.b`)
 LL |             drop(rma);
    |                  --- first borrow later used here
+   |
+   = note: `u.b` is a field of the union `U`, so it overlaps the field `u.a`
 
 error[E0506]: cannot assign to `u.b` because it is borrowed
-  --> $DIR/borrowck-union-borrow.rs:107:13
+  --> $DIR/borrowck-union-borrow.rs:94:13
    |
 LL |             let rma = &mut u.a;
    |                       -------- borrow of `u.b` occurs here
-LL |             u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed
+LL |             u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed
    |             ^^^^^^^ assignment to borrowed `u.b` occurs here
-LL |                      //[mir]~^ ERROR cannot assign to `u.b` because it is borrowed
 LL |             drop(rma);
    |                  --- borrow later used here
 
diff --git a/src/test/ui/borrowck/borrowck-union-borrow.rs b/src/test/ui/borrowck/borrowck-union-borrow.rs
index 62647a2f5e8..8afc0be8b55 100644
--- a/src/test/ui/borrowck/borrowck-union-borrow.rs
+++ b/src/test/ui/borrowck/borrowck-union-borrow.rs
@@ -1,6 +1,4 @@
 // ignore-tidy-linelength
-// revisions: ast mir
-//[mir]compile-flags: -Z borrowck=mir
 
 #[derive(Clone, Copy)]
 union U {
@@ -24,14 +22,12 @@ fn main() {
         }
         {
             let ra = &u.a;
-            let rma = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable
-                                //[mir]~^ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable
+            let rma = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable
             drop(ra);
         }
         {
             let ra = &u.a;
-            u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed
-                     //[mir]~^ ERROR cannot assign to `u.a` because it is borrowed
+            u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed
             drop(ra);
         }
         // Imm borrow, other field
@@ -47,65 +43,55 @@ fn main() {
         }
         {
             let ra = &u.a;
-            let rmb = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`)
-                                //[mir]~^ ERROR cannot borrow `u.b` as mutable because it is also borrowed as immutable
+            let rmb = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`)
             drop(ra);
         }
         {
             let ra = &u.a;
-            u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed
-                     //[mir]~^ ERROR cannot assign to `u.b` because it is borrowed
+            u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed
             drop(ra);
         }
         // Mut borrow, same field
         {
             let rma = &mut u.a;
-            let ra = &u.a; //[ast]~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable
-                         //[mir]~^ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable
+            let ra = &u.a; //~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable
             drop(rma);
         }
         {
             let ra = &mut u.a;
-            let a = u.a; //[ast]~ ERROR cannot use `u.a` because it was mutably borrowed
-                         //[mir]~^ ERROR cannot use `u.a` because it was mutably borrowed
+            let a = u.a; //~ ERROR cannot use `u.a` because it was mutably borrowed
             drop(ra);
         }
         {
             let rma = &mut u.a;
-            let rma2 = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable more than once at a time
-                                 //[mir]~^ ERROR cannot borrow `u.a` as mutable more than once at a time
+            let rma2 = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable more than once at a time
             drop(rma);
         }
         {
             let rma = &mut u.a;
-            u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed
-                     //[mir]~^ ERROR cannot assign to `u.a` because it is borrowed
+            u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed
             drop(rma);
         }
         // Mut borrow, other field
         {
             let rma = &mut u.a;
-            let rb = &u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`)
-                           //[mir]~^ ERROR cannot borrow `u.b` as immutable because it is also borrowed as mutable
+            let rb = &u.b; //~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`)
             drop(rma);
         }
         {
             let ra = &mut u.a;
-            let b = u.b; //[ast]~ ERROR cannot use `u.b` because it was mutably borrowed
-                         //[mir]~^ ERROR cannot use `u.b` because it was mutably borrowed
+            let b = u.b; //~ ERROR cannot use `u.b` because it was mutably borrowed
 
             drop(ra);
         }
         {
             let rma = &mut u.a;
-            let rmb2 = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time
-                                 //[mir]~^ ERROR cannot borrow `u.b` as mutable more than once at a time
+            let rmb2 = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time
             drop(rma);
         }
         {
             let rma = &mut u.a;
-            u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed
-                     //[mir]~^ ERROR cannot assign to `u.b` because it is borrowed
+            u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed
             drop(rma);
         }
     }
diff --git a/src/test/ui/borrowck/borrowck-union-borrow.ast.stderr b/src/test/ui/borrowck/borrowck-union-borrow.stderr
index 0f786b81d3a..ef6a331eda0 100644
--- a/src/test/ui/borrowck/borrowck-union-borrow.ast.stderr
+++ b/src/test/ui/borrowck/borrowck-union-borrow.stderr
@@ -1,115 +1,115 @@
 error[E0502]: cannot borrow `u.a` as mutable because it is also borrowed as immutable
-  --> $DIR/borrowck-union-borrow.rs:27:28
+  --> $DIR/borrowck-union-borrow.rs:25:28
    |
 LL |             let ra = &u.a;
    |                       --- immutable borrow occurs here
-LL |             let rma = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable
+LL |             let rma = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable because it is also borrowed as immutable
    |                            ^^^ mutable borrow occurs here
-...
+LL |             drop(ra);
 LL |         }
    |         - immutable borrow ends here
 
 error[E0506]: cannot assign to `u.a` because it is borrowed
-  --> $DIR/borrowck-union-borrow.rs:33:13
+  --> $DIR/borrowck-union-borrow.rs:30:13
    |
 LL |             let ra = &u.a;
    |                       --- borrow of `u.a` occurs here
-LL |             u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed
+LL |             u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed
    |             ^^^^^^^ assignment to borrowed `u.a` occurs here
 
 error[E0502]: cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`)
-  --> $DIR/borrowck-union-borrow.rs:50:28
+  --> $DIR/borrowck-union-borrow.rs:46:28
    |
 LL |             let ra = &u.a;
    |                       --- immutable borrow occurs here (via `u.a`)
-LL |             let rmb = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`)
-   |                            ^^^ mutable borrow occurs here (via `u.b`)
-...
+LL |             let rmb = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable because `u` is also borrowed as immutable (via `u.a`)
+   |                            ^^^ mutable borrow of `u.b` -- which overlaps with `u.a` -- occurs here
+LL |             drop(ra);
 LL |         }
    |         - immutable borrow ends here
 
 error[E0506]: cannot assign to `u.b` because it is borrowed
-  --> $DIR/borrowck-union-borrow.rs:56:13
+  --> $DIR/borrowck-union-borrow.rs:51:13
    |
 LL |             let ra = &u.a;
    |                       --- borrow of `u.b` occurs here
-LL |             u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed
+LL |             u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed
    |             ^^^^^^^ assignment to borrowed `u.b` occurs here
 
 error[E0502]: cannot borrow `u.a` as immutable because it is also borrowed as mutable
-  --> $DIR/borrowck-union-borrow.rs:63:23
+  --> $DIR/borrowck-union-borrow.rs:57:23
    |
 LL |             let rma = &mut u.a;
    |                            --- mutable borrow occurs here
-LL |             let ra = &u.a; //[ast]~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable
+LL |             let ra = &u.a; //~ ERROR cannot borrow `u.a` as immutable because it is also borrowed as mutable
    |                       ^^^ immutable borrow occurs here
-...
+LL |             drop(rma);
 LL |         }
    |         - mutable borrow ends here
 
 error[E0503]: cannot use `u.a` because it was mutably borrowed
-  --> $DIR/borrowck-union-borrow.rs:69:17
+  --> $DIR/borrowck-union-borrow.rs:62:17
    |
 LL |             let ra = &mut u.a;
    |                           --- borrow of `u.a` occurs here
-LL |             let a = u.a; //[ast]~ ERROR cannot use `u.a` because it was mutably borrowed
+LL |             let a = u.a; //~ ERROR cannot use `u.a` because it was mutably borrowed
    |                 ^ use of borrowed `u.a`
 
 error[E0499]: cannot borrow `u.a` as mutable more than once at a time
-  --> $DIR/borrowck-union-borrow.rs:75:29
+  --> $DIR/borrowck-union-borrow.rs:67:29
    |
 LL |             let rma = &mut u.a;
    |                            --- first mutable borrow occurs here
-LL |             let rma2 = &mut u.a; //[ast]~ ERROR cannot borrow `u.a` as mutable more than once at a time
+LL |             let rma2 = &mut u.a; //~ ERROR cannot borrow `u.a` as mutable more than once at a time
    |                             ^^^ second mutable borrow occurs here
-...
+LL |             drop(rma);
 LL |         }
    |         - first borrow ends here
 
 error[E0506]: cannot assign to `u.a` because it is borrowed
-  --> $DIR/borrowck-union-borrow.rs:81:13
+  --> $DIR/borrowck-union-borrow.rs:72:13
    |
 LL |             let rma = &mut u.a;
    |                            --- borrow of `u.a` occurs here
-LL |             u.a = 1; //[ast]~ ERROR cannot assign to `u.a` because it is borrowed
+LL |             u.a = 1; //~ ERROR cannot assign to `u.a` because it is borrowed
    |             ^^^^^^^ assignment to borrowed `u.a` occurs here
 
 error[E0502]: cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`)
-  --> $DIR/borrowck-union-borrow.rs:88:23
+  --> $DIR/borrowck-union-borrow.rs:78:23
    |
 LL |             let rma = &mut u.a;
    |                            --- mutable borrow occurs here (via `u.a`)
-LL |             let rb = &u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`)
-   |                       ^^^ immutable borrow occurs here (via `u.b`)
-...
+LL |             let rb = &u.b; //~ ERROR cannot borrow `u` (via `u.b`) as immutable because `u` is also borrowed as mutable (via `u.a`)
+   |                       ^^^ immutable borrow of `u.b` -- which overlaps with `u.a` -- occurs here
+LL |             drop(rma);
 LL |         }
    |         - mutable borrow ends here
 
 error[E0503]: cannot use `u.b` because it was mutably borrowed
-  --> $DIR/borrowck-union-borrow.rs:94:17
+  --> $DIR/borrowck-union-borrow.rs:83:17
    |
 LL |             let ra = &mut u.a;
    |                           --- borrow of `u.a` occurs here
-LL |             let b = u.b; //[ast]~ ERROR cannot use `u.b` because it was mutably borrowed
+LL |             let b = u.b; //~ ERROR cannot use `u.b` because it was mutably borrowed
    |                 ^ use of borrowed `u.a`
 
 error[E0499]: cannot borrow `u` (via `u.b`) as mutable more than once at a time
-  --> $DIR/borrowck-union-borrow.rs:101:29
+  --> $DIR/borrowck-union-borrow.rs:89:29
    |
 LL |             let rma = &mut u.a;
    |                            --- first mutable borrow occurs here (via `u.a`)
-LL |             let rmb2 = &mut u.b; //[ast]~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time
+LL |             let rmb2 = &mut u.b; //~ ERROR cannot borrow `u` (via `u.b`) as mutable more than once at a time
    |                             ^^^ second mutable borrow occurs here (via `u.b`)
-...
+LL |             drop(rma);
 LL |         }
    |         - first borrow ends here
 
 error[E0506]: cannot assign to `u.b` because it is borrowed
-  --> $DIR/borrowck-union-borrow.rs:107:13
+  --> $DIR/borrowck-union-borrow.rs:94:13
    |
 LL |             let rma = &mut u.a;
    |                            --- borrow of `u.b` occurs here
-LL |             u.b = 1; //[ast]~ ERROR cannot assign to `u.b` because it is borrowed
+LL |             u.b = 1; //~ ERROR cannot assign to `u.b` because it is borrowed
    |             ^^^^^^^ assignment to borrowed `u.b` occurs here
 
 error: aborting due to 12 previous errors
diff --git a/src/test/ui/issues/issue-17263.ast.stderr b/src/test/ui/issues/issue-17263.ast.stderr
index 3d42dcb52f5..823f2c747d6 100644
--- a/src/test/ui/issues/issue-17263.ast.stderr
+++ b/src/test/ui/issues/issue-17263.ast.stderr
@@ -13,7 +13,7 @@ error[E0502]: cannot borrow `foo` (via `foo.b`) as immutable because `foo` is al
   --> $DIR/issue-17263.rs:21:32
    |
 LL |     let (c, d) = (&mut foo.a, &foo.b);
-   |                        -----   ^^^^^ immutable borrow occurs here (via `foo.b`)
+   |                        -----   ^^^^^ immutable borrow of `foo.b` -- which overlaps with `foo.a` -- occurs here
    |                        |
    |                        mutable borrow occurs here (via `foo.a`)
 ...
diff --git a/src/test/ui/issues/issue-45157.rs b/src/test/ui/issues/issue-45157.rs
index c2c9ed62d4e..22ea254a769 100644
--- a/src/test/ui/issues/issue-45157.rs
+++ b/src/test/ui/issues/issue-45157.rs
@@ -1,6 +1,8 @@
 #![allow(unused)]
 #![feature(nll)]
 
+// ignore-tidy-linelength
+
 #[derive(Clone, Copy, Default)]
 struct S {
     a: u8,
@@ -25,8 +27,7 @@ fn main() {
         *mref = 22;
 
         let nref = &u.z.c;
-        //~^ ERROR cannot borrow `u.z.c` as immutable because it is also borrowed as mutable [E0502]
+        //~^ ERROR cannot borrow `u` (via `u.z.c`) as immutable because it is also borrowed as mutable (via `u.s.a`) [E0502]
         println!("{} {}", mref, nref)
     }
 }
-
diff --git a/src/test/ui/issues/issue-45157.stderr b/src/test/ui/issues/issue-45157.stderr
index 038d6ecf48d..3b15a8dbd9e 100644
--- a/src/test/ui/issues/issue-45157.stderr
+++ b/src/test/ui/issues/issue-45157.stderr
@@ -1,14 +1,16 @@
-error[E0502]: cannot borrow `u.z.c` as immutable because it is also borrowed as mutable
-  --> $DIR/issue-45157.rs:27:20
+error[E0502]: cannot borrow `u` (via `u.z.c`) as immutable because it is also borrowed as mutable (via `u.s.a`)
+  --> $DIR/issue-45157.rs:29:20
    |
 LL |         let mref = &mut u.s.a;
-   |                    ---------- mutable borrow occurs here
+   |                    ---------- mutable borrow occurs here (via `u.s.a`)
 ...
 LL |         let nref = &u.z.c;
-   |                    ^^^^^^ immutable borrow occurs here
-LL |         //~^ ERROR cannot borrow `u.z.c` as immutable because it is also borrowed as mutable [E0502]
+   |                    ^^^^^^ immutable borrow of `u.z.c` -- which overlaps with `u.s.a` -- occurs here
+LL |         //~^ ERROR cannot borrow `u` (via `u.z.c`) as immutable because it is also borrowed as mutable (via `u.s.a`) [E0502]
 LL |         println!("{} {}", mref, nref)
    |                           ---- mutable borrow later used here
+   |
+   = note: `u.z.c` is a field of the union `U`, so it overlaps the field `u.s.a`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/nll/issue-57100.rs b/src/test/ui/nll/issue-57100.rs
new file mode 100644
index 00000000000..f669fe00956
--- /dev/null
+++ b/src/test/ui/nll/issue-57100.rs
@@ -0,0 +1,69 @@
+#![allow(unused)]
+#![feature(nll)]
+
+// ignore-tidy-linelength
+
+// This tests the error messages for borrows of union fields when the unions are embedded in other
+// structs or unions.
+
+#[derive(Clone, Copy, Default)]
+struct Leaf {
+    l1_u8: u8,
+    l2_u8: u8,
+}
+
+#[derive(Clone, Copy)]
+union First {
+    f1_leaf: Leaf,
+    f2_leaf: Leaf,
+    f3_union: Second,
+}
+
+#[derive(Clone, Copy)]
+union Second {
+    s1_leaf: Leaf,
+    s2_leaf: Leaf,
+}
+
+struct Root {
+    r1_u8: u8,
+    r2_union: First,
+}
+
+// Borrow a different field of the nested union.
+fn nested_union() {
+    unsafe {
+        let mut r = Root {
+            r1_u8: 3,
+            r2_union: First { f3_union: Second { s2_leaf: Leaf { l1_u8: 8, l2_u8: 4 } } }
+        };
+
+        let mref = &mut r.r2_union.f3_union.s1_leaf.l1_u8;
+        //                                  ^^^^^^^
+        *mref = 22;
+        let nref = &r.r2_union.f3_union.s2_leaf.l1_u8;
+        //                              ^^^^^^^
+        //~^^ ERROR cannot borrow `r.r2_union.f3_union` (via `r.r2_union.f3_union.s2_leaf.l1_u8`) as immutable because it is also borrowed as mutable (via `r.r2_union.f3_union.s1_leaf.l1_u8`) [E0502]
+        println!("{} {}", mref, nref)
+    }
+}
+
+// Borrow a different field of the first union.
+fn first_union() {
+    unsafe {
+        let mut r = Root {
+            r1_u8: 3,
+            r2_union: First { f3_union: Second { s2_leaf: Leaf { l1_u8: 8, l2_u8: 4 } } }
+        };
+
+        let mref = &mut r.r2_union.f2_leaf.l1_u8;
+        //                         ^^^^^^^
+        *mref = 22;
+        let nref = &r.r2_union.f1_leaf.l1_u8;
+        //                     ^^^^^^^
+        //~^^ ERROR cannot borrow `r.r2_union` (via `r.r2_union.f1_leaf.l1_u8`) as immutable because it is also borrowed as mutable (via `r.r2_union.f2_leaf.l1_u8`) [E0502]
+        println!("{} {}", mref, nref)
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/nll/issue-57100.stderr b/src/test/ui/nll/issue-57100.stderr
new file mode 100644
index 00000000000..5d5c86c3487
--- /dev/null
+++ b/src/test/ui/nll/issue-57100.stderr
@@ -0,0 +1,31 @@
+error[E0502]: cannot borrow `r.r2_union.f3_union` (via `r.r2_union.f3_union.s2_leaf.l1_u8`) as immutable because it is also borrowed as mutable (via `r.r2_union.f3_union.s1_leaf.l1_u8`)
+  --> $DIR/issue-57100.rs:44:20
+   |
+LL |         let mref = &mut r.r2_union.f3_union.s1_leaf.l1_u8;
+   |                    -------------------------------------- mutable borrow occurs here (via `r.r2_union.f3_union.s1_leaf.l1_u8`)
+...
+LL |         let nref = &r.r2_union.f3_union.s2_leaf.l1_u8;
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow of `r.r2_union.f3_union.s2_leaf.l1_u8` -- which overlaps with `r.r2_union.f3_union.s1_leaf.l1_u8` -- occurs here
+...
+LL |         println!("{} {}", mref, nref)
+   |                           ---- mutable borrow later used here
+   |
+   = note: `r.r2_union.f3_union.s2_leaf.l1_u8` is a field of the union `Second`, so it overlaps the field `r.r2_union.f3_union.s1_leaf.l1_u8`
+
+error[E0502]: cannot borrow `r.r2_union` (via `r.r2_union.f1_leaf.l1_u8`) as immutable because it is also borrowed as mutable (via `r.r2_union.f2_leaf.l1_u8`)
+  --> $DIR/issue-57100.rs:62:20
+   |
+LL |         let mref = &mut r.r2_union.f2_leaf.l1_u8;
+   |                    ----------------------------- mutable borrow occurs here (via `r.r2_union.f2_leaf.l1_u8`)
+...
+LL |         let nref = &r.r2_union.f1_leaf.l1_u8;
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow of `r.r2_union.f1_leaf.l1_u8` -- which overlaps with `r.r2_union.f2_leaf.l1_u8` -- occurs here
+...
+LL |         println!("{} {}", mref, nref)
+   |                           ---- mutable borrow later used here
+   |
+   = note: `r.r2_union.f1_leaf.l1_u8` is a field of the union `First`, so it overlaps the field `r.r2_union.f2_leaf.l1_u8`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0502`.
diff --git a/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs b/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs
index 1ef15444790..e0184164b3a 100644
--- a/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs
+++ b/src/test/ui/rfc1598-generic-associated-types/streaming_iterator.rs
@@ -28,7 +28,7 @@ fn foo<T>(iter: T) where T: StreamingIterator, for<'a> T::Item<'a>: Display { /*
 
 // Full example of enumerate iterator
 
-#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+#[must_use = "iterators are lazy and do nothing unless consumed"]
 struct StreamEnumerate<I> {
     iter: I,
     count: usize,
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 953160db14e..848c3d9bdb0 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,12 +1,14 @@
-error[E0502]: cannot borrow `u.y` as immutable because it is also borrowed as mutable
+error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0`)
   --> $DIR/union-borrow-move-parent-sibling.rs:15:13
    |
 LL |     let a = &mut u.x.0;
-   |             ---------- mutable borrow occurs here
+   |             ---------- mutable borrow occurs here (via `u.x.0`)
 LL |     let b = &u.y; //~ ERROR cannot borrow `u.y`
-   |             ^^^^ immutable borrow occurs here
+   |             ^^^^ immutable borrow of `u.y` -- which overlaps with `u.x.0` -- occurs here
 LL |     use_borrow(a);
    |                - mutable borrow later used here
+   |
+   = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x.0`
 
 error[E0382]: use of moved value: `u`
   --> $DIR/union-borrow-move-parent-sibling.rs:22:13
@@ -18,15 +20,17 @@ LL |     let b = 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[E0502]: cannot borrow `u.y` as immutable because it is also borrowed as mutable
+error[E0502]: cannot borrow `u` (via `u.y`) as immutable because it is also borrowed as mutable (via `u.x.0.0`)
   --> $DIR/union-borrow-move-parent-sibling.rs:28:13
    |
 LL |     let a = &mut (u.x.0).0;
-   |             -------------- mutable borrow occurs here
+   |             -------------- mutable borrow occurs here (via `u.x.0.0`)
 LL |     let b = &u.y; //~ ERROR cannot borrow `u.y`
-   |             ^^^^ immutable borrow occurs here
+   |             ^^^^ immutable borrow of `u.y` -- which overlaps with `u.x.0.0` -- occurs here
 LL |     use_borrow(a);
    |                - mutable borrow later used here
+   |
+   = note: `u.y` is a field of the union `U`, so it overlaps the field `u.x.0.0`
 
 error[E0382]: use of moved value: `u`
   --> $DIR/union-borrow-move-parent-sibling.rs:35:13
@@ -38,15 +42,17 @@ LL |     let b = 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[E0502]: cannot borrow `u.x` as immutable because it is also borrowed as mutable
+error[E0502]: cannot borrow `u` (via `u.x`) as immutable because it is also borrowed as mutable (via `*u.y`)
   --> $DIR/union-borrow-move-parent-sibling.rs:41:13
    |
 LL |     let a = &mut *u.y;
-   |             --------- mutable borrow occurs here
+   |             --------- mutable borrow occurs here (via `*u.y`)
 LL |     let b = &u.x; //~ ERROR cannot borrow `u` (via `u.x`)
-   |             ^^^^ immutable borrow occurs here
+   |             ^^^^ immutable borrow of `u.x` -- which overlaps with `*u.y` -- occurs here
 LL |     use_borrow(a);
    |                - mutable borrow later used here
+   |
+   = note: `u.x` is a field of the union `U`, so it overlaps the field `*u.y`
 
 error[E0382]: use of moved value: `u`
   --> $DIR/union-borrow-move-parent-sibling.rs:48:13
diff --git a/src/test/ui/union/union-borrow-move-parent-sibling.stderr b/src/test/ui/union/union-borrow-move-parent-sibling.stderr
index c5baf82c745..9058707e505 100644
--- a/src/test/ui/union/union-borrow-move-parent-sibling.stderr
+++ b/src/test/ui/union/union-borrow-move-parent-sibling.stderr
@@ -46,7 +46,7 @@ error[E0502]: cannot borrow `u` (via `u.x`) as immutable because `u` is also bor
 LL |     let a = &mut *u.y;
    |                  ---- mutable borrow occurs here (via `*u.y`)
 LL |     let b = &u.x; //~ ERROR cannot borrow `u` (via `u.x`)
-   |              ^^^ immutable borrow occurs here (via `u.x`)
+   |              ^^^ immutable borrow of `u.x` -- which overlaps with `*u.y` -- occurs here
 LL |     use_borrow(a);
 LL | }
    | - mutable borrow ends here