From 9ee16e14c51668309f446636e8960b7fbbac066d Mon Sep 17 00:00:00 2001
From: Oliver Scherer <github35764891676564198441@oli-obk.de>
Date: Fri, 20 Dec 2019 23:15:50 +0100
Subject: [PATCH 01/21] Require const stability attributes on intrinsics to be
 able to use them in constant contexts

---
 src/libcore/intrinsics.rs                     | 29 +++++-
 src/libcore/lib.rs                            |  2 +
 src/libcore/macros/mod.rs                     |  1 +
 src/librustc/ty/constness.rs                  | 98 +------------------
 .../consts/const-eval/simd/insert_extract.rs  |  4 +
 src/test/ui/consts/const-fn-type-name.rs      |  1 +
 6 files changed, 38 insertions(+), 97 deletions(-)

diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs
index b02acce2d00bb..a194db52eb2a8 100644
--- a/src/libcore/intrinsics.rs
+++ b/src/libcore/intrinsics.rs
@@ -667,6 +667,7 @@ extern "rust-intrinsic" {
     ///
     /// The stabilized version of this intrinsic is
     /// [`std::mem::size_of`](../../std/mem/fn.size_of.html).
+    #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")]
     pub fn size_of<T>() -> usize;
 
     /// Moves a value to an uninitialized memory location.
@@ -674,7 +675,9 @@ extern "rust-intrinsic" {
     /// Drop glue is not run on the destination.
     pub fn move_val_init<T>(dst: *mut T, src: T);
 
+    #[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")]
     pub fn min_align_of<T>() -> usize;
+    #[rustc_const_unstable(feature = "const_pref_align_of", issue = "0")]
     pub fn pref_align_of<T>() -> usize;
 
     /// The size of the referenced value in bytes.
@@ -685,11 +688,13 @@ extern "rust-intrinsic" {
     pub fn min_align_of_val<T: ?Sized>(_: &T) -> usize;
 
     /// Gets a static string slice containing the name of a type.
+    #[rustc_const_unstable(feature = "const_type_name", issue = "0")]
     pub fn type_name<T: ?Sized>() -> &'static str;
 
     /// Gets an identifier which is globally unique to the specified type. This
     /// function will return the same value for a type regardless of whichever
     /// crate it is invoked in.
+    #[rustc_const_unstable(feature = "const_type_id", issue = "0")]
     pub fn type_id<T: ?Sized + 'static>() -> u64;
 
     /// A guard for unsafe functions that cannot ever be executed if `T` is uninhabited:
@@ -697,6 +702,7 @@ extern "rust-intrinsic" {
     pub fn panic_if_uninhabited<T>();
 
     /// Gets a reference to a static `Location` indicating where it was called.
+    #[rustc_const_unstable(feature = "const_caller_location", issue = "47809")]
     pub fn caller_location() -> &'static crate::panic::Location<'static>;
 
     /// Creates a value initialized to zero.
@@ -951,6 +957,7 @@ extern "rust-intrinsic" {
     ///
     /// The stabilized version of this intrinsic is
     /// [`std::mem::needs_drop`](../../std/mem/fn.needs_drop.html).
+    #[rustc_const_stable(feature = "const_needs_drop", since = "1.40.0")]
     pub fn needs_drop<T>() -> bool;
 
     /// Calculates the offset from a pointer.
@@ -1150,6 +1157,7 @@ extern "rust-intrinsic" {
 
 
     /// Returns the number of bits set in an integer type `T`
+    #[rustc_const_stable(feature = "const_ctpop", since = "1.40.0")]
     pub fn ctpop<T>(x: T) -> T;
 
     /// Returns the number of leading unset bits (zeroes) in an integer type `T`.
@@ -1177,6 +1185,7 @@ extern "rust-intrinsic" {
     /// let num_leading = ctlz(x);
     /// assert_eq!(num_leading, 16);
     /// ```
+    #[rustc_const_stable(feature = "const_ctlz", since = "1.40.0")]
     pub fn ctlz<T>(x: T) -> T;
 
     /// Like `ctlz`, but extra-unsafe as it returns `undef` when
@@ -1193,6 +1202,7 @@ extern "rust-intrinsic" {
     /// let num_leading = unsafe { ctlz_nonzero(x) };
     /// assert_eq!(num_leading, 3);
     /// ```
+    #[rustc_const_unstable(feature = "constctlz", issue = "0")]
     pub fn ctlz_nonzero<T>(x: T) -> T;
 
     /// Returns the number of trailing unset bits (zeroes) in an integer type `T`.
@@ -1220,6 +1230,7 @@ extern "rust-intrinsic" {
     /// let num_trailing = cttz(x);
     /// assert_eq!(num_trailing, 16);
     /// ```
+    #[rustc_const_stable(feature = "const_cttz", since = "1.40.0")]
     pub fn cttz<T>(x: T) -> T;
 
     /// Like `cttz`, but extra-unsafe as it returns `undef` when
@@ -1236,30 +1247,36 @@ extern "rust-intrinsic" {
     /// let num_trailing = unsafe { cttz_nonzero(x) };
     /// assert_eq!(num_trailing, 3);
     /// ```
+    #[rustc_const_unstable(feature = "const_cttz", issue = "0")]
     pub fn cttz_nonzero<T>(x: T) -> T;
 
     /// Reverses the bytes in an integer type `T`.
+    #[rustc_const_stable(feature = "const_bswap", since = "1.40.0")]
     pub fn bswap<T>(x: T) -> T;
 
     /// Reverses the bits in an integer type `T`.
+    #[rustc_const_stable(feature = "const_bitreverse", since = "1.40.0")]
     pub fn bitreverse<T>(x: T) -> T;
 
     /// Performs checked integer addition.
     /// The stabilized versions of this intrinsic are available on the integer
     /// primitives via the `overflowing_add` method. For example,
     /// [`std::u32::overflowing_add`](../../std/primitive.u32.html#method.overflowing_add)
+    #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")]
     pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
 
     /// Performs checked integer subtraction
     /// The stabilized versions of this intrinsic are available on the integer
     /// primitives via the `overflowing_sub` method. For example,
     /// [`std::u32::overflowing_sub`](../../std/primitive.u32.html#method.overflowing_sub)
+    #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")]
     pub fn sub_with_overflow<T>(x: T, y: T) -> (T, bool);
 
     /// Performs checked integer multiplication
     /// The stabilized versions of this intrinsic are available on the integer
     /// primitives via the `overflowing_mul` method. For example,
     /// [`std::u32::overflowing_mul`](../../std/primitive.u32.html#method.overflowing_mul)
+    #[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")]
     pub fn mul_with_overflow<T>(x: T, y: T) -> (T, bool);
 
     /// Performs an exact division, resulting in undefined behavior where
@@ -1275,9 +1292,11 @@ extern "rust-intrinsic" {
 
     /// Performs an unchecked left shift, resulting in undefined behavior when
     /// y < 0 or y >= N, where N is the width of T in bits.
+    #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")]
     pub fn unchecked_shl<T>(x: T, y: T) -> T;
     /// Performs an unchecked right shift, resulting in undefined behavior when
     /// y < 0 or y >= N, where N is the width of T in bits.
+    #[rustc_const_stable(feature = "const_int_unchecked", since = "1.40.0")]
     pub fn unchecked_shr<T>(x: T, y: T) -> T;
 
     /// Returns the result of an unchecked addition, resulting in
@@ -1296,39 +1315,46 @@ extern "rust-intrinsic" {
     /// The stabilized versions of this intrinsic are available on the integer
     /// primitives via the `rotate_left` method. For example,
     /// [`std::u32::rotate_left`](../../std/primitive.u32.html#method.rotate_left)
+    #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")]
     pub fn rotate_left<T>(x: T, y: T) -> T;
 
     /// Performs rotate right.
     /// The stabilized versions of this intrinsic are available on the integer
     /// primitives via the `rotate_right` method. For example,
-    /// [`std::u32::rotate_right`](../../std/primitive.u32.html#method.rotate_right)
+    /// [`std::u32::rotate_right`](../../std/primitive.u32.html#method.rotate_right
+    #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")]
     pub fn rotate_right<T>(x: T, y: T) -> T;
 
     /// Returns (a + b) mod 2<sup>N</sup>, where N is the width of T in bits.
     /// The stabilized versions of this intrinsic are available on the integer
     /// primitives via the `wrapping_add` method. For example,
     /// [`std::u32::wrapping_add`](../../std/primitive.u32.html#method.wrapping_add)
+    #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")]
     pub fn wrapping_add<T>(a: T, b: T) -> T;
     /// Returns (a - b) mod 2<sup>N</sup>, where N is the width of T in bits.
     /// The stabilized versions of this intrinsic are available on the integer
     /// primitives via the `wrapping_sub` method. For example,
     /// [`std::u32::wrapping_sub`](../../std/primitive.u32.html#method.wrapping_sub)
+    #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")]
     pub fn wrapping_sub<T>(a: T, b: T) -> T;
     /// Returns (a * b) mod 2<sup>N</sup>, where N is the width of T in bits.
     /// The stabilized versions of this intrinsic are available on the integer
     /// primitives via the `wrapping_mul` method. For example,
     /// [`std::u32::wrapping_mul`](../../std/primitive.u32.html#method.wrapping_mul)
+    #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")]
     pub fn wrapping_mul<T>(a: T, b: T) -> T;
 
     /// Computes `a + b`, while saturating at numeric bounds.
     /// The stabilized versions of this intrinsic are available on the integer
     /// primitives via the `saturating_add` method. For example,
     /// [`std::u32::saturating_add`](../../std/primitive.u32.html#method.saturating_add)
+    #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")]
     pub fn saturating_add<T>(a: T, b: T) -> T;
     /// Computes `a - b`, while saturating at numeric bounds.
     /// The stabilized versions of this intrinsic are available on the integer
     /// primitives via the `saturating_sub` method. For example,
     /// [`std::u32::saturating_sub`](../../std/primitive.u32.html#method.saturating_sub)
+    #[rustc_const_stable(feature = "const_int_saturating", since = "1.40.0")]
     pub fn saturating_sub<T>(a: T, b: T) -> T;
 
     /// Returns the value of the discriminant for the variant in 'v',
@@ -1350,6 +1376,7 @@ extern "rust-intrinsic" {
     pub fn nontemporal_store<T>(ptr: *mut T, val: T);
 
     /// See documentation of `<*const T>::offset_from` for details.
+    #[rustc_const_unstable(feature = "const_ptr_offset_from", issue = "0")]
     pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
 
     /// Internal hook used by Miri to implement unwinding.
diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs
index a2ab85e64baa1..74dc56c156aea 100644
--- a/src/libcore/lib.rs
+++ b/src/libcore/lib.rs
@@ -128,6 +128,8 @@
 #![feature(maybe_uninit_slice)]
 #![feature(external_doc)]
 #![feature(associated_type_bounds)]
+#![feature(const_type_id)]
+#![feature(const_caller_location)]
 
 #[prelude_import]
 #[allow(unused)]
diff --git a/src/libcore/macros/mod.rs b/src/libcore/macros/mod.rs
index dd06da7a6d23e..b1475f2ad9fc4 100644
--- a/src/libcore/macros/mod.rs
+++ b/src/libcore/macros/mod.rs
@@ -5,6 +5,7 @@
     // the `caller_location` intrinsic, but once  `#[track_caller]` is implemented,
     // `panicking::{panic, panic_fmt}` can use that instead of a `Location` argument.
     core_intrinsics,
+    const_caller_location,
 )]
 #[stable(feature = "core", since = "1.6.0")]
 macro_rules! panic {
diff --git a/src/librustc/ty/constness.rs b/src/librustc/ty/constness.rs
index 897a3678c4004..7fe950ef7b7f6 100644
--- a/src/librustc/ty/constness.rs
+++ b/src/librustc/ty/constness.rs
@@ -2,7 +2,7 @@ use crate::ty::query::Providers;
 use crate::hir::def_id::DefId;
 use crate::hir;
 use crate::ty::TyCtxt;
-use syntax_pos::symbol::{sym, Symbol};
+use syntax_pos::symbol::Symbol;
 use rustc_target::spec::abi::Abi;
 use crate::hir::map::blocks::FnLikeNode;
 use syntax::attr;
@@ -41,51 +41,12 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
-    /// Returns `true` if the `def_id` refers to an intrisic which we've whitelisted
-    /// for being called from stable `const fn`s (`min_const_fn`).
-    ///
-    /// Adding more intrinsics requires sign-off from @rust-lang/lang.
-    ///
-    /// This list differs from the list in `is_const_intrinsic` in the sense that any item on this
-    /// list must be on the `is_const_intrinsic` list, too, because if an intrinsic is callable from
-    /// stable, it must be callable at all.
-    fn is_intrinsic_min_const_fn(self, def_id: DefId) -> bool {
-        match self.item_name(def_id) {
-            | sym::size_of
-            | sym::min_align_of
-            | sym::needs_drop
-            // Arithmetic:
-            | sym::add_with_overflow // ~> .overflowing_add
-            | sym::sub_with_overflow // ~> .overflowing_sub
-            | sym::mul_with_overflow // ~> .overflowing_mul
-            | sym::wrapping_add // ~> .wrapping_add
-            | sym::wrapping_sub // ~> .wrapping_sub
-            | sym::wrapping_mul // ~> .wrapping_mul
-            | sym::saturating_add // ~> .saturating_add
-            | sym::saturating_sub // ~> .saturating_sub
-            | sym::unchecked_shl // ~> .wrapping_shl
-            | sym::unchecked_shr // ~> .wrapping_shr
-            | sym::rotate_left // ~> .rotate_left
-            | sym::rotate_right // ~> .rotate_right
-            | sym::ctpop // ~> .count_ones
-            | sym::ctlz // ~> .leading_zeros
-            | sym::cttz // ~> .trailing_zeros
-            | sym::bswap // ~> .swap_bytes
-            | sym::bitreverse // ~> .reverse_bits
-            => true,
-            _ => false,
-        }
-    }
-
     /// Returns `true` if this function must conform to `min_const_fn`
     pub fn is_min_const_fn(self, def_id: DefId) -> bool {
         // Bail out if the signature doesn't contain `const`
         if !self.is_const_fn_raw(def_id) {
             return false;
         }
-        if let Abi::RustIntrinsic = self.fn_sig(def_id).abi() {
-            return self.is_intrinsic_min_const_fn(def_id);
-        }
 
         if self.features().staged_api {
             // In order for a libstd function to be considered min_const_fn
@@ -134,62 +95,7 @@ pub fn provide(providers: &mut Providers<'_>) {
     fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
         match tcx.fn_sig(def_id).abi() {
             Abi::RustIntrinsic |
-            Abi::PlatformIntrinsic => {
-                // FIXME: deduplicate these two lists as much as possible
-                match tcx.item_name(def_id) {
-                    // Keep this list in the same order as the match patterns in
-                    // `librustc_mir/interpret/intrinsics.rs`
-
-                    // This whitelist is a list of intrinsics that have a miri-engine implementation
-                    // and can thus be called when enabling enough feature gates. The similar
-                    // whitelist in `is_intrinsic_min_const_fn` (in this file), exists for allowing
-                    // the intrinsics to be called by stable const fns.
-                    | sym::caller_location
-
-                    | sym::min_align_of
-                    | sym::pref_align_of
-                    | sym::needs_drop
-                    | sym::size_of
-                    | sym::type_id
-                    | sym::type_name
-
-                    | sym::ctpop
-                    | sym::cttz
-                    | sym::cttz_nonzero
-                    | sym::ctlz
-                    | sym::ctlz_nonzero
-                    | sym::bswap
-                    | sym::bitreverse
-
-                    | sym::wrapping_add
-                    | sym::wrapping_sub
-                    | sym::wrapping_mul
-                    | sym::add_with_overflow
-                    | sym::sub_with_overflow
-                    | sym::mul_with_overflow
-
-                    | sym::saturating_add
-                    | sym::saturating_sub
-
-                    | sym::unchecked_shl
-                    | sym::unchecked_shr
-
-                    | sym::rotate_left
-                    | sym::rotate_right
-
-                    | sym::ptr_offset_from
-
-                    | sym::transmute
-
-                    | sym::simd_insert
-
-                    | sym::simd_extract
-
-                    => Some(true),
-
-                    _ => Some(false)
-                }
-            }
+            Abi::PlatformIntrinsic => Some(tcx.lookup_const_stability(def_id).is_some()),
             _ => None
         }
     }
diff --git a/src/test/ui/consts/const-eval/simd/insert_extract.rs b/src/test/ui/consts/const-eval/simd/insert_extract.rs
index d3462d802ea4e..92231d4ced321 100644
--- a/src/test/ui/consts/const-eval/simd/insert_extract.rs
+++ b/src/test/ui/consts/const-eval/simd/insert_extract.rs
@@ -2,6 +2,8 @@
 #![feature(const_fn)]
 #![feature(repr_simd)]
 #![feature(platform_intrinsics)]
+#![feature(staged_api)]
+#![stable(feature = "foo", since = "1.33.7")]
 #![allow(non_camel_case_types)]
 
 #[repr(simd)] struct i8x1(i8);
@@ -9,7 +11,9 @@
 #[repr(simd)] struct f32x3(f32, f32, f32);
 
 extern "platform-intrinsic" {
+    #[rustc_const_stable(feature = "foo", since = "1.3.37")]
     fn simd_insert<T, U>(x: T, idx: u32, val: U) -> T;
+    #[rustc_const_stable(feature = "foo", since = "1.3.37")]
     fn simd_extract<T, U>(x: T, idx: u32) -> U;
 }
 
diff --git a/src/test/ui/consts/const-fn-type-name.rs b/src/test/ui/consts/const-fn-type-name.rs
index 2bb1aeecf376d..72fac19c19173 100644
--- a/src/test/ui/consts/const-fn-type-name.rs
+++ b/src/test/ui/consts/const-fn-type-name.rs
@@ -2,6 +2,7 @@
 
 #![feature(core_intrinsics)]
 #![feature(const_fn)]
+#![feature(const_type_name)]
 #![allow(dead_code)]
 
 const fn type_name_wrapper<T>(_: &T) -> &'static str {

From 032d810e24a39aad311c15d05dfef4dc4d6829c4 Mon Sep 17 00:00:00 2001
From: Oliver Scherer <github35764891676564198441@oli-obk.de>
Date: Sat, 21 Dec 2019 00:07:36 +0100
Subject: [PATCH 02/21] Add a big notice about const intrinsics

---
 src/libcore/intrinsics.rs | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs
index a194db52eb2a8..544fd4fb99fa1 100644
--- a/src/libcore/intrinsics.rs
+++ b/src/libcore/intrinsics.rs
@@ -1,6 +1,22 @@
 //! Compiler intrinsics.
 //!
 //! The corresponding definitions are in `librustc_codegen_llvm/intrinsic.rs`.
+//! The corresponding const implementations are in `librustc_mir/interpret/intrinsics.rs`
+//!
+//! # Const intrinsics
+//!
+//! Note: any changes to the constness of intrinsics should be discussed with the language team.
+//! This includes changes in the stability of the constness.
+//!
+//! In order to make an intrinsic usable at compile-time, one needs to copy the implementation
+//! from https://github.com/rust-lang/miri/blob/master/src/shims/intrinsics.rs to
+//! `librustc_mir/interpret/intrinsics.rs` and add a
+//! `#[rustc_const_unstable(feature = "foo", issue = "01234")]` to the intrinsic.
+//!
+//! If an intrinsic is supposed to be used from a `const fn` with a `rustc_const_stable` attribute,
+//! the intrinsic's attribute must be `rustc_const_stable`, too. Such a change should not be done
+//! without T-lang consulation, because it bakes a feature into the language that cannot be
+//! replicated in user code without compiler support.
 //!
 //! # Volatiles
 //!

From 89986a39a8236a3e4bfbb74b4d3a0a03667e8b56 Mon Sep 17 00:00:00 2001
From: Luro02 <24826124+Luro02@users.noreply.github.com>
Date: Wed, 11 Dec 2019 17:59:32 +0100
Subject: [PATCH 03/21] add partialeq and eq to cursor

---
 src/libstd/io/cursor.rs | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs
index a94176e710005..1cdedd859d338 100644
--- a/src/libstd/io/cursor.rs
+++ b/src/libstd/io/cursor.rs
@@ -72,7 +72,7 @@ use core::convert::TryInto;
 /// }
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
-#[derive(Clone, Debug, Default)]
+#[derive(Clone, Debug, Default, Eq, PartialEq)]
 pub struct Cursor<T> {
     inner: T,
     pos: u64,
@@ -902,4 +902,16 @@ mod tests {
         c.set_position(<usize>::max_value() as u64 + 1);
         assert!(c.write_all(&[1, 2, 3]).is_err());
     }
+
+    #[test]
+    fn test_partial_eq() {
+        assert_eq!(Cursor::new(Vec::<u8>::new()), Cursor::new(Vec::<u8>::new()));
+    }
+
+    #[test]
+    fn test_eq() {
+        struct AssertEq<T: Eq>(pub T);
+
+        let _: AssertEq<Cursor<Vec<u8>>> = AssertEq(Cursor::new(Vec::new()));
+    }
 }

From 4ce2384501833127fa3c9e995240d78b07f0ab2f Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Sun, 22 Dec 2019 14:14:37 +0100
Subject: [PATCH 04/21] Improve JS code a bit by avoid erasing all event
 handlers

---
 src/librustdoc/html/static/main.js | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index 1459e8f37cd32..ad988557f9bcc 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -324,6 +324,7 @@ function getSearchElement() {
     }
 
     function handleEscape(ev) {
+        debugger;
         var help = getHelpElement();
         var search = getSearchElement();
         hideModal();
@@ -390,8 +391,8 @@ function getSearchElement() {
         return null;
     }
 
-    document.onkeypress = handleShortcut;
-    document.onkeydown = handleShortcut;
+    document.addEventListener("keypress", handleShortcut);
+    document.addEventListener("keydown", handleShortcut);
 
     var handleSourceHighlight = (function() {
         var prev_line_id = 0;
@@ -430,7 +431,7 @@ function getSearchElement() {
         }
     })();
 
-    document.onclick = function(ev) {
+    document.addEventListener("click", function(ev) {
         if (hasClass(ev.target, "collapse-toggle")) {
             collapseDocs(ev.target, "toggle");
         } else if (hasClass(ev.target.parentNode, "collapse-toggle")) {
@@ -452,7 +453,7 @@ function getSearchElement() {
                 expandSection(a.hash.replace(/^#/, ""));
             }
         }
-    };
+    });
 
     var x = document.getElementsByClassName("version-selector");
     if (x.length > 0) {

From b67701347abd3469a92577cfe748206ecfc25694 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Sun, 22 Dec 2019 15:42:10 +0100
Subject: [PATCH 05/21] Improve code readability

---
 src/librustdoc/html/static/main.js | 80 ++++++++++++++----------------
 1 file changed, 37 insertions(+), 43 deletions(-)

diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index ad988557f9bcc..d0c44cd40b21a 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -138,6 +138,22 @@ function getSearchElement() {
         }
     }
 
+    function showSearchResults(search) {
+        if (search === null || typeof search === 'undefined') {
+            search = getSearchElement();
+        }
+        addClass(main, "hidden");
+        removeClass(search, "hidden");
+    }
+
+    function hideSearchResults(search) {
+        if (search === null || typeof search === 'undefined') {
+            search = getSearchElement();
+        }
+        addClass(search, "hidden");
+        removeClass(main, "hidden");
+    }
+
     // used for special search precedence
     var TY_PRIMITIVE = itemTypes.indexOf("primitive");
     var TY_KEYWORD = itemTypes.indexOf("keyword");
@@ -169,8 +185,7 @@ function getSearchElement() {
         if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) {
             // This block occurs when clicking on an element in the navbar while
             // in a search.
-            addClass(search, "hidden");
-            removeClass(main, "hidden");
+            hideSearchResults(search);
             var hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);
             if (browserSupportsHistoryApi()) {
                 history.replaceState(hash, "", "?search=#" + hash);
@@ -324,7 +339,6 @@ function getSearchElement() {
     }
 
     function handleEscape(ev) {
-        debugger;
         var help = getHelpElement();
         var search = getSearchElement();
         hideModal();
@@ -332,8 +346,7 @@ function getSearchElement() {
             displayHelp(false, ev, help);
         } else if (hasClass(search, "hidden") === false) {
             ev.preventDefault();
-            addClass(search, "hidden");
-            removeClass(main, "hidden");
+            hideSearchResults(search);
             document.title = titleBeforeSearch;
         }
         defocusSearchBar();
@@ -1265,8 +1278,7 @@ function getSearchElement() {
                 }
                 dst = dst[0];
                 if (window.location.pathname === dst.pathname) {
-                    addClass(getSearchElement(), "hidden");
-                    removeClass(main, "hidden");
+                    hideSearchResults();
                     document.location.href = dst.href;
                 }
             };
@@ -1341,8 +1353,6 @@ function getSearchElement() {
                     e.preventDefault();
                 } else if (e.which === 16) { // shift
                     // Does nothing, it's just to avoid losing "focus" on the highlighted element.
-                } else if (e.which === 27) { // escape
-                    handleEscape(e);
                 } else if (actives[currentTab].length > 0) {
                     removeClass(actives[currentTab][0], "highlighted");
                 }
@@ -1492,10 +1502,9 @@ function getSearchElement() {
                 "</div><div id=\"results\">" +
                 ret_others[0] + ret_in_args[0] + ret_returned[0] + "</div>";
 
-            addClass(main, "hidden");
             var search = getSearchElement();
-            removeClass(search, "hidden");
             search.innerHTML = output;
+            showSearchResults(search);
             var tds = search.getElementsByTagName("td");
             var td_width = 0;
             if (tds.length > 0) {
@@ -1700,13 +1709,7 @@ function getSearchElement() {
                     if (browserSupportsHistoryApi()) {
                         history.replaceState("", window.currentCrate + " - Rust", "?search=");
                     }
-                    if (hasClass(main, "content")) {
-                        removeClass(main, "hidden");
-                    }
-                    var search_c = getSearchElement();
-                    if (hasClass(search_c, "content")) {
-                        addClass(search_c, "hidden");
-                    }
+                    hideSearchResults();
                 } else {
                     searchTimeout = setTimeout(search, 500);
                 }
@@ -1742,19 +1745,8 @@ function getSearchElement() {
                 // Store the previous <title> so we can revert back to it later.
                 var previousTitle = document.title;
 
-                window.onpopstate = function(e) {
+                window.addEventListener("popstate", function(e) {
                     var params = getQueryStringParams();
-                    // When browsing back from search results the main page
-                    // visibility must be reset.
-                    if (!params.search) {
-                        if (hasClass(main, "content")) {
-                            removeClass(main, "hidden");
-                        }
-                        var search_c = getSearchElement();
-                        if (hasClass(search_c, "content")) {
-                            addClass(search_c, "hidden");
-                        }
-                    }
                     // Revert to the previous title manually since the History
                     // API ignores the title parameter.
                     document.title = previousTitle;
@@ -1766,18 +1758,21 @@ function getSearchElement() {
                     // perform the search. This will empty the bar if there's
                     // nothing there, which lets you really go back to a
                     // previous state with nothing in the bar.
-                    if (params.search) {
+                    if (params.search && params.search.length > 0) {
                         search_input.value = params.search;
+                        // Some browsers fire "onpopstate" for every page load
+                        // (Chrome), while others fire the event only when actually
+                        // popping a state (Firefox), which is why search() is
+                        // called both here and at the end of the startSearch()
+                        // function.
+                        search(e);
                     } else {
                         search_input.value = "";
+                        // When browsing back from search results the main page
+                        // visibility must be reset.
+                        hideSearchResults();
                     }
-                    // Some browsers fire "onpopstate" for every page load
-                    // (Chrome), while others fire the event only when actually
-                    // popping a state (Firefox), which is why search() is
-                    // called both here and at the end of the startSearch()
-                    // function.
-                    search();
-                };
+                });
             }
             search();
         }
@@ -2523,9 +2518,9 @@ function getSearchElement() {
     }
 
     function putBackSearch(search_input) {
-        if (search_input.value !== "") {
-            addClass(main, "hidden");
-            removeClass(getSearchElement(), "hidden");
+        var search = getSearchElement();
+        if (search_input.value !== "" && hasClass(search, "hidden")) {
+            showSearchResults(search);
             if (browserSupportsHistoryApi()) {
                 history.replaceState(search_input.value,
                                      "",
@@ -2542,10 +2537,9 @@ function getSearchElement() {
 
     var params = getQueryStringParams();
     if (params && params.search) {
-        addClass(main, "hidden");
         var search = getSearchElement();
-        removeClass(search, "hidden");
         search.innerHTML = "<h3 style=\"text-align: center;\">Loading search results...</h3>";
+        showSearchResults(search);
     }
 
     var sidebar_menu = document.getElementsByClassName("sidebar-menu")[0];

From 71ff18fb8965712cc0a35a9d968cdc41c6561386 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Sun, 22 Dec 2019 15:42:29 +0100
Subject: [PATCH 06/21] Fix invalid results showing back

---
 src/librustdoc/html/static/main.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index d0c44cd40b21a..8ccb74d6f15d3 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -1722,6 +1722,10 @@ function getSearchElement() {
                 search();
             };
             search_input.onchange = function(e) {
+                if (e.target !== document.activeElement) {
+                    // To prevent doing anything when it's from a blur event.
+                    return;
+                }
                 // Do NOT e.preventDefault() here. It will prevent pasting.
                 clearTimeout(searchTimeout);
                 // zero-timeout necessary here because at the time of event handler execution the

From 056dff5748270249d81a03b355bc7ddd8da7f900 Mon Sep 17 00:00:00 2001
From: Oliver Scherer <github35764891676564198441@oli-obk.de>
Date: Mon, 23 Dec 2019 01:24:11 +0100
Subject: [PATCH 07/21] Fix ICE in mir interpretation

---
 src/librustc_mir/interpret/place.rs            | 3 ++-
 src/test/ui/consts/const_prop_slice_pat_ice.rs | 9 +++++++++
 2 files changed, 11 insertions(+), 1 deletion(-)
 create mode 100644 src/test/ui/consts/const_prop_slice_pat_ice.rs

diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs
index 8923b167fdee8..6e7fa302abcbb 100644
--- a/src/librustc_mir/interpret/place.rs
+++ b/src/librustc_mir/interpret/place.rs
@@ -530,11 +530,12 @@ where
                     // This can only be reached in ConstProp and non-rustc-MIR.
                     throw_ub!(BoundsCheckFailed { len: min_length as u64, index: n as u64 });
                 }
-                assert!(offset < min_length);
 
                 let index = if from_end {
+                    assert!(offset - 1 < min_length);
                     n - u64::from(offset)
                 } else {
+                    assert!(offset < min_length);
                     u64::from(offset)
                 };
 
diff --git a/src/test/ui/consts/const_prop_slice_pat_ice.rs b/src/test/ui/consts/const_prop_slice_pat_ice.rs
new file mode 100644
index 0000000000000..5fec36e44bd8c
--- /dev/null
+++ b/src/test/ui/consts/const_prop_slice_pat_ice.rs
@@ -0,0 +1,9 @@
+// check-pass
+#![feature(slice_patterns)]
+
+fn main() {
+    match &[0, 1] as &[i32] {
+        [a @ .., x] => {}
+        &[] => {}
+    }
+}

From c205f6a06a8360367a7f6242f0d2615e94824024 Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Sat, 21 Dec 2019 21:43:13 -0500
Subject: [PATCH 08/21] Remove mem::uninitalized from tests

This purges uses of uninitialized where possible from test cases. Some
are merely moved over to the equally bad pattern of
MaybeUninit::uninit().assume_init() but with an annotation that this is
"the best we can do".
---
 .../run-make-fulldeps/sanitizer-memory/Makefile |  2 ++
 .../sanitizer-memory/maybeuninit.rs             |  8 ++++++++
 .../sanitizer-memory/uninit.rs                  |  6 +++---
 src/test/rustdoc/issue-52873.rs                 |  5 ++---
 src/test/ui/abi/stack-probes.rs                 | 15 ++++++++-------
 .../ui/const-generics/issues/issue-61422.rs     | 12 ++++++++++++
 .../ui/for-loop-while/for-loop-has-unit-body.rs |  4 ++--
 src/test/ui/issues/issue-48131.rs               |  6 +++---
 src/test/ui/issues/issue-58212.rs               |  9 ++++-----
 .../enum-non-c-like-repr-c-and-int.rs           |  6 ++++--
 .../ui/structs-enums/enum-non-c-like-repr-c.rs  |  7 +++++--
 .../structs-enums/enum-non-c-like-repr-int.rs   |  6 ++++--
 .../uninhabited-matches-feature-gated.rs        | 17 ++++++++++-------
 .../uninhabited-matches-feature-gated.stderr    | 14 +++++++-------
 src/test/ui/uninit-empty-types.rs               | 12 +++++++-----
 15 files changed, 81 insertions(+), 48 deletions(-)
 create mode 100644 src/test/run-make-fulldeps/sanitizer-memory/maybeuninit.rs

diff --git a/src/test/run-make-fulldeps/sanitizer-memory/Makefile b/src/test/run-make-fulldeps/sanitizer-memory/Makefile
index b3376f8a72358..f5787903a2b59 100644
--- a/src/test/run-make-fulldeps/sanitizer-memory/Makefile
+++ b/src/test/run-make-fulldeps/sanitizer-memory/Makefile
@@ -7,3 +7,5 @@
 all:
 	$(RUSTC) -g -Z sanitizer=memory -Z print-link-args uninit.rs | $(CGREP) librustc_msan
 	$(TMPDIR)/uninit 2>&1 | $(CGREP) use-of-uninitialized-value
+	$(RUSTC) -g -Z sanitizer=memory -Z print-link-args maybeuninit.rs | $(CGREP) librustc_msan
+	$(TMPDIR)/maybeuninit 2>&1 | $(CGREP) use-of-uninitialized-value
diff --git a/src/test/run-make-fulldeps/sanitizer-memory/maybeuninit.rs b/src/test/run-make-fulldeps/sanitizer-memory/maybeuninit.rs
new file mode 100644
index 0000000000000..a9ae85f57639e
--- /dev/null
+++ b/src/test/run-make-fulldeps/sanitizer-memory/maybeuninit.rs
@@ -0,0 +1,8 @@
+use std::mem::MaybeUninit;
+
+fn main() {
+    // This is technically not sound -- but we're literally trying to test
+    // that the sanitizer catches this, so I guess "intentionally unsound"?
+    let xs: [u8; 4] = unsafe { MaybeUninit::uninit().assume_init() };
+    let y = xs[0] + xs[1];
+}
diff --git a/src/test/run-make-fulldeps/sanitizer-memory/uninit.rs b/src/test/run-make-fulldeps/sanitizer-memory/uninit.rs
index cb857e3bc38d3..eae52508f6585 100644
--- a/src/test/run-make-fulldeps/sanitizer-memory/uninit.rs
+++ b/src/test/run-make-fulldeps/sanitizer-memory/uninit.rs
@@ -1,7 +1,7 @@
-use std::mem;
-
 fn main() {
+    // This is technically not sound -- but we're literally trying to test
+    // that the sanitizer catches this, so I guess "intentionally unsound"?
     #[allow(deprecated)]
-    let xs: [u8; 4] = unsafe { mem::uninitialized() };
+    let xs: [u8; 4] = unsafe { std::mem::uninitialized() };
     let y = xs[0] + xs[1];
 }
diff --git a/src/test/rustdoc/issue-52873.rs b/src/test/rustdoc/issue-52873.rs
index 653c004c04b23..8000ce73bd42f 100644
--- a/src/test/rustdoc/issue-52873.rs
+++ b/src/test/rustdoc/issue-52873.rs
@@ -105,8 +105,7 @@ impl<U: Unsigned, B: Bit> Add<B0> for UInt<U, B> {
 impl<U: Unsigned> Add<U> for UTerm {
     type Output = U;
     fn add(self, _: U) -> Self::Output {
-        #[allow(deprecated)]
-        unsafe { ::std::mem::uninitialized() }
+        unimplemented!()
     }
 }
 
@@ -137,7 +136,7 @@ where
 {
     type Output = UInt<Prod<Ul, UInt<Ur, B>>, B0>;
     fn mul(self, _: UInt<Ur, B>) -> Self::Output {
-        unsafe { ::std::mem::uninitialized() }
+        unimplemented!()
     }
 }
 
diff --git a/src/test/ui/abi/stack-probes.rs b/src/test/ui/abi/stack-probes.rs
index 1ab1d6df66d4b..1d5b362e29dfe 100644
--- a/src/test/ui/abi/stack-probes.rs
+++ b/src/test/ui/abi/stack-probes.rs
@@ -13,7 +13,7 @@
 // ignore-sgx no processes
 // ignore-musl FIXME #31506
 
-use std::mem;
+use std::mem::MaybeUninit;
 use std::process::Command;
 use std::thread;
 use std::env;
@@ -28,8 +28,8 @@ fn main() {
     let args = env::args().skip(1).collect::<Vec<_>>();
     if args.len() > 0 {
         match &args[0][..] {
-            "main-thread" => recurse(&[]),
-            "child-thread" => thread::spawn(|| recurse(&[])).join().unwrap(),
+            "main-thread" => recurse(&MaybeUninit::uninit()),
+            "child-thread" => thread::spawn(|| recurse(&MaybeUninit::uninit())).join().unwrap(),
             _ => panic!(),
         }
         return
@@ -48,10 +48,11 @@ fn main() {
 }
 
 #[allow(unconditional_recursion)]
-fn recurse(array: &[u64]) {
-    unsafe { black_box(array.as_ptr() as u64); }
-    #[allow(deprecated)]
-    let local: [_; 1024] = unsafe { mem::uninitialized() };
+fn recurse(array: &MaybeUninit<[u64; 1024]>) {
+    unsafe {
+        black_box(array.as_ptr() as u64);
+    }
+    let local: MaybeUninit<[u64; 1024]> = MaybeUninit::uninit();
     recurse(&local);
 }
 
diff --git a/src/test/ui/const-generics/issues/issue-61422.rs b/src/test/ui/const-generics/issues/issue-61422.rs
index 45d37b6a2f3c5..4fa150ffef09e 100644
--- a/src/test/ui/const-generics/issues/issue-61422.rs
+++ b/src/test/ui/const-generics/issues/issue-61422.rs
@@ -5,6 +5,10 @@
 
 use std::mem;
 
+// Neither of the uninits below are currently accepted as not UB, however,
+// this code does not run and is merely checking that we do not ICE on this pattern,
+// so this is fine.
+
 fn foo<const SIZE: usize>() {
     let arr: [u8; SIZE] = unsafe {
         #[allow(deprecated)]
@@ -13,4 +17,12 @@ fn foo<const SIZE: usize>() {
     };
 }
 
+fn bar<const SIZE: usize>() {
+    let arr: [u8; SIZE] = unsafe {
+        let array: [u8; SIZE] = mem::MaybeUninit::uninit().assume_init();
+        array
+    };
+}
+
+
 fn main() {}
diff --git a/src/test/ui/for-loop-while/for-loop-has-unit-body.rs b/src/test/ui/for-loop-while/for-loop-has-unit-body.rs
index 38c34d2dc2e6f..eba385461b951 100644
--- a/src/test/ui/for-loop-while/for-loop-has-unit-body.rs
+++ b/src/test/ui/for-loop-while/for-loop-has-unit-body.rs
@@ -2,8 +2,8 @@
 fn main() {
     // Check that the tail statement in the body unifies with something
     for _ in 0..3 {
-        #[allow(deprecated)]
-        unsafe { std::mem::uninitialized() }
+        // `()` is fine to zero-initialize as it is zero sized and inhabited.
+        unsafe { std::mem::zeroed() }
     }
 
     // Check that the tail statement in the body can be unit
diff --git a/src/test/ui/issues/issue-48131.rs b/src/test/ui/issues/issue-48131.rs
index c8540729352b2..85664e62eaded 100644
--- a/src/test/ui/issues/issue-48131.rs
+++ b/src/test/ui/issues/issue-48131.rs
@@ -1,7 +1,7 @@
 // This note is annotated because the purpose of the test
 // is to ensure that certain other notes are not generated.
 #![deny(unused_unsafe)] //~ NOTE
-#![allow(deprecated)]
+
 
 // (test that no note is generated on this unsafe fn)
 pub unsafe fn a() {
@@ -20,8 +20,8 @@ pub fn b() {
             unsafe { /* unnecessary */ } //~ ERROR unnecessary `unsafe`
                                          //~^ NOTE
         }
-
-        let () = ::std::mem::uninitialized();
+        // `()` is fine to zero-initialize as it is zero sized and inhabited.
+        let () = ::std::mem::zeroed();
 
         inner()
     }
diff --git a/src/test/ui/issues/issue-58212.rs b/src/test/ui/issues/issue-58212.rs
index 21dcdd56bf60c..4695497c3a184 100644
--- a/src/test/ui/issues/issue-58212.rs
+++ b/src/test/ui/issues/issue-58212.rs
@@ -1,13 +1,12 @@
-// run-pass
+// check-pass
 
 trait FromUnchecked {
-    unsafe fn from_unchecked();
+    fn from_unchecked();
 }
 
 impl FromUnchecked for [u8; 1] {
-    unsafe fn from_unchecked() {
-        #[allow(deprecated)]
-        let mut array: Self = std::mem::uninitialized();
+    fn from_unchecked() {
+        let mut array: Self = [0; 1];
         let _ptr = &mut array as *mut [u8] as *mut u8;
     }
 }
diff --git a/src/test/ui/structs-enums/enum-non-c-like-repr-c-and-int.rs b/src/test/ui/structs-enums/enum-non-c-like-repr-c-and-int.rs
index 78d8e5e3a5dbf..7d15d607dd606 100644
--- a/src/test/ui/structs-enums/enum-non-c-like-repr-c-and-int.rs
+++ b/src/test/ui/structs-enums/enum-non-c-like-repr-c-and-int.rs
@@ -69,8 +69,10 @@ fn main() {
     unsafe {
         // This should be safe, because we don't match on it unless it's fully formed,
         // and it doesn't have a destructor.
-        #[allow(deprecated)]
-        let mut dest: MyEnum = mem::uninitialized();
+        //
+        // MyEnum is repr(C, u8) so it is guaranteed to have a separate discriminant and each
+        // variant can be zero initialized.
+        let mut dest: MyEnum = mem::zeroed();
         while buf.len() > 0 {
             match parse_my_enum(&mut dest, &mut buf) {
                 Ok(()) => output.push(Ok(dest)),
diff --git a/src/test/ui/structs-enums/enum-non-c-like-repr-c.rs b/src/test/ui/structs-enums/enum-non-c-like-repr-c.rs
index 1209533efda82..fc9efdeca7d19 100644
--- a/src/test/ui/structs-enums/enum-non-c-like-repr-c.rs
+++ b/src/test/ui/structs-enums/enum-non-c-like-repr-c.rs
@@ -69,8 +69,11 @@ fn main() {
     unsafe {
         // This should be safe, because we don't match on it unless it's fully formed,
         // and it doesn't have a destructor.
-        #[allow(deprecated)]
-        let mut dest: MyEnum = mem::uninitialized();
+        //
+        // Furthermore, there are no types within MyEnum which cannot be initialized with zero,
+        // specifically, though padding and such are present, there are no references or similar
+        // types.
+        let mut dest: MyEnum = mem::zeroed();
         while buf.len() > 0 {
             match parse_my_enum(&mut dest, &mut buf) {
                 Ok(()) => output.push(Ok(dest)),
diff --git a/src/test/ui/structs-enums/enum-non-c-like-repr-int.rs b/src/test/ui/structs-enums/enum-non-c-like-repr-int.rs
index 5dd9c1863d62d..f9e96c1a0f4ae 100644
--- a/src/test/ui/structs-enums/enum-non-c-like-repr-int.rs
+++ b/src/test/ui/structs-enums/enum-non-c-like-repr-int.rs
@@ -65,8 +65,10 @@ fn main() {
     unsafe {
         // This should be safe, because we don't match on it unless it's fully formed,
         // and it doesn't have a destructor.
-        #[allow(deprecated)]
-        let mut dest: MyEnum = mem::uninitialized();
+        //
+        // MyEnum is repr(u8) so it is guaranteed to have a separate discriminant and each variant
+        // can be zero initialized.
+        let mut dest: MyEnum = mem::zeroed();
         while buf.len() > 0 {
             match parse_my_enum(&mut dest, &mut buf) {
                 Ok(()) => output.push(Ok(dest)),
diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs
index a5360fa13c4ea..e804afcf9ed99 100644
--- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs
+++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.rs
@@ -1,5 +1,4 @@
-#![allow(deprecated)]
-
+use std::mem::zeroed;
 enum Void {}
 
 fn main() {
@@ -8,21 +7,25 @@ fn main() {
         Ok(n) => n,
     };
 
-    let x: &Void = unsafe { std::mem::uninitialized() };
+    // This is pretty much instant UB. However, we have no choice -- we need to
+    // test matching on a reference to `&Void`; we cannot do anything other than
+    // just accept the fact that this is UB if `main` did run, but it doesn't;
+    // this test only checks that these are feature-gated.
+    let x: &Void = unsafe { zeroed() };
     let _ = match x {}; //~ ERROR non-exhaustive
 
-    let x: (Void,) = unsafe { std::mem::uninitialized() };
+    let x: (Void,) = unsafe { zeroed() };
     let _ = match x {}; //~ ERROR non-exhaustive
 
-    let x: [Void; 1] = unsafe { std::mem::uninitialized() };
+    let x: [Void; 1] = unsafe { zeroed() };
     let _ = match x {}; //~ ERROR non-exhaustive
 
-    let x: &[Void] = unsafe { std::mem::uninitialized() };
+    let x: &[Void] = unsafe { zeroed() };
     let _ = match x {   //~ ERROR non-exhaustive
         &[] => (),
     };
 
-    let x: Void = unsafe { std::mem::uninitialized() };
+    let x: Void = unsafe { zeroed() };
     let _ = match x {}; // okay
 
     let x: Result<u32, Void> = Ok(23);
diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr
index 18ffdccb9b2db..a667e1fe2da3a 100644
--- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr
+++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr
@@ -1,5 +1,5 @@
 error[E0004]: non-exhaustive patterns: `Err(_)` not covered
-  --> $DIR/uninhabited-matches-feature-gated.rs:7:19
+  --> $DIR/uninhabited-matches-feature-gated.rs:6:19
    |
 LL |     let _ = match x {
    |                   ^ pattern `Err(_)` not covered
@@ -7,7 +7,7 @@ LL |     let _ = match x {
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
 
 error[E0004]: non-exhaustive patterns: type `&Void` is non-empty
-  --> $DIR/uninhabited-matches-feature-gated.rs:12:19
+  --> $DIR/uninhabited-matches-feature-gated.rs:15:19
    |
 LL | enum Void {}
    | ------------ `Void` defined here
@@ -18,7 +18,7 @@ LL |     let _ = match x {};
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
 
 error[E0004]: non-exhaustive patterns: type `(Void,)` is non-empty
-  --> $DIR/uninhabited-matches-feature-gated.rs:15:19
+  --> $DIR/uninhabited-matches-feature-gated.rs:18:19
    |
 LL |     let _ = match x {};
    |                   ^
@@ -26,7 +26,7 @@ LL |     let _ = match x {};
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
 
 error[E0004]: non-exhaustive patterns: type `[Void; 1]` is non-empty
-  --> $DIR/uninhabited-matches-feature-gated.rs:18:19
+  --> $DIR/uninhabited-matches-feature-gated.rs:21:19
    |
 LL |     let _ = match x {};
    |                   ^
@@ -34,7 +34,7 @@ LL |     let _ = match x {};
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
 
 error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered
-  --> $DIR/uninhabited-matches-feature-gated.rs:21:19
+  --> $DIR/uninhabited-matches-feature-gated.rs:24:19
    |
 LL |     let _ = match x {
    |                   ^ pattern `&[_, ..]` not covered
@@ -42,7 +42,7 @@ LL |     let _ = match x {
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
 
 error[E0004]: non-exhaustive patterns: `Err(_)` not covered
-  --> $DIR/uninhabited-matches-feature-gated.rs:29:19
+  --> $DIR/uninhabited-matches-feature-gated.rs:32:19
    |
 LL |     let _ = match x {
    |                   ^ pattern `Err(_)` not covered
@@ -50,7 +50,7 @@ LL |     let _ = match x {
    = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
 
 error[E0005]: refutable pattern in local binding: `Err(_)` not covered
-  --> $DIR/uninhabited-matches-feature-gated.rs:34:9
+  --> $DIR/uninhabited-matches-feature-gated.rs:37:9
    |
 LL |     let Ok(x) = x;
    |         ^^^^^ pattern `Err(_)` not covered
diff --git a/src/test/ui/uninit-empty-types.rs b/src/test/ui/uninit-empty-types.rs
index 0d1235776a6ef..b21de882b2ceb 100644
--- a/src/test/ui/uninit-empty-types.rs
+++ b/src/test/ui/uninit-empty-types.rs
@@ -1,17 +1,19 @@
-// run-pass
+// build-pass
 // Test the uninit() construct returning various empty types.
 
 // pretty-expanded FIXME #23616
 
-use std::mem;
+use std::mem::MaybeUninit;
 
-#[derive(Clone)]
 struct Foo;
 
 #[allow(deprecated)]
 pub fn main() {
     unsafe {
-        let _x: Foo = mem::uninitialized();
-        let _x: [Foo; 2] = mem::uninitialized();
+        // `Foo` and `[Foo; 2]` are both zero sized and inhabited, so this is safe.
+        let _x: Foo = MaybeUninit::uninit().assume_init();
+        let _x: [Foo; 2] = MaybeUninit::uninit().assume_init();
+        let _x: Foo = std::mem::uninitialized();
+        let _x: [Foo; 2] = std::mem::uninitialized();
     }
 }

From 5b8df34203d1d6f308da7413c9f27b0b0887b438 Mon Sep 17 00:00:00 2001
From: Oliver Scherer <github35764891676564198441@oli-obk.de>
Date: Mon, 23 Dec 2019 11:27:34 +0100
Subject: [PATCH 09/21] Update src/librustc_mir/interpret/place.rs

Co-Authored-By: Ralf Jung <post@ralfj.de>
---
 src/librustc_mir/interpret/place.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs
index 6e7fa302abcbb..37540d8a3ccb1 100644
--- a/src/librustc_mir/interpret/place.rs
+++ b/src/librustc_mir/interpret/place.rs
@@ -532,7 +532,7 @@ where
                 }
 
                 let index = if from_end {
-                    assert!(offset - 1 < min_length);
+                    assert!(0 < offset && offset - 1 < min_length);
                     n - u64::from(offset)
                 } else {
                     assert!(offset < min_length);

From 45acee30903f6a84d22a5afa29df965265ca486b Mon Sep 17 00:00:00 2001
From: Mazdak Farrokhzad <twingoow@gmail.com>
Date: Sun, 22 Dec 2019 20:46:14 +0100
Subject: [PATCH 10/21] Move `{hir::lowering -> hir}::is_range_literal`.

The function is never used inside lowering,
but only ever in external crates.

By moving it, we faciliate lowering as its own crate.
---
 src/librustc/hir/lowering.rs | 62 ------------------------------------
 src/librustc/hir/mod.rs      | 62 ++++++++++++++++++++++++++++++++++++
 2 files changed, 62 insertions(+), 62 deletions(-)

diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 83869951ea2a1..a702eb839845e 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -3437,65 +3437,3 @@ fn body_ids(bodies: &BTreeMap<hir::BodyId, hir::Body<'hir>>) -> Vec<hir::BodyId>
     body_ids.sort_by_key(|b| bodies[b].value.span);
     body_ids
 }
-
-/// Checks if the specified expression is a built-in range literal.
-/// (See: `LoweringContext::lower_expr()`).
-pub fn is_range_literal(sess: &Session, expr: &hir::Expr) -> bool {
-    use hir::{Path, QPath, ExprKind, TyKind};
-
-    // Returns whether the given path represents a (desugared) range,
-    // either in std or core, i.e. has either a `::std::ops::Range` or
-    // `::core::ops::Range` prefix.
-    fn is_range_path(path: &Path) -> bool {
-        let segs: Vec<_> = path.segments.iter().map(|seg| seg.ident.to_string()).collect();
-        let segs: Vec<_> = segs.iter().map(|seg| &**seg).collect();
-
-        // "{{root}}" is the equivalent of `::` prefix in `Path`.
-        if let ["{{root}}", std_core, "ops", range] = segs.as_slice() {
-            (*std_core == "std" || *std_core == "core") && range.starts_with("Range")
-        } else {
-            false
-        }
-    };
-
-    // Check whether a span corresponding to a range expression is a
-    // range literal, rather than an explicit struct or `new()` call.
-    fn is_lit(sess: &Session, span: &Span) -> bool {
-        let source_map = sess.source_map();
-        let end_point = source_map.end_point(*span);
-
-        if let Ok(end_string) = source_map.span_to_snippet(end_point) {
-            !(end_string.ends_with("}") || end_string.ends_with(")"))
-        } else {
-            false
-        }
-    };
-
-    match expr.kind {
-        // All built-in range literals but `..=` and `..` desugar to `Struct`s.
-        ExprKind::Struct(ref qpath, _, _) => {
-            if let QPath::Resolved(None, ref path) = **qpath {
-                return is_range_path(&path) && is_lit(sess, &expr.span);
-            }
-        }
-
-        // `..` desugars to its struct path.
-        ExprKind::Path(QPath::Resolved(None, ref path)) => {
-            return is_range_path(&path) && is_lit(sess, &expr.span);
-        }
-
-        // `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
-        ExprKind::Call(ref func, _) => {
-            if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.kind {
-                if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.kind {
-                    let new_call = segment.ident.name == sym::new;
-                    return is_range_path(&path) && is_lit(sess, &expr.span) && new_call;
-                }
-            }
-        }
-
-        _ => {}
-    }
-
-    false
-}
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 4aa8c12a219ca..55a30a7cc3a19 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -1564,6 +1564,68 @@ impl fmt::Debug for Expr {
     }
 }
 
+/// Checks if the specified expression is a built-in range literal.
+/// (See: `LoweringContext::lower_expr()`).
+pub fn is_range_literal(sess: &Session, expr: &hir::Expr) -> bool {
+    use hir::{Path, QPath, ExprKind, TyKind};
+
+    // Returns whether the given path represents a (desugared) range,
+    // either in std or core, i.e. has either a `::std::ops::Range` or
+    // `::core::ops::Range` prefix.
+    fn is_range_path(path: &Path) -> bool {
+        let segs: Vec<_> = path.segments.iter().map(|seg| seg.ident.to_string()).collect();
+        let segs: Vec<_> = segs.iter().map(|seg| &**seg).collect();
+
+        // "{{root}}" is the equivalent of `::` prefix in `Path`.
+        if let ["{{root}}", std_core, "ops", range] = segs.as_slice() {
+            (*std_core == "std" || *std_core == "core") && range.starts_with("Range")
+        } else {
+            false
+        }
+    };
+
+    // Check whether a span corresponding to a range expression is a
+    // range literal, rather than an explicit struct or `new()` call.
+    fn is_lit(sess: &Session, span: &Span) -> bool {
+        let source_map = sess.source_map();
+        let end_point = source_map.end_point(*span);
+
+        if let Ok(end_string) = source_map.span_to_snippet(end_point) {
+            !(end_string.ends_with("}") || end_string.ends_with(")"))
+        } else {
+            false
+        }
+    };
+
+    match expr.kind {
+        // All built-in range literals but `..=` and `..` desugar to `Struct`s.
+        ExprKind::Struct(ref qpath, _, _) => {
+            if let QPath::Resolved(None, ref path) = **qpath {
+                return is_range_path(&path) && is_lit(sess, &expr.span);
+            }
+        }
+
+        // `..` desugars to its struct path.
+        ExprKind::Path(QPath::Resolved(None, ref path)) => {
+            return is_range_path(&path) && is_lit(sess, &expr.span);
+        }
+
+        // `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
+        ExprKind::Call(ref func, _) => {
+            if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.kind {
+                if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.kind {
+                    let new_call = segment.ident.name == sym::new;
+                    return is_range_path(&path) && is_lit(sess, &expr.span) && new_call;
+                }
+            }
+        }
+
+        _ => {}
+    }
+
+    false
+}
+
 #[derive(RustcEncodable, RustcDecodable, Debug, HashStable)]
 pub enum ExprKind {
     /// A `box x` expression.

From a5991c57cf4430d1f67886637c65f2178ff1b372 Mon Sep 17 00:00:00 2001
From: varkor <github@varkor.com>
Date: Sun, 22 Dec 2019 18:18:49 +0000
Subject: [PATCH 11/21] Add the full issue reference to equality constraints in
 `where` clauses

---
 src/librustc_passes/ast_validation.rs                  | 10 ++++++++--
 .../ui/where-clauses/where-equality-constraints.rs     |  4 ++--
 .../ui/where-clauses/where-equality-constraints.stderr |  8 ++++++--
 3 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs
index ee6a67802ade3..1d5e65c6d27cd 100644
--- a/src/librustc_passes/ast_validation.rs
+++ b/src/librustc_passes/ast_validation.rs
@@ -737,8 +737,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         for predicate in &generics.where_clause.predicates {
             if let WherePredicate::EqPredicate(ref predicate) = *predicate {
                 self.err_handler()
-                    .span_err(predicate.span, "equality constraints are not yet \
-                                               supported in where clauses (see #20041)");
+                    .struct_span_err(
+                        predicate.span,
+                        "equality constraints are not yet supported in `where` clauses",
+                    )
+                    .note(
+                        "for more information, see https://github.com/rust-lang/rust/issues/20041",
+                    )
+                    .emit();
             }
         }
 
diff --git a/src/test/ui/where-clauses/where-equality-constraints.rs b/src/test/ui/where-clauses/where-equality-constraints.rs
index f2349144b88ae..8828f09d92d33 100644
--- a/src/test/ui/where-clauses/where-equality-constraints.rs
+++ b/src/test/ui/where-clauses/where-equality-constraints.rs
@@ -1,6 +1,6 @@
 fn f() where u8 = u16 {}
-//~^ ERROR equality constraints are not yet supported in where clauses
+//~^ ERROR equality constraints are not yet supported in `where` clauses
 fn g() where for<'a> &'static (u8,) == u16, {}
-//~^ ERROR equality constraints are not yet supported in where clauses
+//~^ ERROR equality constraints are not yet supported in `where` clauses
 
 fn main() {}
diff --git a/src/test/ui/where-clauses/where-equality-constraints.stderr b/src/test/ui/where-clauses/where-equality-constraints.stderr
index 220447079c629..c0241fe708f64 100644
--- a/src/test/ui/where-clauses/where-equality-constraints.stderr
+++ b/src/test/ui/where-clauses/where-equality-constraints.stderr
@@ -1,14 +1,18 @@
-error: equality constraints are not yet supported in where clauses (see #20041)
+error: equality constraints are not yet supported in `where` clauses
   --> $DIR/where-equality-constraints.rs:1:14
    |
 LL | fn f() where u8 = u16 {}
    |              ^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/20041
 
-error: equality constraints are not yet supported in where clauses (see #20041)
+error: equality constraints are not yet supported in `where` clauses
   --> $DIR/where-equality-constraints.rs:3:14
    |
 LL | fn g() where for<'a> &'static (u8,) == u16, {}
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: for more information, see https://github.com/rust-lang/rust/issues/20041
 
 error: aborting due to 2 previous errors
 

From b7bfdbe68147f2ea8ca4870270643180bef76e02 Mon Sep 17 00:00:00 2001
From: varkor <github@varkor.com>
Date: Sun, 22 Dec 2019 18:42:15 +0000
Subject: [PATCH 12/21] Improve invalid assignment error

---
 src/librustc_typeck/check/expr.rs             |  9 ++++--
 src/librustc_typeck/check/op.rs               |  8 ++---
 src/test/ui/bad/bad-expr-lhs.rs               | 10 +++---
 src/test/ui/bad/bad-expr-lhs.stderr           | 32 ++++++++++++-------
 src/test/ui/error-codes/E0067.stderr          |  8 +++--
 src/test/ui/error-codes/E0070.stderr          | 18 +++++++----
 src/test/ui/issues/issue-13407.rs             |  2 +-
 src/test/ui/issues/issue-13407.stderr         |  6 ++--
 src/test/ui/issues/issue-26093.rs             |  4 ++-
 src/test/ui/issues/issue-26093.stderr         | 26 ++++++++++++---
 src/test/ui/issues/issue-34334.rs             |  2 +-
 src/test/ui/issues/issue-34334.stderr         |  6 ++--
 .../type-check/assignment-expected-bool.rs    |  2 +-
 .../assignment-expected-bool.stderr           |  6 ++--
 .../ui/type/type-check/assignment-in-if.rs    |  2 +-
 15 files changed, 92 insertions(+), 49 deletions(-)

diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index 6ecf3ccd6e7ed..27f6b3cb45f4b 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -753,9 +753,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             err.emit();
         } else if !lhs.is_syntactic_place_expr() {
-            struct_span_err!(self.tcx.sess, expr.span, E0070, "invalid left-hand side expression")
-                .span_label(expr.span, "left-hand of expression not valid")
-                .emit();
+            struct_span_err!(
+                self.tcx.sess,
+                expr.span,
+                E0070,
+                "invalid left-hand side of assignment",
+            ).span_label(lhs.span, "cannot assign to this expression").emit();
         }
 
         self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized);
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index 53475569b2c3f..eeee7dea4d1c1 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -36,12 +36,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if !lhs_expr.is_syntactic_place_expr() {
             struct_span_err!(
                 self.tcx.sess,
-                lhs_expr.span,
+                op.span,
                 E0067,
-                "invalid left-hand side expression"
-            )
-            .span_label(lhs_expr.span, "invalid expression for left-hand side")
-            .emit();
+                "invalid left-hand side of assignment",
+            ).span_label(lhs_expr.span, "cannot assign to this expression").emit();
         }
         ty
     }
diff --git a/src/test/ui/bad/bad-expr-lhs.rs b/src/test/ui/bad/bad-expr-lhs.rs
index 2cd8bc9d47333..d7cf1b7700514 100644
--- a/src/test/ui/bad/bad-expr-lhs.rs
+++ b/src/test/ui/bad/bad-expr-lhs.rs
@@ -1,10 +1,10 @@
 fn main() {
-    1 = 2; //~ ERROR invalid left-hand side expression
-    1 += 2; //~ ERROR invalid left-hand side expression
-    (1, 2) = (3, 4); //~ ERROR invalid left-hand side expression
+    1 = 2; //~ ERROR invalid left-hand side of assignment
+    1 += 2; //~ ERROR invalid left-hand side of assignment
+    (1, 2) = (3, 4); //~ ERROR invalid left-hand side of assignment
 
     let (a, b) = (1, 2);
-    (a, b) = (3, 4); //~ ERROR invalid left-hand side expression
+    (a, b) = (3, 4); //~ ERROR invalid left-hand side of assignment
 
-    None = Some(3); //~ ERROR invalid left-hand side expression
+    None = Some(3); //~ ERROR invalid left-hand side of assignment
 }
diff --git a/src/test/ui/bad/bad-expr-lhs.stderr b/src/test/ui/bad/bad-expr-lhs.stderr
index a0de6a73797e2..07cffbe97a8d9 100644
--- a/src/test/ui/bad/bad-expr-lhs.stderr
+++ b/src/test/ui/bad/bad-expr-lhs.stderr
@@ -1,32 +1,42 @@
-error[E0070]: invalid left-hand side expression
+error[E0070]: invalid left-hand side of assignment
   --> $DIR/bad-expr-lhs.rs:2:5
    |
 LL |     1 = 2;
-   |     ^^^^^ left-hand of expression not valid
+   |     -^^^^
+   |     |
+   |     cannot assign to this expression
 
-error[E0067]: invalid left-hand side expression
-  --> $DIR/bad-expr-lhs.rs:3:5
+error[E0067]: invalid left-hand side of assignment
+  --> $DIR/bad-expr-lhs.rs:3:7
    |
 LL |     1 += 2;
-   |     ^ invalid expression for left-hand side
+   |     - ^^
+   |     |
+   |     cannot assign to this expression
 
-error[E0070]: invalid left-hand side expression
+error[E0070]: invalid left-hand side of assignment
   --> $DIR/bad-expr-lhs.rs:4:5
    |
 LL |     (1, 2) = (3, 4);
-   |     ^^^^^^^^^^^^^^^ left-hand of expression not valid
+   |     ------^^^^^^^^^
+   |     |
+   |     cannot assign to this expression
 
-error[E0070]: invalid left-hand side expression
+error[E0070]: invalid left-hand side of assignment
   --> $DIR/bad-expr-lhs.rs:7:5
    |
 LL |     (a, b) = (3, 4);
-   |     ^^^^^^^^^^^^^^^ left-hand of expression not valid
+   |     ------^^^^^^^^^
+   |     |
+   |     cannot assign to this expression
 
-error[E0070]: invalid left-hand side expression
+error[E0070]: invalid left-hand side of assignment
   --> $DIR/bad-expr-lhs.rs:9:5
    |
 LL |     None = Some(3);
-   |     ^^^^^^^^^^^^^^ left-hand of expression not valid
+   |     ----^^^^^^^^^^
+   |     |
+   |     cannot assign to this expression
 
 error: aborting due to 5 previous errors
 
diff --git a/src/test/ui/error-codes/E0067.stderr b/src/test/ui/error-codes/E0067.stderr
index 0334565840f83..526503798b3d4 100644
--- a/src/test/ui/error-codes/E0067.stderr
+++ b/src/test/ui/error-codes/E0067.stderr
@@ -8,11 +8,13 @@ LL |     LinkedList::new() += 1;
    |
    = note: an implementation of `std::ops::AddAssign` might be missing for `std::collections::LinkedList<_>`
 
-error[E0067]: invalid left-hand side expression
-  --> $DIR/E0067.rs:4:5
+error[E0067]: invalid left-hand side of assignment
+  --> $DIR/E0067.rs:4:23
    |
 LL |     LinkedList::new() += 1;
-   |     ^^^^^^^^^^^^^^^^^ invalid expression for left-hand side
+   |     ----------------- ^^
+   |     |
+   |     cannot assign to this expression
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/error-codes/E0070.stderr b/src/test/ui/error-codes/E0070.stderr
index 845833bc82f70..1fb812d94672f 100644
--- a/src/test/ui/error-codes/E0070.stderr
+++ b/src/test/ui/error-codes/E0070.stderr
@@ -1,14 +1,18 @@
-error[E0070]: invalid left-hand side expression
+error[E0070]: invalid left-hand side of assignment
   --> $DIR/E0070.rs:6:5
    |
 LL |     SOME_CONST = 14;
-   |     ^^^^^^^^^^^^^^^ left-hand of expression not valid
+   |     ----------^^^^^
+   |     |
+   |     cannot assign to this expression
 
-error[E0070]: invalid left-hand side expression
+error[E0070]: invalid left-hand side of assignment
   --> $DIR/E0070.rs:7:5
    |
 LL |     1 = 3;
-   |     ^^^^^ left-hand of expression not valid
+   |     -^^^^
+   |     |
+   |     cannot assign to this expression
 
 error[E0308]: mismatched types
   --> $DIR/E0070.rs:8:25
@@ -16,11 +20,13 @@ error[E0308]: mismatched types
 LL |     some_other_func() = 4;
    |                         ^ expected `()`, found integer
 
-error[E0070]: invalid left-hand side expression
+error[E0070]: invalid left-hand side of assignment
   --> $DIR/E0070.rs:8:5
    |
 LL |     some_other_func() = 4;
-   |     ^^^^^^^^^^^^^^^^^^^^^ left-hand of expression not valid
+   |     -----------------^^^^
+   |     |
+   |     cannot assign to this expression
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/issues/issue-13407.rs b/src/test/ui/issues/issue-13407.rs
index 322e67cc13180..fa53d55f5b3d7 100644
--- a/src/test/ui/issues/issue-13407.rs
+++ b/src/test/ui/issues/issue-13407.rs
@@ -4,7 +4,7 @@ mod A {
 
 fn main() {
     A::C = 1;
-    //~^ ERROR: invalid left-hand side expression
+    //~^ ERROR: invalid left-hand side of assignment
     //~| ERROR: mismatched types
     //~| ERROR: struct `C` is private
 }
diff --git a/src/test/ui/issues/issue-13407.stderr b/src/test/ui/issues/issue-13407.stderr
index 5a465cc533bb7..05fd97b025f60 100644
--- a/src/test/ui/issues/issue-13407.stderr
+++ b/src/test/ui/issues/issue-13407.stderr
@@ -10,11 +10,13 @@ error[E0308]: mismatched types
 LL |     A::C = 1;
    |            ^ expected struct `A::C`, found integer
 
-error[E0070]: invalid left-hand side expression
+error[E0070]: invalid left-hand side of assignment
   --> $DIR/issue-13407.rs:6:5
    |
 LL |     A::C = 1;
-   |     ^^^^^^^^ left-hand of expression not valid
+   |     ----^^^^
+   |     |
+   |     cannot assign to this expression
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/issues/issue-26093.rs b/src/test/ui/issues/issue-26093.rs
index 7895c90068fe2..c838515caf997 100644
--- a/src/test/ui/issues/issue-26093.rs
+++ b/src/test/ui/issues/issue-26093.rs
@@ -1,7 +1,9 @@
 macro_rules! not_a_place {
     ($thing:expr) => {
         $thing = 42;
-        //~^ ERROR invalid left-hand side expression
+        //~^ ERROR invalid left-hand side of assignment
+        $thing += 42;
+        //~^ ERROR invalid left-hand side of assignment
     }
 }
 
diff --git a/src/test/ui/issues/issue-26093.stderr b/src/test/ui/issues/issue-26093.stderr
index 947c52f08d2e6..48f72cef0a85a 100644
--- a/src/test/ui/issues/issue-26093.stderr
+++ b/src/test/ui/issues/issue-26093.stderr
@@ -1,12 +1,28 @@
-error[E0070]: invalid left-hand side expression
+error[E0070]: invalid left-hand side of assignment
   --> $DIR/issue-26093.rs:3:9
    |
 LL |         $thing = 42;
-   |         ^^^^^^^^^^^ left-hand of expression not valid
+   |         ^^^^^^^^^^^
 ...
 LL |     not_a_place!(99);
-   |     ----------------- in this macro invocation
+   |     -----------------
+   |     |            |
+   |     |            cannot assign to this expression
+   |     in this macro invocation
 
-error: aborting due to previous error
+error[E0067]: invalid left-hand side of assignment
+  --> $DIR/issue-26093.rs:5:16
+   |
+LL |         $thing += 42;
+   |                ^^
+...
+LL |     not_a_place!(99);
+   |     -----------------
+   |     |            |
+   |     |            cannot assign to this expression
+   |     in this macro invocation
+
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0070`.
+Some errors have detailed explanations: E0067, E0070.
+For more information about an error, try `rustc --explain E0067`.
diff --git a/src/test/ui/issues/issue-34334.rs b/src/test/ui/issues/issue-34334.rs
index 4457d71cbb4a7..e34b5c9a0f47e 100644
--- a/src/test/ui/issues/issue-34334.rs
+++ b/src/test/ui/issues/issue-34334.rs
@@ -3,7 +3,7 @@ fn main () {
     //~^ ERROR expected one of `,` or `>`, found `=`
     //~| ERROR expected value, found struct `Vec`
     //~| ERROR mismatched types
-    //~| ERROR invalid left-hand side expression
+    //~| ERROR invalid left-hand side of assignment
     //~| ERROR expected expression, found reserved identifier `_`
     //~| ERROR expected expression, found reserved identifier `_`
     let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect();
diff --git a/src/test/ui/issues/issue-34334.stderr b/src/test/ui/issues/issue-34334.stderr
index fc90e0674cf55..e54f0c77cd973 100644
--- a/src/test/ui/issues/issue-34334.stderr
+++ b/src/test/ui/issues/issue-34334.stderr
@@ -35,11 +35,13 @@ LL |     let sr: Vec<(u32, _, _) = vec![];
             found struct `std::vec::Vec<_>`
    = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
 
-error[E0070]: invalid left-hand side expression
+error[E0070]: invalid left-hand side of assignment
   --> $DIR/issue-34334.rs:2:13
    |
 LL |     let sr: Vec<(u32, _, _) = vec![];
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^ left-hand of expression not valid
+   |             ---------------^^^^^^^^^
+   |             |
+   |             cannot assign to this expression
 
 error[E0599]: no method named `iter` found for type `()` in the current scope
   --> $DIR/issue-34334.rs:9:36
diff --git a/src/test/ui/type/type-check/assignment-expected-bool.rs b/src/test/ui/type/type-check/assignment-expected-bool.rs
index 03830fea062cf..191939bdb705b 100644
--- a/src/test/ui/type/type-check/assignment-expected-bool.rs
+++ b/src/test/ui/type/type-check/assignment-expected-bool.rs
@@ -30,5 +30,5 @@ fn main() {
     // A test to check that not expecting `bool` behaves well:
     let _: usize = 0 = 0;
     //~^ ERROR mismatched types [E0308]
-    //~| ERROR invalid left-hand side expression [E0070]
+    //~| ERROR invalid left-hand side of assignment [E0070]
 }
diff --git a/src/test/ui/type/type-check/assignment-expected-bool.stderr b/src/test/ui/type/type-check/assignment-expected-bool.stderr
index 9a1cf5b25625c..bbd961f845016 100644
--- a/src/test/ui/type/type-check/assignment-expected-bool.stderr
+++ b/src/test/ui/type/type-check/assignment-expected-bool.stderr
@@ -97,11 +97,13 @@ LL |         || (0 = 0);
    |            expected `bool`, found `()`
    |            help: try comparing for equality: `0 == 0`
 
-error[E0070]: invalid left-hand side expression
+error[E0070]: invalid left-hand side of assignment
   --> $DIR/assignment-expected-bool.rs:31:20
    |
 LL |     let _: usize = 0 = 0;
-   |                    ^^^^^ left-hand of expression not valid
+   |                    -^^^^
+   |                    |
+   |                    cannot assign to this expression
 
 error[E0308]: mismatched types
   --> $DIR/assignment-expected-bool.rs:31:20
diff --git a/src/test/ui/type/type-check/assignment-in-if.rs b/src/test/ui/type/type-check/assignment-in-if.rs
index 77b122b0a794a..8da7b32b47b14 100644
--- a/src/test/ui/type/type-check/assignment-in-if.rs
+++ b/src/test/ui/type/type-check/assignment-in-if.rs
@@ -26,7 +26,7 @@ fn main() {
         //~^ ERROR mismatched types
         println!("{}", x);
     }
-    // "invalid left-hand side expression" error is suppresed
+    // "invalid left-hand side of assignment" error is suppresed
     if 3 = x {
         //~^ ERROR mismatched types
         println!("{}", x);

From 5fa02ecc291f0a6b356fbb3b1e14649082b93a2f Mon Sep 17 00:00:00 2001
From: varkor <github@varkor.com>
Date: Sun, 22 Dec 2019 20:14:08 +0000
Subject: [PATCH 13/21] Add note about destructuring assignments

---
 src/librustc_typeck/check/expr.rs             |  44 +++++--
 src/librustc_typeck/check/op.rs               |  20 ++--
 src/test/ui/bad/bad-expr-lhs.stderr           |   3 +
 src/test/ui/bad/destructuring-assignment.rs   |  21 ++++
 .../ui/bad/destructuring-assignment.stderr    | 111 ++++++++++++++++++
 5 files changed, 178 insertions(+), 21 deletions(-)
 create mode 100644 src/test/ui/bad/destructuring-assignment.rs
 create mode 100644 src/test/ui/bad/destructuring-assignment.stderr

diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index 27f6b3cb45f4b..9085528c84e9f 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -17,7 +17,7 @@ use crate::check::TupleArgumentsFlag::DontTupleArguments;
 use crate::util::common::ErrorReported;
 use crate::util::nodemap::FxHashMap;
 
-use errors::{pluralize, Applicability, DiagnosticBuilder};
+use errors::{pluralize, Applicability, DiagnosticBuilder, DiagnosticId};
 use rustc::hir;
 use rustc::hir::def::{CtorKind, DefKind, Res};
 use rustc::hir::def_id::DefId;
@@ -723,6 +723,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
     }
 
+    pub(crate) fn check_lhs_assignable(
+        &self,
+        lhs: &'tcx hir::Expr,
+        err_code: &'static str,
+        expr_span: &Span,
+    ) {
+        if !lhs.is_syntactic_place_expr() {
+            let mut err = self.tcx.sess.struct_span_err_with_code(
+                *expr_span,
+                "invalid left-hand side of assignment",
+                DiagnosticId::Error(err_code.into()),
+            );
+            err.span_label(lhs.span, "cannot assign to this expression");
+            let destructuring_assignment = match &lhs.kind {
+                ExprKind::Array(comps) | ExprKind::Tup(comps) => {
+                    comps.iter().all(|e| e.is_syntactic_place_expr())
+                }
+                ExprKind::Struct(_path, fields, rest) => {
+                    rest.as_ref().map(|e| e.is_syntactic_place_expr()).unwrap_or(true) &&
+                        fields.iter().all(|f| f.expr.is_syntactic_place_expr())
+                }
+                _ => false,
+            };
+            if destructuring_assignment {
+                err.note("destructuring assignments are not yet supported");
+                err.note(
+                    "for more information, see https://github.com/rust-lang/rfcs/issues/372",
+                );
+            }
+            err.emit();
+        }
+    }
+
     /// Type check assignment expression `expr` of form `lhs = rhs`.
     /// The expected type is `()` and is passsed to the function for the purposes of diagnostics.
     fn check_expr_assign(
@@ -752,13 +785,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 err.help(msg);
             }
             err.emit();
-        } else if !lhs.is_syntactic_place_expr() {
-            struct_span_err!(
-                self.tcx.sess,
-                expr.span,
-                E0070,
-                "invalid left-hand side of assignment",
-            ).span_label(lhs.span, "cannot assign to this expression").emit();
+        } else {
+            self.check_lhs_assignable(lhs, "E0070", &expr.span);
         }
 
         self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized);
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index eeee7dea4d1c1..dfa55e324a625 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -19,28 +19,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         expr: &'tcx hir::Expr,
         op: hir::BinOp,
-        lhs_expr: &'tcx hir::Expr,
-        rhs_expr: &'tcx hir::Expr,
+        lhs: &'tcx hir::Expr,
+        rhs: &'tcx hir::Expr,
     ) -> Ty<'tcx> {
         let (lhs_ty, rhs_ty, return_ty) =
-            self.check_overloaded_binop(expr, lhs_expr, rhs_expr, op, IsAssign::Yes);
+            self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes);
 
         let ty =
-            if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
-                self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
+            if !lhs.is_ty_var() && !rhs.is_ty_var() && is_builtin_binop(lhs, rhs, op) {
+                self.enforce_builtin_binop_types(lhs_expr, lhs, rhs_expr, rhs, op);
                 self.tcx.mk_unit()
             } else {
                 return_ty
             };
 
-        if !lhs_expr.is_syntactic_place_expr() {
-            struct_span_err!(
-                self.tcx.sess,
-                op.span,
-                E0067,
-                "invalid left-hand side of assignment",
-            ).span_label(lhs_expr.span, "cannot assign to this expression").emit();
-        }
+        self.check_lhs_assignable(lhs, "E0067", &op.span);
+
         ty
     }
 
diff --git a/src/test/ui/bad/bad-expr-lhs.stderr b/src/test/ui/bad/bad-expr-lhs.stderr
index 07cffbe97a8d9..61c25bb471c37 100644
--- a/src/test/ui/bad/bad-expr-lhs.stderr
+++ b/src/test/ui/bad/bad-expr-lhs.stderr
@@ -29,6 +29,9 @@ LL |     (a, b) = (3, 4);
    |     ------^^^^^^^^^
    |     |
    |     cannot assign to this expression
+   |
+   = note: destructuring assignments are not yet supported
+   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0070]: invalid left-hand side of assignment
   --> $DIR/bad-expr-lhs.rs:9:5
diff --git a/src/test/ui/bad/destructuring-assignment.rs b/src/test/ui/bad/destructuring-assignment.rs
new file mode 100644
index 0000000000000..7112cedfd0009
--- /dev/null
+++ b/src/test/ui/bad/destructuring-assignment.rs
@@ -0,0 +1,21 @@
+struct S { x: u8, y: u8 }
+
+fn main() {
+    let (a, b) = (1, 2);
+
+    (a, b) = (3, 4); //~ ERROR invalid left-hand side of assignment
+    (a, b) += (3, 4); //~ ERROR invalid left-hand side of assignment
+    //~^ ERROR binary assignment operation `+=` cannot be applied
+
+    [a, b] = [3, 4]; //~ ERROR invalid left-hand side of assignment
+    [a, b] += [3, 4]; //~ ERROR invalid left-hand side of assignment
+    //~^ ERROR binary assignment operation `+=` cannot be applied
+
+    let s = S { x: 3, y: 4 };
+
+    S { x: a, y: b } = s; //~ ERROR invalid left-hand side of assignment
+    S { x: a, y: b } += s; //~ ERROR invalid left-hand side of assignment
+    //~^ ERROR binary assignment operation `+=` cannot be applied
+
+    S { x: a, ..s } = S { x: 3, y: 4 }; //~ ERROR invalid left-hand side of assignment
+}
diff --git a/src/test/ui/bad/destructuring-assignment.stderr b/src/test/ui/bad/destructuring-assignment.stderr
new file mode 100644
index 0000000000000..676576b7bc526
--- /dev/null
+++ b/src/test/ui/bad/destructuring-assignment.stderr
@@ -0,0 +1,111 @@
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/destructuring-assignment.rs:6:5
+   |
+LL |     (a, b) = (3, 4);
+   |     ------^^^^^^^^^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: destructuring assignments are not yet supported
+   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
+
+error[E0368]: binary assignment operation `+=` cannot be applied to type `({integer}, {integer})`
+  --> $DIR/destructuring-assignment.rs:7:5
+   |
+LL |     (a, b) += (3, 4);
+   |     ------^^^^^^^^^^
+   |     |
+   |     cannot use `+=` on type `({integer}, {integer})`
+   |
+   = note: an implementation of `std::ops::AddAssign` might be missing for `({integer}, {integer})`
+
+error[E0067]: invalid left-hand side of assignment
+  --> $DIR/destructuring-assignment.rs:7:12
+   |
+LL |     (a, b) += (3, 4);
+   |     ------ ^^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: destructuring assignments are not yet supported
+   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
+
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/destructuring-assignment.rs:10:5
+   |
+LL |     [a, b] = [3, 4];
+   |     ------^^^^^^^^^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: destructuring assignments are not yet supported
+   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
+
+error[E0368]: binary assignment operation `+=` cannot be applied to type `[{integer}; 2]`
+  --> $DIR/destructuring-assignment.rs:11:5
+   |
+LL |     [a, b] += [3, 4];
+   |     ------^^^^^^^^^^
+   |     |
+   |     cannot use `+=` on type `[{integer}; 2]`
+   |
+   = note: an implementation of `std::ops::AddAssign` might be missing for `[{integer}; 2]`
+
+error[E0067]: invalid left-hand side of assignment
+  --> $DIR/destructuring-assignment.rs:11:12
+   |
+LL |     [a, b] += [3, 4];
+   |     ------ ^^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: destructuring assignments are not yet supported
+   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
+
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/destructuring-assignment.rs:16:5
+   |
+LL |     S { x: a, y: b } = s;
+   |     ----------------^^^^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: destructuring assignments are not yet supported
+   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
+
+error[E0368]: binary assignment operation `+=` cannot be applied to type `S`
+  --> $DIR/destructuring-assignment.rs:17:5
+   |
+LL |     S { x: a, y: b } += s;
+   |     ----------------^^^^^
+   |     |
+   |     cannot use `+=` on type `S`
+   |
+   = note: an implementation of `std::ops::AddAssign` might be missing for `S`
+
+error[E0067]: invalid left-hand side of assignment
+  --> $DIR/destructuring-assignment.rs:17:22
+   |
+LL |     S { x: a, y: b } += s;
+   |     ---------------- ^^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: destructuring assignments are not yet supported
+   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
+
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/destructuring-assignment.rs:20:5
+   |
+LL |     S { x: a, ..s } = S { x: 3, y: 4 };
+   |     ---------------^^^^^^^^^^^^^^^^^^^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: destructuring assignments are not yet supported
+   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
+
+error: aborting due to 10 previous errors
+
+Some errors have detailed explanations: E0067, E0070, E0368.
+For more information about an error, try `rustc --explain E0067`.

From 5ab4735559aeeece0b5811dad95fdf515b1bcfbd Mon Sep 17 00:00:00 2001
From: varkor <github@varkor.com>
Date: Sun, 22 Dec 2019 20:27:42 +0000
Subject: [PATCH 14/21] Recognise nested tuples/arrays/structs

---
 src/librustc_typeck/check/expr.rs             | 25 +++++++++++--------
 src/test/ui/bad/destructuring-assignment.rs   |  4 +++
 .../ui/bad/destructuring-assignment.stderr    | 13 +++++++++-
 3 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index 9085528c84e9f..13046d8002a9b 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -723,6 +723,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
     }
 
+    fn is_destructuring_place_expr(&self, expr: &'tcx hir::Expr) -> bool {
+        match &expr.kind {
+            ExprKind::Array(comps) | ExprKind::Tup(comps) => {
+                comps.iter().all(|e| self.is_destructuring_place_expr(e))
+            }
+            ExprKind::Struct(_path, fields, rest) => {
+                rest.as_ref().map(|e| self.is_destructuring_place_expr(e)).unwrap_or(true) &&
+                    fields.iter().all(|f| self.is_destructuring_place_expr(&f.expr))
+            }
+            _ => expr.is_syntactic_place_expr(),
+        }
+    }
+
     pub(crate) fn check_lhs_assignable(
         &self,
         lhs: &'tcx hir::Expr,
@@ -736,17 +749,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 DiagnosticId::Error(err_code.into()),
             );
             err.span_label(lhs.span, "cannot assign to this expression");
-            let destructuring_assignment = match &lhs.kind {
-                ExprKind::Array(comps) | ExprKind::Tup(comps) => {
-                    comps.iter().all(|e| e.is_syntactic_place_expr())
-                }
-                ExprKind::Struct(_path, fields, rest) => {
-                    rest.as_ref().map(|e| e.is_syntactic_place_expr()).unwrap_or(true) &&
-                        fields.iter().all(|f| f.expr.is_syntactic_place_expr())
-                }
-                _ => false,
-            };
-            if destructuring_assignment {
+            if self.is_destructuring_place_expr(lhs) {
                 err.note("destructuring assignments are not yet supported");
                 err.note(
                     "for more information, see https://github.com/rust-lang/rfcs/issues/372",
diff --git a/src/test/ui/bad/destructuring-assignment.rs b/src/test/ui/bad/destructuring-assignment.rs
index 7112cedfd0009..876c9efea2647 100644
--- a/src/test/ui/bad/destructuring-assignment.rs
+++ b/src/test/ui/bad/destructuring-assignment.rs
@@ -18,4 +18,8 @@ fn main() {
     //~^ ERROR binary assignment operation `+=` cannot be applied
 
     S { x: a, ..s } = S { x: 3, y: 4 }; //~ ERROR invalid left-hand side of assignment
+
+    let c = 3;
+
+    ((a, b), c) = ((3, 4), 5); //~ ERROR invalid left-hand side of assignment
 }
diff --git a/src/test/ui/bad/destructuring-assignment.stderr b/src/test/ui/bad/destructuring-assignment.stderr
index 676576b7bc526..845008b06937c 100644
--- a/src/test/ui/bad/destructuring-assignment.stderr
+++ b/src/test/ui/bad/destructuring-assignment.stderr
@@ -105,7 +105,18 @@ LL |     S { x: a, ..s } = S { x: 3, y: 4 };
    = note: destructuring assignments are not yet supported
    = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
-error: aborting due to 10 previous errors
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/destructuring-assignment.rs:24:5
+   |
+LL |     ((a, b), c) = ((3, 4), 5);
+   |     -----------^^^^^^^^^^^^^^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: destructuring assignments are not yet supported
+   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
+
+error: aborting due to 11 previous errors
 
 Some errors have detailed explanations: E0067, E0070, E0368.
 For more information about an error, try `rustc --explain E0067`.

From 35979a92bf6dba402885a1488ecfd84046e4bd71 Mon Sep 17 00:00:00 2001
From: varkor <github@varkor.com>
Date: Sun, 22 Dec 2019 21:08:53 +0000
Subject: [PATCH 15/21] Add span information to `ExprKind::Assign`

---
 src/librustc/hir/intravisit.rs                |  6 +++---
 src/librustc/hir/lowering/expr.rs             | 11 ++++++----
 src/librustc/hir/mod.rs                       |  2 +-
 src/librustc/hir/print.rs                     |  4 ++--
 src/librustc_lint/unused.rs                   |  2 +-
 src/librustc_mir/hair/cx/expr.rs              |  2 +-
 src/librustc_parse/parser/expr.rs             |  4 +++-
 src/librustc_passes/liveness.rs               |  4 ++--
 src/librustc_privacy/lib.rs                   |  2 +-
 src/librustc_typeck/check/demand.rs           |  2 +-
 src/librustc_typeck/check/expr.rs             |  8 +++++---
 src/librustc_typeck/expr_use_visitor.rs       |  2 +-
 src/libsyntax/ast.rs                          |  2 +-
 src/libsyntax/mut_visit.rs                    |  2 +-
 src/libsyntax/print/pprust.rs                 |  2 +-
 src/libsyntax/util/parser.rs                  |  2 +-
 src/libsyntax/visit.rs                        |  6 +++---
 src/test/ui-fulldeps/pprust-expr-roundtrip.rs |  4 ++--
 src/test/ui/bad/bad-expr-lhs.stderr           | 16 +++++++--------
 .../ui/bad/destructuring-assignment.stderr    | 20 +++++++++----------
 src/test/ui/error-codes/E0070.stderr          | 12 +++++------
 src/test/ui/issues/issue-13407.stderr         |  4 ++--
 src/test/ui/issues/issue-26093.stderr         |  4 ++--
 src/test/ui/issues/issue-34334.stderr         |  4 ++--
 .../assignment-expected-bool.stderr           |  4 ++--
 25 files changed, 69 insertions(+), 62 deletions(-)

diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs
index dc2008fdd9743..a7a8673d49eb1 100644
--- a/src/librustc/hir/intravisit.rs
+++ b/src/librustc/hir/intravisit.rs
@@ -1043,9 +1043,9 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
             walk_list!(visitor, visit_label, opt_label);
             visitor.visit_block(block);
         }
-        ExprKind::Assign(ref left_hand_expression, ref right_hand_expression) => {
-            visitor.visit_expr(right_hand_expression);
-            visitor.visit_expr(left_hand_expression)
+        ExprKind::Assign(ref lhs, ref rhs, _) => {
+            visitor.visit_expr(rhs);
+            visitor.visit_expr(lhs)
         }
         ExprKind::AssignOp(_, ref left_expression, ref right_expression) => {
             visitor.visit_expr(right_expression);
diff --git a/src/librustc/hir/lowering/expr.rs b/src/librustc/hir/lowering/expr.rs
index 8939b5eef2660..8311b9168e455 100644
--- a/src/librustc/hir/lowering/expr.rs
+++ b/src/librustc/hir/lowering/expr.rs
@@ -122,8 +122,8 @@ impl LoweringContext<'_, '_> {
                 self.lower_block(blk, opt_label.is_some()),
                 self.lower_label(opt_label),
             ),
-            ExprKind::Assign(ref el, ref er) => {
-                hir::ExprKind::Assign(P(self.lower_expr(el)), P(self.lower_expr(er)))
+            ExprKind::Assign(ref el, ref er, span) => {
+                hir::ExprKind::Assign(P(self.lower_expr(el)), P(self.lower_expr(er)), span)
             }
             ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp(
                 self.lower_binop(op),
@@ -994,8 +994,11 @@ impl LoweringContext<'_, '_> {
             let (val_pat, val_pat_hid) = self.pat_ident(pat.span, val_ident);
             let val_expr = P(self.expr_ident(pat.span, val_ident, val_pat_hid));
             let next_expr = P(self.expr_ident(pat.span, next_ident, next_pat_hid));
-            let assign =
-                P(self.expr(pat.span, hir::ExprKind::Assign(next_expr, val_expr), ThinVec::new()));
+            let assign = P(self.expr(
+                pat.span,
+                hir::ExprKind::Assign(next_expr, val_expr, pat.span),
+                ThinVec::new(),
+            ));
             let some_pat = self.pat_some(pat.span, val_pat);
             self.arm(some_pat, assign)
         };
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 4aa8c12a219ca..457851bd7ecdd 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -1628,7 +1628,7 @@ pub enum ExprKind {
     Block(P<Block>, Option<Label>),
 
     /// An assignment (e.g., `a = foo()`).
-    Assign(P<Expr>, P<Expr>),
+    Assign(P<Expr>, P<Expr>, Span),
     /// An assignment with an operator.
     ///
     /// E.g., `a += 1`.
diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs
index 767e53f951901..2f3b6f82ee5ee 100644
--- a/src/librustc/hir/print.rs
+++ b/src/librustc/hir/print.rs
@@ -1289,7 +1289,7 @@ impl<'a> State<'a> {
                 self.ibox(0);
                 self.print_block(&blk);
             }
-            hir::ExprKind::Assign(ref lhs, ref rhs) => {
+            hir::ExprKind::Assign(ref lhs, ref rhs, _) => {
                 let prec = AssocOp::Assign.precedence() as i8;
                 self.print_expr_maybe_paren(&lhs, prec + 1);
                 self.s.space();
@@ -2265,7 +2265,7 @@ fn contains_exterior_struct_lit(value: &hir::Expr) -> bool {
     match value.kind {
         hir::ExprKind::Struct(..) => true,
 
-        hir::ExprKind::Assign(ref lhs, ref rhs)
+        hir::ExprKind::Assign(ref lhs, ref rhs, _)
         | hir::ExprKind::AssignOp(_, ref lhs, ref rhs)
         | hir::ExprKind::Binary(_, ref lhs, ref rhs) => {
             // `X { y: 1 } + X { y: 2 }`
diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs
index 6bdb77bb804f4..5f57aabe8d426 100644
--- a/src/librustc_lint/unused.rs
+++ b/src/librustc_lint/unused.rs
@@ -490,7 +490,7 @@ impl EarlyLintPass for UnusedParens {
                 (value, "`return` value", false, Some(left), None)
             }
 
-            Assign(_, ref value) => (value, "assigned value", false, None, None),
+            Assign(_, ref value, _) => (value, "assigned value", false, None, None),
             AssignOp(.., ref value) => (value, "assigned value", false, None, None),
             // either function/method call, or something this lint doesn't care about
             ref call_or_other => {
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index bfeb3c40e28e8..b5cd24bebc33a 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -227,7 +227,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr
 
         hir::ExprKind::Block(ref blk, _) => ExprKind::Block { body: &blk },
 
-        hir::ExprKind::Assign(ref lhs, ref rhs) => {
+        hir::ExprKind::Assign(ref lhs, ref rhs, _) => {
             ExprKind::Assign { lhs: lhs.to_ref(), rhs: rhs.to_ref() }
         }
 
diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs
index fa68ddf272a84..e0eb841f2c0cf 100644
--- a/src/librustc_parse/parser/expr.rs
+++ b/src/librustc_parse/parser/expr.rs
@@ -281,7 +281,9 @@ impl<'a> Parser<'a> {
                     let binary = self.mk_binary(source_map::respan(cur_op_span, ast_op), lhs, rhs);
                     self.mk_expr(span, binary, AttrVec::new())
                 }
-                AssocOp::Assign => self.mk_expr(span, ExprKind::Assign(lhs, rhs), AttrVec::new()),
+                AssocOp::Assign => {
+                    self.mk_expr(span, ExprKind::Assign(lhs, rhs, cur_op_span), AttrVec::new())
+                }
                 AssocOp::AssignOp(k) => {
                     let aop = match k {
                         token::Plus => BinOpKind::Add,
diff --git a/src/librustc_passes/liveness.rs b/src/librustc_passes/liveness.rs
index de532421bfb03..1a8abeb7abcdc 100644
--- a/src/librustc_passes/liveness.rs
+++ b/src/librustc_passes/liveness.rs
@@ -1079,7 +1079,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                     .unwrap_or_else(|| span_bug!(expr.span, "continue to unknown label"))
             }
 
-            hir::ExprKind::Assign(ref l, ref r) => {
+            hir::ExprKind::Assign(ref l, ref r, _) => {
                 // see comment on places in
                 // propagate_through_place_components()
                 let succ = self.write_place(&l, succ, ACC_WRITE);
@@ -1373,7 +1373,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
 
 fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr) {
     match expr.kind {
-        hir::ExprKind::Assign(ref l, _) => {
+        hir::ExprKind::Assign(ref l, ..) => {
             this.check_place(&l);
         }
 
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index f7e48907e0141..cdfcb8090e65e 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -1251,7 +1251,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
             return;
         }
         match expr.kind {
-            hir::ExprKind::Assign(.., ref rhs) | hir::ExprKind::Match(ref rhs, ..) => {
+            hir::ExprKind::Assign(_, ref rhs, _) | hir::ExprKind::Match(ref rhs, ..) => {
                 // Do not report duplicate errors for `x = y` and `match x { ... }`.
                 if self.check_expr_pat_type(rhs.hir_id, rhs.span) {
                     return;
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 944b4f8fe56f0..d63d30b7b8d83 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -492,7 +492,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             String::new()
                         };
                         if let Some(hir::Node::Expr(hir::Expr {
-                            kind: hir::ExprKind::Assign(left_expr, _),
+                            kind: hir::ExprKind::Assign(left_expr, ..),
                             ..
                         })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
                         {
diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index 13046d8002a9b..1ff2ea19b7c14 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -219,7 +219,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ExprKind::Box(ref subexpr) => self.check_expr_box(subexpr, expected),
             ExprKind::Lit(ref lit) => self.check_lit(&lit, expected),
             ExprKind::Binary(op, ref lhs, ref rhs) => self.check_binop(expr, op, lhs, rhs),
-            ExprKind::AssignOp(op, ref lhs, ref rhs) => self.check_binop_assign(expr, op, lhs, rhs),
+            ExprKind::Assign(ref lhs, ref rhs, ref span) => {
+                self.check_binop_assign(expr, op, lhs, rhs)
+            }
             ExprKind::Unary(unop, ref oprnd) => {
                 self.check_expr_unary(unop, oprnd, expected, needs, expr)
             }
@@ -245,7 +247,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             }
             ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr),
-            ExprKind::Assign(ref lhs, ref rhs) => self.check_expr_assign(expr, expected, lhs, rhs),
             ExprKind::Loop(ref body, _, source) => {
                 self.check_expr_loop(body, source, expected, expr)
             }
@@ -767,6 +768,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Expectation<'tcx>,
         lhs: &'tcx hir::Expr,
         rhs: &'tcx hir::Expr,
+        span: &Span,
     ) -> Ty<'tcx> {
         let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace);
         let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty);
@@ -789,7 +791,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             err.emit();
         } else {
-            self.check_lhs_assignable(lhs, "E0070", &expr.span);
+            self.check_lhs_assignable(lhs, "E0070", span);
         }
 
         self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized);
diff --git a/src/librustc_typeck/expr_use_visitor.rs b/src/librustc_typeck/expr_use_visitor.rs
index 58baf24438bd0..6c7e3658365a0 100644
--- a/src/librustc_typeck/expr_use_visitor.rs
+++ b/src/librustc_typeck/expr_use_visitor.rs
@@ -286,7 +286,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
                 }
             }
 
-            hir::ExprKind::Assign(ref lhs, ref rhs) => {
+            hir::ExprKind::Assign(ref lhs, ref rhs, _) => {
                 self.mutate_expr(lhs);
                 self.consume_expr(rhs);
             }
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 9009faa544004..c00fc761a6a25 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -1216,7 +1216,7 @@ pub enum ExprKind {
     TryBlock(P<Block>),
 
     /// An assignment (`a = foo()`).
-    Assign(P<Expr>, P<Expr>),
+    Assign(P<Expr>, P<Expr>, Span),
     /// An assignment with an operator.
     ///
     /// E.g., `a += 1`.
diff --git a/src/libsyntax/mut_visit.rs b/src/libsyntax/mut_visit.rs
index 780323d114eda..f6817c713a4a6 100644
--- a/src/libsyntax/mut_visit.rs
+++ b/src/libsyntax/mut_visit.rs
@@ -1168,7 +1168,7 @@ pub fn noop_visit_expr<T: MutVisitor>(Expr { kind, id, span, attrs }: &mut Expr,
             vis.visit_block(body);
         }
         ExprKind::Await(expr) => vis.visit_expr(expr),
-        ExprKind::Assign(el, er) => {
+        ExprKind::Assign(el, er, _) => {
             vis.visit_expr(el);
             vis.visit_expr(er);
         }
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index dc01f2472b7f0..b1b667f03bee2 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -2041,7 +2041,7 @@ impl<'a> State<'a> {
                 self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX);
                 self.s.word(".await");
             }
-            ast::ExprKind::Assign(ref lhs, ref rhs) => {
+            ast::ExprKind::Assign(ref lhs, ref rhs, _) => {
                 let prec = AssocOp::Assign.precedence() as i8;
                 self.print_expr_maybe_paren(lhs, prec + 1);
                 self.s.space();
diff --git a/src/libsyntax/util/parser.rs b/src/libsyntax/util/parser.rs
index 88e3d8daf7047..98af382efb083 100644
--- a/src/libsyntax/util/parser.rs
+++ b/src/libsyntax/util/parser.rs
@@ -378,7 +378,7 @@ pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
     match value.kind {
         ast::ExprKind::Struct(..) => true,
 
-        ast::ExprKind::Assign(ref lhs, ref rhs)
+        ast::ExprKind::Assign(ref lhs, ref rhs, _)
         | ast::ExprKind::AssignOp(_, ref lhs, ref rhs)
         | ast::ExprKind::Binary(_, ref lhs, ref rhs) => {
             // X { y: 1 } + X { y: 2 }
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index d6573a06647d1..fbc5d1332490f 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -766,9 +766,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
             visitor.visit_block(body);
         }
         ExprKind::Await(ref expr) => visitor.visit_expr(expr),
-        ExprKind::Assign(ref left_hand_expression, ref right_hand_expression) => {
-            visitor.visit_expr(left_hand_expression);
-            visitor.visit_expr(right_hand_expression);
+        ExprKind::Assign(ref lhs, ref rhs, _) => {
+            visitor.visit_expr(lhs);
+            visitor.visit_expr(rhs);
         }
         ExprKind::AssignOp(_, ref left_expression, ref right_expression) => {
             visitor.visit_expr(left_expression);
diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
index 0c050e314133e..36d47cea13b07 100644
--- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
+++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
@@ -126,8 +126,8 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
                                           DUMMY_SP)));
             },
             12 => {
-                iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(e, make_x())));
-                iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(make_x(), e)));
+                iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(e, make_x(), DUMMY_SP)));
+                iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(make_x(), e, DUMMY_SP)));
             },
             13 => {
                 iter_exprs(depth - 1, &mut |e| g(ExprKind::Field(e, Ident::from_str("f"))));
diff --git a/src/test/ui/bad/bad-expr-lhs.stderr b/src/test/ui/bad/bad-expr-lhs.stderr
index 61c25bb471c37..ce793d8910b13 100644
--- a/src/test/ui/bad/bad-expr-lhs.stderr
+++ b/src/test/ui/bad/bad-expr-lhs.stderr
@@ -1,8 +1,8 @@
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/bad-expr-lhs.rs:2:5
+  --> $DIR/bad-expr-lhs.rs:2:7
    |
 LL |     1 = 2;
-   |     -^^^^
+   |     - ^
    |     |
    |     cannot assign to this expression
 
@@ -15,18 +15,18 @@ LL |     1 += 2;
    |     cannot assign to this expression
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/bad-expr-lhs.rs:4:5
+  --> $DIR/bad-expr-lhs.rs:4:12
    |
 LL |     (1, 2) = (3, 4);
-   |     ------^^^^^^^^^
+   |     ------ ^
    |     |
    |     cannot assign to this expression
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/bad-expr-lhs.rs:7:5
+  --> $DIR/bad-expr-lhs.rs:7:12
    |
 LL |     (a, b) = (3, 4);
-   |     ------^^^^^^^^^
+   |     ------ ^
    |     |
    |     cannot assign to this expression
    |
@@ -34,10 +34,10 @@ LL |     (a, b) = (3, 4);
    = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/bad-expr-lhs.rs:9:5
+  --> $DIR/bad-expr-lhs.rs:9:10
    |
 LL |     None = Some(3);
-   |     ----^^^^^^^^^^
+   |     ---- ^
    |     |
    |     cannot assign to this expression
 
diff --git a/src/test/ui/bad/destructuring-assignment.stderr b/src/test/ui/bad/destructuring-assignment.stderr
index 845008b06937c..3e0925b565806 100644
--- a/src/test/ui/bad/destructuring-assignment.stderr
+++ b/src/test/ui/bad/destructuring-assignment.stderr
@@ -1,8 +1,8 @@
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/destructuring-assignment.rs:6:5
+  --> $DIR/destructuring-assignment.rs:6:12
    |
 LL |     (a, b) = (3, 4);
-   |     ------^^^^^^^^^
+   |     ------ ^
    |     |
    |     cannot assign to this expression
    |
@@ -31,10 +31,10 @@ LL |     (a, b) += (3, 4);
    = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/destructuring-assignment.rs:10:5
+  --> $DIR/destructuring-assignment.rs:10:12
    |
 LL |     [a, b] = [3, 4];
-   |     ------^^^^^^^^^
+   |     ------ ^
    |     |
    |     cannot assign to this expression
    |
@@ -63,10 +63,10 @@ LL |     [a, b] += [3, 4];
    = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/destructuring-assignment.rs:16:5
+  --> $DIR/destructuring-assignment.rs:16:22
    |
 LL |     S { x: a, y: b } = s;
-   |     ----------------^^^^
+   |     ---------------- ^
    |     |
    |     cannot assign to this expression
    |
@@ -95,10 +95,10 @@ LL |     S { x: a, y: b } += s;
    = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/destructuring-assignment.rs:20:5
+  --> $DIR/destructuring-assignment.rs:20:21
    |
 LL |     S { x: a, ..s } = S { x: 3, y: 4 };
-   |     ---------------^^^^^^^^^^^^^^^^^^^
+   |     --------------- ^
    |     |
    |     cannot assign to this expression
    |
@@ -106,10 +106,10 @@ LL |     S { x: a, ..s } = S { x: 3, y: 4 };
    = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/destructuring-assignment.rs:24:5
+  --> $DIR/destructuring-assignment.rs:24:17
    |
 LL |     ((a, b), c) = ((3, 4), 5);
-   |     -----------^^^^^^^^^^^^^^
+   |     ----------- ^
    |     |
    |     cannot assign to this expression
    |
diff --git a/src/test/ui/error-codes/E0070.stderr b/src/test/ui/error-codes/E0070.stderr
index 1fb812d94672f..d809bb18dee16 100644
--- a/src/test/ui/error-codes/E0070.stderr
+++ b/src/test/ui/error-codes/E0070.stderr
@@ -1,16 +1,16 @@
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/E0070.rs:6:5
+  --> $DIR/E0070.rs:6:16
    |
 LL |     SOME_CONST = 14;
-   |     ----------^^^^^
+   |     ---------- ^
    |     |
    |     cannot assign to this expression
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/E0070.rs:7:5
+  --> $DIR/E0070.rs:7:7
    |
 LL |     1 = 3;
-   |     -^^^^
+   |     - ^
    |     |
    |     cannot assign to this expression
 
@@ -21,10 +21,10 @@ LL |     some_other_func() = 4;
    |                         ^ expected `()`, found integer
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/E0070.rs:8:5
+  --> $DIR/E0070.rs:8:23
    |
 LL |     some_other_func() = 4;
-   |     -----------------^^^^
+   |     ----------------- ^
    |     |
    |     cannot assign to this expression
 
diff --git a/src/test/ui/issues/issue-13407.stderr b/src/test/ui/issues/issue-13407.stderr
index 05fd97b025f60..b280de3158fed 100644
--- a/src/test/ui/issues/issue-13407.stderr
+++ b/src/test/ui/issues/issue-13407.stderr
@@ -11,10 +11,10 @@ LL |     A::C = 1;
    |            ^ expected struct `A::C`, found integer
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/issue-13407.rs:6:5
+  --> $DIR/issue-13407.rs:6:10
    |
 LL |     A::C = 1;
-   |     ----^^^^
+   |     ---- ^
    |     |
    |     cannot assign to this expression
 
diff --git a/src/test/ui/issues/issue-26093.stderr b/src/test/ui/issues/issue-26093.stderr
index 48f72cef0a85a..c96228b518a85 100644
--- a/src/test/ui/issues/issue-26093.stderr
+++ b/src/test/ui/issues/issue-26093.stderr
@@ -1,8 +1,8 @@
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/issue-26093.rs:3:9
+  --> $DIR/issue-26093.rs:3:16
    |
 LL |         $thing = 42;
-   |         ^^^^^^^^^^^
+   |                ^
 ...
 LL |     not_a_place!(99);
    |     -----------------
diff --git a/src/test/ui/issues/issue-34334.stderr b/src/test/ui/issues/issue-34334.stderr
index e54f0c77cd973..3055e316a082a 100644
--- a/src/test/ui/issues/issue-34334.stderr
+++ b/src/test/ui/issues/issue-34334.stderr
@@ -36,10 +36,10 @@ LL |     let sr: Vec<(u32, _, _) = vec![];
    = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/issue-34334.rs:2:13
+  --> $DIR/issue-34334.rs:2:29
    |
 LL |     let sr: Vec<(u32, _, _) = vec![];
-   |             ---------------^^^^^^^^^
+   |             --------------- ^
    |             |
    |             cannot assign to this expression
 
diff --git a/src/test/ui/type/type-check/assignment-expected-bool.stderr b/src/test/ui/type/type-check/assignment-expected-bool.stderr
index bbd961f845016..3f1caddf728cb 100644
--- a/src/test/ui/type/type-check/assignment-expected-bool.stderr
+++ b/src/test/ui/type/type-check/assignment-expected-bool.stderr
@@ -98,10 +98,10 @@ LL |         || (0 = 0);
    |            help: try comparing for equality: `0 == 0`
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/assignment-expected-bool.rs:31:20
+  --> $DIR/assignment-expected-bool.rs:31:22
    |
 LL |     let _: usize = 0 = 0;
-   |                    -^^^^
+   |                    - ^
    |                    |
    |                    cannot assign to this expression
 

From 9a602243c422f183f574ff53e5d3e9e85fcc1210 Mon Sep 17 00:00:00 2001
From: varkor <github@varkor.com>
Date: Sun, 22 Dec 2019 23:09:54 +0000
Subject: [PATCH 16/21] Add new folder for destructuring assignment tests

---
 src/librustc/hir/mod.rs                       |  1 +
 src/librustc_typeck/check/expr.rs             |  2 +-
 src/libsyntax/ast.rs                          |  1 +
 src/test/ui/bad/bad-expr-lhs.stderr           |  2 +-
 .../note-unsupported.rs}                      |  0
 .../note-unsupported.stderr}                  | 38 +++++++++----------
 6 files changed, 23 insertions(+), 21 deletions(-)
 rename src/test/ui/{bad/destructuring-assignment.rs => destructuring-assignment/note-unsupported.rs} (100%)
 rename src/test/ui/{bad/destructuring-assignment.stderr => destructuring-assignment/note-unsupported.stderr} (76%)

diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 457851bd7ecdd..3c7860ae02fe7 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -1628,6 +1628,7 @@ pub enum ExprKind {
     Block(P<Block>, Option<Label>),
 
     /// An assignment (e.g., `a = foo()`).
+    /// The `Span` argument is the span of the `=` token.
     Assign(P<Expr>, P<Expr>, Span),
     /// An assignment with an operator.
     ///
diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index 1ff2ea19b7c14..542c2b16c602e 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -751,7 +751,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             );
             err.span_label(lhs.span, "cannot assign to this expression");
             if self.is_destructuring_place_expr(lhs) {
-                err.note("destructuring assignments are not yet supported");
+                err.note("destructuring assignments are not currently supported");
                 err.note(
                     "for more information, see https://github.com/rust-lang/rfcs/issues/372",
                 );
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index c00fc761a6a25..c98942abaf3c2 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -1216,6 +1216,7 @@ pub enum ExprKind {
     TryBlock(P<Block>),
 
     /// An assignment (`a = foo()`).
+    /// The `Span` argument is the span of the `=` token.
     Assign(P<Expr>, P<Expr>, Span),
     /// An assignment with an operator.
     ///
diff --git a/src/test/ui/bad/bad-expr-lhs.stderr b/src/test/ui/bad/bad-expr-lhs.stderr
index ce793d8910b13..a195e1054d099 100644
--- a/src/test/ui/bad/bad-expr-lhs.stderr
+++ b/src/test/ui/bad/bad-expr-lhs.stderr
@@ -30,7 +30,7 @@ LL |     (a, b) = (3, 4);
    |     |
    |     cannot assign to this expression
    |
-   = note: destructuring assignments are not yet supported
+   = note: destructuring assignments are not currently supported
    = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0070]: invalid left-hand side of assignment
diff --git a/src/test/ui/bad/destructuring-assignment.rs b/src/test/ui/destructuring-assignment/note-unsupported.rs
similarity index 100%
rename from src/test/ui/bad/destructuring-assignment.rs
rename to src/test/ui/destructuring-assignment/note-unsupported.rs
diff --git a/src/test/ui/bad/destructuring-assignment.stderr b/src/test/ui/destructuring-assignment/note-unsupported.stderr
similarity index 76%
rename from src/test/ui/bad/destructuring-assignment.stderr
rename to src/test/ui/destructuring-assignment/note-unsupported.stderr
index 3e0925b565806..a6805c32a6e52 100644
--- a/src/test/ui/bad/destructuring-assignment.stderr
+++ b/src/test/ui/destructuring-assignment/note-unsupported.stderr
@@ -1,16 +1,16 @@
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/destructuring-assignment.rs:6:12
+  --> $DIR/note-unsupported.rs:6:12
    |
 LL |     (a, b) = (3, 4);
    |     ------ ^
    |     |
    |     cannot assign to this expression
    |
-   = note: destructuring assignments are not yet supported
+   = note: destructuring assignments are not currently supported
    = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0368]: binary assignment operation `+=` cannot be applied to type `({integer}, {integer})`
-  --> $DIR/destructuring-assignment.rs:7:5
+  --> $DIR/note-unsupported.rs:7:5
    |
 LL |     (a, b) += (3, 4);
    |     ------^^^^^^^^^^
@@ -20,29 +20,29 @@ LL |     (a, b) += (3, 4);
    = note: an implementation of `std::ops::AddAssign` might be missing for `({integer}, {integer})`
 
 error[E0067]: invalid left-hand side of assignment
-  --> $DIR/destructuring-assignment.rs:7:12
+  --> $DIR/note-unsupported.rs:7:12
    |
 LL |     (a, b) += (3, 4);
    |     ------ ^^
    |     |
    |     cannot assign to this expression
    |
-   = note: destructuring assignments are not yet supported
+   = note: destructuring assignments are not currently supported
    = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/destructuring-assignment.rs:10:12
+  --> $DIR/note-unsupported.rs:10:12
    |
 LL |     [a, b] = [3, 4];
    |     ------ ^
    |     |
    |     cannot assign to this expression
    |
-   = note: destructuring assignments are not yet supported
+   = note: destructuring assignments are not currently supported
    = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0368]: binary assignment operation `+=` cannot be applied to type `[{integer}; 2]`
-  --> $DIR/destructuring-assignment.rs:11:5
+  --> $DIR/note-unsupported.rs:11:5
    |
 LL |     [a, b] += [3, 4];
    |     ------^^^^^^^^^^
@@ -52,29 +52,29 @@ LL |     [a, b] += [3, 4];
    = note: an implementation of `std::ops::AddAssign` might be missing for `[{integer}; 2]`
 
 error[E0067]: invalid left-hand side of assignment
-  --> $DIR/destructuring-assignment.rs:11:12
+  --> $DIR/note-unsupported.rs:11:12
    |
 LL |     [a, b] += [3, 4];
    |     ------ ^^
    |     |
    |     cannot assign to this expression
    |
-   = note: destructuring assignments are not yet supported
+   = note: destructuring assignments are not currently supported
    = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/destructuring-assignment.rs:16:22
+  --> $DIR/note-unsupported.rs:16:22
    |
 LL |     S { x: a, y: b } = s;
    |     ---------------- ^
    |     |
    |     cannot assign to this expression
    |
-   = note: destructuring assignments are not yet supported
+   = note: destructuring assignments are not currently supported
    = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0368]: binary assignment operation `+=` cannot be applied to type `S`
-  --> $DIR/destructuring-assignment.rs:17:5
+  --> $DIR/note-unsupported.rs:17:5
    |
 LL |     S { x: a, y: b } += s;
    |     ----------------^^^^^
@@ -84,36 +84,36 @@ LL |     S { x: a, y: b } += s;
    = note: an implementation of `std::ops::AddAssign` might be missing for `S`
 
 error[E0067]: invalid left-hand side of assignment
-  --> $DIR/destructuring-assignment.rs:17:22
+  --> $DIR/note-unsupported.rs:17:22
    |
 LL |     S { x: a, y: b } += s;
    |     ---------------- ^^
    |     |
    |     cannot assign to this expression
    |
-   = note: destructuring assignments are not yet supported
+   = note: destructuring assignments are not currently supported
    = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/destructuring-assignment.rs:20:21
+  --> $DIR/note-unsupported.rs:20:21
    |
 LL |     S { x: a, ..s } = S { x: 3, y: 4 };
    |     --------------- ^
    |     |
    |     cannot assign to this expression
    |
-   = note: destructuring assignments are not yet supported
+   = note: destructuring assignments are not currently supported
    = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/destructuring-assignment.rs:24:17
+  --> $DIR/note-unsupported.rs:24:17
    |
 LL |     ((a, b), c) = ((3, 4), 5);
    |     ----------- ^
    |     |
    |     cannot assign to this expression
    |
-   = note: destructuring assignments are not yet supported
+   = note: destructuring assignments are not currently supported
    = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error: aborting due to 11 previous errors

From dd7f49301eaf1b9783ee695a7ac43af69a0f4768 Mon Sep 17 00:00:00 2001
From: Mazdak Farrokhzad <twingoow@gmail.com>
Date: Sun, 22 Dec 2019 20:53:30 +0100
Subject: [PATCH 17/21] is_range_literal: fix fallout

---
 src/librustc/hir/mod.rs             | 21 +++++++++------------
 src/librustc_lint/types.rs          |  7 +++----
 src/librustc_typeck/check/demand.rs |  6 ++----
 3 files changed, 14 insertions(+), 20 deletions(-)

diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 55a30a7cc3a19..b4c51cca4d24d 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -29,10 +29,10 @@ use syntax::ast::{AttrVec, Attribute, FloatTy, IntTy, Label, LitKind, StrStyle,
 pub use syntax::ast::{BorrowKind, ImplPolarity, IsAuto};
 pub use syntax::ast::{CaptureBy, Constness, Movability, Mutability, Unsafety};
 use syntax::attr::{InlineAttr, OptimizeAttr};
-use syntax::source_map::Spanned;
-use syntax::symbol::{kw, Symbol};
 use syntax::tokenstream::TokenStream;
 use syntax::util::parser::ExprPrecedence;
+use syntax_pos::source_map::{SourceMap, Spanned};
+use syntax_pos::symbol::{kw, sym, Symbol};
 use syntax_pos::{MultiSpan, Span, DUMMY_SP};
 
 /// HIR doesn't commit to a concrete storage type and has its own alias for a vector.
@@ -1566,9 +1566,7 @@ impl fmt::Debug for Expr {
 
 /// Checks if the specified expression is a built-in range literal.
 /// (See: `LoweringContext::lower_expr()`).
-pub fn is_range_literal(sess: &Session, expr: &hir::Expr) -> bool {
-    use hir::{Path, QPath, ExprKind, TyKind};
-
+pub fn is_range_literal(sm: &SourceMap, expr: &Expr) -> bool {
     // Returns whether the given path represents a (desugared) range,
     // either in std or core, i.e. has either a `::std::ops::Range` or
     // `::core::ops::Range` prefix.
@@ -1586,11 +1584,10 @@ pub fn is_range_literal(sess: &Session, expr: &hir::Expr) -> bool {
 
     // Check whether a span corresponding to a range expression is a
     // range literal, rather than an explicit struct or `new()` call.
-    fn is_lit(sess: &Session, span: &Span) -> bool {
-        let source_map = sess.source_map();
-        let end_point = source_map.end_point(*span);
+    fn is_lit(sm: &SourceMap, span: &Span) -> bool {
+        let end_point = sm.end_point(*span);
 
-        if let Ok(end_string) = source_map.span_to_snippet(end_point) {
+        if let Ok(end_string) = sm.span_to_snippet(end_point) {
             !(end_string.ends_with("}") || end_string.ends_with(")"))
         } else {
             false
@@ -1601,13 +1598,13 @@ pub fn is_range_literal(sess: &Session, expr: &hir::Expr) -> bool {
         // All built-in range literals but `..=` and `..` desugar to `Struct`s.
         ExprKind::Struct(ref qpath, _, _) => {
             if let QPath::Resolved(None, ref path) = **qpath {
-                return is_range_path(&path) && is_lit(sess, &expr.span);
+                return is_range_path(&path) && is_lit(sm, &expr.span);
             }
         }
 
         // `..` desugars to its struct path.
         ExprKind::Path(QPath::Resolved(None, ref path)) => {
-            return is_range_path(&path) && is_lit(sess, &expr.span);
+            return is_range_path(&path) && is_lit(sm, &expr.span);
         }
 
         // `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
@@ -1615,7 +1612,7 @@ pub fn is_range_literal(sess: &Session, expr: &hir::Expr) -> bool {
             if let ExprKind::Path(QPath::TypeRelative(ref ty, ref segment)) = func.kind {
                 if let TyKind::Path(QPath::Resolved(None, ref path)) = ty.kind {
                     let new_call = segment.ident.name == sym::new;
-                    return is_range_path(&path) && is_lit(sess, &expr.span) && new_call;
+                    return is_range_path(&path) && is_lit(sm, &expr.span) && new_call;
                 }
             }
         }
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index 8c9279d025d80..45b8666c42b45 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -3,8 +3,7 @@
 use crate::hir::def_id::DefId;
 use lint::{LateContext, LintArray, LintContext};
 use lint::{LateLintPass, LintPass};
-use rustc::hir::lowering::is_range_literal;
-use rustc::hir::{ExprKind, Node};
+use rustc::hir::{is_range_literal, ExprKind, Node};
 use rustc::ty::layout::{self, IntegerExt, LayoutOf, SizeSkeleton, VariantIdx};
 use rustc::ty::subst::SubstsRef;
 use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt};
@@ -266,7 +265,7 @@ fn lint_int_literal<'a, 'tcx>(
         let par_id = cx.tcx.hir().get_parent_node(e.hir_id);
         if let Node::Expr(par_e) = cx.tcx.hir().get(par_id) {
             if let hir::ExprKind::Struct(..) = par_e.kind {
-                if is_range_literal(cx.sess(), par_e)
+                if is_range_literal(cx.sess().source_map(), par_e)
                     && lint_overflowing_range_endpoint(cx, lit, v, max, e, par_e, t.name_str())
                 {
                     // The overflowing literal lint was overridden.
@@ -318,7 +317,7 @@ fn lint_uint_literal<'a, 'tcx>(
                         return;
                     }
                 }
-                hir::ExprKind::Struct(..) if is_range_literal(cx.sess(), par_e) => {
+                hir::ExprKind::Struct(..) if is_range_literal(cx.sess().source_map(), par_e) => {
                     let t = t.name_str();
                     if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, par_e, t) {
                         // The overflowing literal lint was overridden.
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index 944b4f8fe56f0..3a25786365b48 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -3,9 +3,7 @@ use rustc::infer::InferOk;
 use rustc::traits::{self, ObligationCause, ObligationCauseCode};
 
 use errors::{Applicability, DiagnosticBuilder};
-use rustc::hir;
-use rustc::hir::Node;
-use rustc::hir::{lowering::is_range_literal, print};
+use rustc::hir::{self, is_range_literal, print, Node};
 use rustc::ty::adjustment::AllowTwoPhase;
 use rustc::ty::{self, AssocItem, Ty};
 use syntax::symbol::sym;
@@ -478,7 +476,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             // parenthesize if needed (Issue #46756)
                             hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
                             // parenthesize borrows of range literals (Issue #54505)
-                            _ if is_range_literal(self.tcx.sess, expr) => true,
+                            _ if is_range_literal(self.tcx.sess.source_map(), expr) => true,
                             _ => false,
                         };
                         let sugg_expr = if needs_parens { format!("({})", src) } else { src };

From 4bb83468a202c7402de529199dd363db5f307f33 Mon Sep 17 00:00:00 2001
From: Mazdak Farrokhzad <twingoow@gmail.com>
Date: Sun, 22 Dec 2019 20:55:27 +0100
Subject: [PATCH 18/21] is_range_literal: leave FIXME

---
 src/librustc/hir/mod.rs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index b4c51cca4d24d..915b0afc48fcb 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -1566,6 +1566,9 @@ impl fmt::Debug for Expr {
 
 /// Checks if the specified expression is a built-in range literal.
 /// (See: `LoweringContext::lower_expr()`).
+///
+/// FIXME(#60607): This function is a hack. If and when we have `QPath::Lang(...)`,
+/// we can use that instead as simpler, more reliable mechanism, as opposed to using `SourceMap`.
 pub fn is_range_literal(sm: &SourceMap, expr: &Expr) -> bool {
     // Returns whether the given path represents a (desugared) range,
     // either in std or core, i.e. has either a `::std::ops::Range` or

From 9e5081394cac4429de287e6c10b350f698a97f65 Mon Sep 17 00:00:00 2001
From: varkor <github@varkor.com>
Date: Mon, 23 Dec 2019 12:39:48 +0000
Subject: [PATCH 19/21] Fix reformatting rebase issues

---
 src/librustc_typeck/check/expr.rs | 11 +++++------
 src/librustc_typeck/check/op.rs   |  4 ++--
 2 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index 542c2b16c602e..5c602ad76cd32 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -220,8 +220,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ExprKind::Lit(ref lit) => self.check_lit(&lit, expected),
             ExprKind::Binary(op, ref lhs, ref rhs) => self.check_binop(expr, op, lhs, rhs),
             ExprKind::Assign(ref lhs, ref rhs, ref span) => {
-                self.check_binop_assign(expr, op, lhs, rhs)
+                self.check_expr_assign(expr, expected, lhs, rhs, span)
             }
+            ExprKind::AssignOp(op, ref lhs, ref rhs) => self.check_binop_assign(expr, op, lhs, rhs),
             ExprKind::Unary(unop, ref oprnd) => {
                 self.check_expr_unary(unop, oprnd, expected, needs, expr)
             }
@@ -730,8 +731,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 comps.iter().all(|e| self.is_destructuring_place_expr(e))
             }
             ExprKind::Struct(_path, fields, rest) => {
-                rest.as_ref().map(|e| self.is_destructuring_place_expr(e)).unwrap_or(true) &&
-                    fields.iter().all(|f| self.is_destructuring_place_expr(&f.expr))
+                rest.as_ref().map(|e| self.is_destructuring_place_expr(e)).unwrap_or(true)
+                    && fields.iter().all(|f| self.is_destructuring_place_expr(&f.expr))
             }
             _ => expr.is_syntactic_place_expr(),
         }
@@ -752,9 +753,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             err.span_label(lhs.span, "cannot assign to this expression");
             if self.is_destructuring_place_expr(lhs) {
                 err.note("destructuring assignments are not currently supported");
-                err.note(
-                    "for more information, see https://github.com/rust-lang/rfcs/issues/372",
-                );
+                err.note("for more information, see https://github.com/rust-lang/rfcs/issues/372");
             }
             err.emit();
         }
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index dfa55e324a625..c5d3aac136bcb 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -26,8 +26,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes);
 
         let ty =
-            if !lhs.is_ty_var() && !rhs.is_ty_var() && is_builtin_binop(lhs, rhs, op) {
-                self.enforce_builtin_binop_types(lhs_expr, lhs, rhs_expr, rhs, op);
+            if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
+                self.enforce_builtin_binop_types(lhs, lhs_ty, rhs, rhs_ty, op);
                 self.tcx.mk_unit()
             } else {
                 return_ty

From a08df28a6fdb5a2c781f01082e630faa2d7ccde9 Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Mon, 23 Dec 2019 08:13:12 -0500
Subject: [PATCH 20/21] Document that calling Drop, even after it panics, is UB

---
 src/libcore/ops/drop.rs | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/libcore/ops/drop.rs b/src/libcore/ops/drop.rs
index eae63ea2390a8..5233b475c4646 100644
--- a/src/libcore/ops/drop.rs
+++ b/src/libcore/ops/drop.rs
@@ -99,9 +99,15 @@ pub trait Drop {
     /// Given that a [`panic!`] will call `drop` as it unwinds, any [`panic!`]
     /// in a `drop` implementation will likely abort.
     ///
+    /// Note that even if this panics, the value is considered to be dropped;
+    /// you must not cause `drop` to be called again. This is normally automatically
+    /// handled by the compiler, but when using unsafe code, can sometimes occur
+    /// unintentionally, particularly when using [`std::ptr::drop_in_place`].
+    ///
     /// [E0040]: ../../error-index.html#E0040
     /// [`panic!`]: ../macro.panic.html
     /// [`std::mem::drop`]: ../../std/mem/fn.drop.html
+    /// [`std::ptr::drop_in_place`]: ../../std/ptr/fn.drop_in_place.html
     #[stable(feature = "rust1", since = "1.0.0")]
     fn drop(&mut self);
 }

From 63d28221c62be5c63d6df4ac83043d9cacd2a226 Mon Sep 17 00:00:00 2001
From: Oliver Scherer <github35764891676564198441@oli-obk.de>
Date: Mon, 23 Dec 2019 15:11:09 +0100
Subject: [PATCH 21/21] Fix typo

---
 src/libcore/intrinsics.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs
index 544fd4fb99fa1..6481789c1dd00 100644
--- a/src/libcore/intrinsics.rs
+++ b/src/libcore/intrinsics.rs
@@ -1337,7 +1337,7 @@ extern "rust-intrinsic" {
     /// Performs rotate right.
     /// The stabilized versions of this intrinsic are available on the integer
     /// primitives via the `rotate_right` method. For example,
-    /// [`std::u32::rotate_right`](../../std/primitive.u32.html#method.rotate_right
+    /// [`std::u32::rotate_right`](../../std/primitive.u32.html#method.rotate_right)
     #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")]
     pub fn rotate_right<T>(x: T, y: T) -> T;