From 34b51187ce8fa261ebe3bef4a9a26135b919ac02 Mon Sep 17 00:00:00 2001
From: Stanislav Tkach <stanislav.tkach@gmail.com>
Date: Wed, 20 May 2020 17:48:21 +0300
Subject: [PATCH 01/26] Suggest using std::mem::drop function instead of
 explicit destructor call

---
 src/librustc_typeck/check/callee.rs           | 26 ++++++++++++++++---
 src/librustc_typeck/check/method/confirm.rs   |  9 ++++---
 src/librustc_typeck/check/mod.rs              |  2 +-
 src/test/ui/error-codes/E0040.stderr          |  5 +++-
 .../ui/explicit/explicit-call-to-dtor.stderr  |  5 +++-
 .../explicit-call-to-supertrait-dtor.stderr   |  5 +++-
 src/test/ui/illegal-ufcs-drop.stderr          |  5 +++-
 7 files changed, 45 insertions(+), 12 deletions(-)

diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs
index 52ddacc1c4b1a..4c31363f2c084 100644
--- a/src/librustc_typeck/check/callee.rs
+++ b/src/librustc_typeck/check/callee.rs
@@ -21,11 +21,29 @@ use rustc_target::spec::abi;
 /// Checks that it is legal to call methods of the trait corresponding
 /// to `trait_id` (this only cares about the trait, not the specific
 /// method that is called).
-pub fn check_legal_trait_for_method_call(tcx: TyCtxt<'_>, span: Span, trait_id: DefId) {
+pub fn check_legal_trait_for_method_call(
+    tcx: TyCtxt<'_>,
+    span: Span,
+    receiver: Option<Span>,
+    trait_id: DefId,
+) {
     if tcx.lang_items().drop_trait() == Some(trait_id) {
-        struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method")
-            .span_label(span, "explicit destructor calls not allowed")
-            .emit();
+        let mut err = struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method");
+        err.span_label(span, "explicit destructor calls not allowed");
+
+        let snippet = receiver
+            .and_then(|s| tcx.sess.source_map().span_to_snippet(s).ok())
+            .unwrap_or_default();
+
+        let (suggestion, applicability) = if snippet.is_empty() {
+            (snippet, Applicability::Unspecified)
+        } else {
+            (format!("drop({})", snippet), Applicability::MachineApplicable)
+        };
+
+        err.span_suggestion(span, "consider using `drop` function", suggestion, applicability);
+
+        err.emit();
     }
 }
 
diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs
index c4805c54a7d43..38a5bd5fce00d 100644
--- a/src/librustc_typeck/check/method/confirm.rs
+++ b/src/librustc_typeck/check/method/confirm.rs
@@ -597,9 +597,12 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
     fn enforce_illegal_method_limitations(&self, pick: &probe::Pick<'_>) {
         // Disallow calls to the method `drop` defined in the `Drop` trait.
         match pick.item.container {
-            ty::TraitContainer(trait_def_id) => {
-                callee::check_legal_trait_for_method_call(self.tcx, self.span, trait_def_id)
-            }
+            ty::TraitContainer(trait_def_id) => callee::check_legal_trait_for_method_call(
+                self.tcx,
+                self.span,
+                Some(self.self_expr.span),
+                trait_def_id,
+            ),
             ty::ImplContainer(..) => {}
         }
     }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index d72c74e4188ee..3ac3934bea32b 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -5438,7 +5438,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container);
                 match container {
                     ty::TraitContainer(trait_did) => {
-                        callee::check_legal_trait_for_method_call(tcx, span, trait_did)
+                        callee::check_legal_trait_for_method_call(tcx, span, None, trait_did)
                     }
                     ty::ImplContainer(impl_def_id) => {
                         if segments.len() == 1 {
diff --git a/src/test/ui/error-codes/E0040.stderr b/src/test/ui/error-codes/E0040.stderr
index 966455902817d..69cf28b29704f 100644
--- a/src/test/ui/error-codes/E0040.stderr
+++ b/src/test/ui/error-codes/E0040.stderr
@@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method
   --> $DIR/E0040.rs:13:7
    |
 LL |     x.drop();
-   |       ^^^^ explicit destructor calls not allowed
+   |       ^^^^
+   |       |
+   |       explicit destructor calls not allowed
+   |       help: consider using `drop` function: `drop(x)`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/explicit/explicit-call-to-dtor.stderr b/src/test/ui/explicit/explicit-call-to-dtor.stderr
index cbbe967179ef3..5ebe4ee4b90f8 100644
--- a/src/test/ui/explicit/explicit-call-to-dtor.stderr
+++ b/src/test/ui/explicit/explicit-call-to-dtor.stderr
@@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method
   --> $DIR/explicit-call-to-dtor.rs:13:7
    |
 LL |     x.drop();
-   |       ^^^^ explicit destructor calls not allowed
+   |       ^^^^
+   |       |
+   |       explicit destructor calls not allowed
+   |       help: consider using `drop` function: `drop(x)`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr b/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr
index 0b302e30b64f3..cd3fb3119a5cf 100644
--- a/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr
+++ b/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr
@@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method
   --> $DIR/explicit-call-to-supertrait-dtor.rs:17:14
    |
 LL |         self.drop();
-   |              ^^^^ explicit destructor calls not allowed
+   |              ^^^^
+   |              |
+   |              explicit destructor calls not allowed
+   |              help: consider using `drop` function: `drop(self)`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/illegal-ufcs-drop.stderr b/src/test/ui/illegal-ufcs-drop.stderr
index d35d376962c17..922d50d259569 100644
--- a/src/test/ui/illegal-ufcs-drop.stderr
+++ b/src/test/ui/illegal-ufcs-drop.stderr
@@ -2,7 +2,10 @@ error[E0040]: explicit use of destructor method
   --> $DIR/illegal-ufcs-drop.rs:8:5
    |
 LL |     Drop::drop(&mut Foo)
-   |     ^^^^^^^^^^ explicit destructor calls not allowed
+   |     ^^^^^^^^^^
+   |     |
+   |     explicit destructor calls not allowed
+   |     help: consider using `drop` function
 
 error: aborting due to previous error
 

From a9199de34d0ad78fe1ff0d072f6bd5c01906bdb3 Mon Sep 17 00:00:00 2001
From: Stanislav Tkach <stanislav.tkach@gmail.com>
Date: Sat, 23 May 2020 00:16:17 +0300
Subject: [PATCH 02/26] Merge spans for the suggestion

---
 src/librustc_typeck/check/callee.rs             | 17 +++++++++++------
 src/test/ui/error-codes/E0040.stderr            |  8 ++++----
 .../ui/explicit/explicit-call-to-dtor.stderr    |  8 ++++----
 .../explicit-call-to-supertrait-dtor.stderr     |  8 ++++----
 src/test/ui/illegal-ufcs-drop.stderr            |  2 +-
 5 files changed, 24 insertions(+), 19 deletions(-)

diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs
index 4c31363f2c084..a32174a83337d 100644
--- a/src/librustc_typeck/check/callee.rs
+++ b/src/librustc_typeck/check/callee.rs
@@ -35,13 +35,18 @@ pub fn check_legal_trait_for_method_call(
             .and_then(|s| tcx.sess.source_map().span_to_snippet(s).ok())
             .unwrap_or_default();
 
-        let (suggestion, applicability) = if snippet.is_empty() {
-            (snippet, Applicability::Unspecified)
-        } else {
-            (format!("drop({})", snippet), Applicability::MachineApplicable)
-        };
+        let suggestion =
+            if snippet.is_empty() { "drop".to_string() } else { format!("drop({})", snippet) };
+
+        let suggestion_span =
+            receiver.and_then(|s| tcx.sess.source_map().merge_spans(s, span)).unwrap_or(span);
 
-        err.span_suggestion(span, "consider using `drop` function", suggestion, applicability);
+        err.span_suggestion(
+            suggestion_span,
+            "consider using `drop` function",
+            suggestion,
+            Applicability::MaybeIncorrect,
+        );
 
         err.emit();
     }
diff --git a/src/test/ui/error-codes/E0040.stderr b/src/test/ui/error-codes/E0040.stderr
index 69cf28b29704f..3b864d4ea4b2c 100644
--- a/src/test/ui/error-codes/E0040.stderr
+++ b/src/test/ui/error-codes/E0040.stderr
@@ -2,10 +2,10 @@ error[E0040]: explicit use of destructor method
   --> $DIR/E0040.rs:13:7
    |
 LL |     x.drop();
-   |       ^^^^
-   |       |
-   |       explicit destructor calls not allowed
-   |       help: consider using `drop` function: `drop(x)`
+   |     --^^^^
+   |     | |
+   |     | explicit destructor calls not allowed
+   |     help: consider using `drop` function: `drop(x)`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/explicit/explicit-call-to-dtor.stderr b/src/test/ui/explicit/explicit-call-to-dtor.stderr
index 5ebe4ee4b90f8..33ce235b30fda 100644
--- a/src/test/ui/explicit/explicit-call-to-dtor.stderr
+++ b/src/test/ui/explicit/explicit-call-to-dtor.stderr
@@ -2,10 +2,10 @@ error[E0040]: explicit use of destructor method
   --> $DIR/explicit-call-to-dtor.rs:13:7
    |
 LL |     x.drop();
-   |       ^^^^
-   |       |
-   |       explicit destructor calls not allowed
-   |       help: consider using `drop` function: `drop(x)`
+   |     --^^^^
+   |     | |
+   |     | explicit destructor calls not allowed
+   |     help: consider using `drop` function: `drop(x)`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr b/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr
index cd3fb3119a5cf..2e7bfac71cd32 100644
--- a/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr
+++ b/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr
@@ -2,10 +2,10 @@ error[E0040]: explicit use of destructor method
   --> $DIR/explicit-call-to-supertrait-dtor.rs:17:14
    |
 LL |         self.drop();
-   |              ^^^^
-   |              |
-   |              explicit destructor calls not allowed
-   |              help: consider using `drop` function: `drop(self)`
+   |         -----^^^^
+   |         |    |
+   |         |    explicit destructor calls not allowed
+   |         help: consider using `drop` function: `drop(self)`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/illegal-ufcs-drop.stderr b/src/test/ui/illegal-ufcs-drop.stderr
index 922d50d259569..57c99739afd24 100644
--- a/src/test/ui/illegal-ufcs-drop.stderr
+++ b/src/test/ui/illegal-ufcs-drop.stderr
@@ -5,7 +5,7 @@ LL |     Drop::drop(&mut Foo)
    |     ^^^^^^^^^^
    |     |
    |     explicit destructor calls not allowed
-   |     help: consider using `drop` function
+   |     help: consider using `drop` function: `drop`
 
 error: aborting due to previous error
 

From 68bab3e56e7df618c093f5b93901f5319ef65997 Mon Sep 17 00:00:00 2001
From: Pyry Kontio <pyry.kontio@drasa.eu>
Date: Sat, 23 May 2020 04:41:24 +0900
Subject: [PATCH 03/26] Add total_cmp to f32 and f64, plus tests

---
 src/libcore/num/f32.rs |  73 +++++++++++++++++++++
 src/libcore/num/f64.rs |  73 +++++++++++++++++++++
 src/libstd/f32.rs      | 143 +++++++++++++++++++++++++++++++++++++++++
 src/libstd/f64.rs      | 143 +++++++++++++++++++++++++++++++++++++++++
 src/libstd/lib.rs      |   1 +
 5 files changed, 433 insertions(+)

diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs
index 434569020d2a8..47f489c40abb1 100644
--- a/src/libcore/num/f32.rs
+++ b/src/libcore/num/f32.rs
@@ -810,4 +810,77 @@ impl f32 {
     pub fn from_ne_bytes(bytes: [u8; 4]) -> Self {
         Self::from_bits(u32::from_ne_bytes(bytes))
     }
+
+    /// Returns an ordering between self and other values.
+    /// Unlike the standard partial comparison between floating point numbers,
+    /// this comparison always produces an ordering in accordance to
+    /// the totalOrder predicate as defined in IEEE 754 (2008 revision)
+    /// floating point standard. The values are ordered in following order:
+    /// - Negative quiet NaN
+    /// - Negative signaling NaN
+    /// - Negative infinity
+    /// - Negative numbers
+    /// - Negative subnormal numbers
+    /// - Negative zero
+    /// - Positive zero
+    /// - Positive subnormal numbers
+    /// - Positive numbers
+    /// - Positive infinity
+    /// - Positive signaling NaN
+    /// - Positive quiet NaN
+    ///
+    /// # Example
+    /// ```
+    /// #![feature(total_cmp)]
+    /// struct GoodBoy {
+    ///     name: String,
+    ///     weight: f32,
+    /// }
+    ///
+    /// let mut bois = vec![
+    ///     GoodBoy { name: "Pucci".to_owned(), weight: 0.1 },
+    ///     GoodBoy { name: "Woofer".to_owned(), weight: 99.0 },
+    ///     GoodBoy { name: "Yapper".to_owned(), weight: 10.0 },
+    ///     GoodBoy { name: "Chonk".to_owned(), weight: f32::INFINITY },
+    ///     GoodBoy { name: "Abs. Unit".to_owned(), weight: f32::NAN },
+    ///     GoodBoy { name: "Floaty".to_owned(), weight: -5.0 },
+    /// ];
+    ///
+    /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight));
+    /// # assert!(bois.into_iter().map(|b| b.weight)
+    /// #     .zip([-5.0, 0.1, 10.0, 99.0, f32::INFINITY, f32::NAN].iter())
+    /// #     .all(|(a, b)| a.to_bits() == b.to_bits()))
+    /// ```
+    #[must_use = "method returns a new number and does not mutate the original value"]
+    #[unstable(feature = "total_cmp", issue = "none")]
+    #[inline]
+    pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
+        let mut left = self.to_bits() as i32;
+        let mut right = other.to_bits() as i32;
+
+        // In case of negatives, flip all the bits expect the sign
+        // to achieve a similar layout as two's complement integers
+        //
+        // Why does this work? IEEE 754 floats consist of three fields:
+        // Sign bit, exponent and mantissa. The set of exponent and mantissa
+        // fields as a whole have the property that their bitwise order is
+        // equal to the numeric magnitude where the magnitude is defined.
+        // The magnitude is not normally defined on NaN values, but
+        // IEEE 754 totalOrder defines the NaN values also to follow the
+        // bitwise order. This leads to order explained in the doc comment.
+        // However, the representation of magnitude is the same for negative
+        // and positive numbers – only the sign bit is different.
+        // To easily compare the floats as signed integers, we need to
+        // flip the exponent and mantissa bits in case of negative numbers.
+        // We effectively convert the numbers to "two's complement" form.
+        if left < 0 {
+            // i32::MAX corresponds the bit pattern of "all ones expect for the sign bit"
+            left ^= i32::MAX
+        };
+        if right < 0 {
+            right ^= i32::MAX
+        };
+
+        left.cmp(&right)
+    }
 }
diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs
index 6476ddb4541ff..9c02180838014 100644
--- a/src/libcore/num/f64.rs
+++ b/src/libcore/num/f64.rs
@@ -824,4 +824,77 @@ impl f64 {
     pub fn from_ne_bytes(bytes: [u8; 8]) -> Self {
         Self::from_bits(u64::from_ne_bytes(bytes))
     }
+
+    /// Returns an ordering between self and other values.
+    /// Unlike the standard partial comparison between floating point numbers,
+    /// this comparison always produces an ordering in accordance to
+    /// the totalOrder predicate as defined in IEEE 754 (2008 revision)
+    /// floating point standard. The values are ordered in following order:
+    /// - Negative quiet NaN
+    /// - Negative signaling NaN
+    /// - Negative infinity
+    /// - Negative numbers
+    /// - Negative subnormal numbers
+    /// - Negative zero
+    /// - Positive zero
+    /// - Positive subnormal numbers
+    /// - Positive numbers
+    /// - Positive infinity
+    /// - Positive signaling NaN
+    /// - Positive quiet NaN
+    ///
+    /// # Example
+    /// ```
+    /// #![feature(total_cmp)]
+    /// struct GoodBoy {
+    ///     name: String,
+    ///     weight: f64,
+    /// }
+    ///
+    /// let mut bois = vec![
+    ///     GoodBoy { name: "Pucci".to_owned(), weight: 0.1 },
+    ///     GoodBoy { name: "Woofer".to_owned(), weight: 99.0 },
+    ///     GoodBoy { name: "Yapper".to_owned(), weight: 10.0 },
+    ///     GoodBoy { name: "Chonk".to_owned(), weight: f64::INFINITY },
+    ///     GoodBoy { name: "Abs. Unit".to_owned(), weight: f64::NAN },
+    ///     GoodBoy { name: "Floaty".to_owned(), weight: -5.0 },
+    /// ];
+    ///
+    /// bois.sort_by(|a, b| a.weight.total_cmp(&b.weight));
+    /// # assert!(bois.into_iter().map(|b| b.weight)
+    /// #     .zip([-5.0, 0.1, 10.0, 99.0, f64::INFINITY, f64::NAN].iter())
+    /// #     .all(|(a, b)| a.to_bits() == b.to_bits()))
+    /// ```
+    #[must_use = "method returns a new number and does not mutate the original value"]
+    #[unstable(feature = "total_cmp", issue = "none")]
+    #[inline]
+    pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
+        let mut left = self.to_bits() as i64;
+        let mut right = other.to_bits() as i64;
+
+        // In case of negatives, flip all the bits expect the sign
+        // to achieve a similar layout as two's complement integers
+        //
+        // Why does this work? IEEE 754 floats consist of three fields:
+        // Sign bit, exponent and mantissa. The set of exponent and mantissa
+        // fields as a whole have the property that their bitwise order is
+        // equal to the numeric magnitude where the magnitude is defined.
+        // The magnitude is not normally defined on NaN values, but
+        // IEEE 754 totalOrder defines the NaN values also to follow the
+        // bitwise order. This leads to order explained in the doc comment.
+        // However, the representation of magnitude is the same for negative
+        // and positive numbers – only the sign bit is different.
+        // To easily compare the floats as signed integers, we need to
+        // flip the exponent and mantissa bits in case of negative numbers.
+        // We effectively convert the numbers to "two's complement" form.
+        if left < 0 {
+            // i64::MAX corresponds the bit pattern of "all ones expect for the sign bit"
+            left ^= i64::MAX
+        };
+        if right < 0 {
+            right ^= i64::MAX
+        };
+
+        left.cmp(&right)
+    }
 }
diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs
index 8e743ace99bfb..c5d8bb4fcf243 100644
--- a/src/libstd/f32.rs
+++ b/src/libstd/f32.rs
@@ -1531,4 +1531,147 @@ mod tests {
     fn test_clamp_max_is_nan() {
         let _ = 1.0f32.clamp(3.0, NAN);
     }
+
+    #[test]
+    fn test_total_cmp() {
+        use core::cmp::Ordering;
+
+        fn quiet_bit_mask() -> u32 {
+            1 << (f32::MANTISSA_DIGITS - 2)
+        }
+
+        fn min_subnorm() -> f32 {
+            f32::MIN_POSITIVE / f32::powf(2.0, f32::MANTISSA_DIGITS as f32 - 1.0)
+        }
+
+        fn max_subnorm() -> f32 {
+            f32::MIN_POSITIVE - min_subnorm()
+        }
+
+        fn q_nan() -> f32 {
+            f32::from_bits(f32::NAN.to_bits() | quiet_bit_mask())
+        }
+
+        fn s_nan() -> f32 {
+            f32::from_bits((f32::NAN.to_bits() & !quiet_bit_mask()) + 42)
+        }
+
+        assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan()));
+        assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan()));
+        assert_eq!(Ordering::Equal, (-f32::INFINITY).total_cmp(&-f32::INFINITY));
+        assert_eq!(Ordering::Equal, (-f32::MAX).total_cmp(&-f32::MAX));
+        assert_eq!(Ordering::Equal, (-2.5_f32).total_cmp(&-2.5));
+        assert_eq!(Ordering::Equal, (-1.0_f32).total_cmp(&-1.0));
+        assert_eq!(Ordering::Equal, (-1.5_f32).total_cmp(&-1.5));
+        assert_eq!(Ordering::Equal, (-0.5_f32).total_cmp(&-0.5));
+        assert_eq!(Ordering::Equal, (-f32::MIN_POSITIVE).total_cmp(&-f32::MIN_POSITIVE));
+        assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm()));
+        assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm()));
+        assert_eq!(Ordering::Equal, (-0.0_f32).total_cmp(&-0.0));
+        assert_eq!(Ordering::Equal, 0.0_f32.total_cmp(&0.0));
+        assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm()));
+        assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm()));
+        assert_eq!(Ordering::Equal, f32::MIN_POSITIVE.total_cmp(&f32::MIN_POSITIVE));
+        assert_eq!(Ordering::Equal, 0.5_f32.total_cmp(&0.5));
+        assert_eq!(Ordering::Equal, 1.0_f32.total_cmp(&1.0));
+        assert_eq!(Ordering::Equal, 1.5_f32.total_cmp(&1.5));
+        assert_eq!(Ordering::Equal, 2.5_f32.total_cmp(&2.5));
+        assert_eq!(Ordering::Equal, f32::MAX.total_cmp(&f32::MAX));
+        assert_eq!(Ordering::Equal, f32::INFINITY.total_cmp(&f32::INFINITY));
+        assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan()));
+        assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan()));
+
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY));
+        assert_eq!(Ordering::Less, (-f32::INFINITY).total_cmp(&-f32::MAX));
+        assert_eq!(Ordering::Less, (-f32::MAX).total_cmp(&-2.5));
+        assert_eq!(Ordering::Less, (-2.5_f32).total_cmp(&-1.5));
+        assert_eq!(Ordering::Less, (-1.5_f32).total_cmp(&-1.0));
+        assert_eq!(Ordering::Less, (-1.0_f32).total_cmp(&-0.5));
+        assert_eq!(Ordering::Less, (-0.5_f32).total_cmp(&-f32::MIN_POSITIVE));
+        assert_eq!(Ordering::Less, (-f32::MIN_POSITIVE).total_cmp(&-max_subnorm()));
+        assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm()));
+        assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0));
+        assert_eq!(Ordering::Less, (-0.0_f32).total_cmp(&0.0));
+        assert_eq!(Ordering::Less, 0.0_f32.total_cmp(&min_subnorm()));
+        assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm()));
+        assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f32::MIN_POSITIVE));
+        assert_eq!(Ordering::Less, f32::MIN_POSITIVE.total_cmp(&0.5));
+        assert_eq!(Ordering::Less, 0.5_f32.total_cmp(&1.0));
+        assert_eq!(Ordering::Less, 1.0_f32.total_cmp(&1.5));
+        assert_eq!(Ordering::Less, 1.5_f32.total_cmp(&2.5));
+        assert_eq!(Ordering::Less, 2.5_f32.total_cmp(&f32::MAX));
+        assert_eq!(Ordering::Less, f32::MAX.total_cmp(&f32::INFINITY));
+        assert_eq!(Ordering::Less, f32::INFINITY.total_cmp(&s_nan()));
+        assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan()));
+
+        assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan()));
+        assert_eq!(Ordering::Greater, (-f32::INFINITY).total_cmp(&-s_nan()));
+        assert_eq!(Ordering::Greater, (-f32::MAX).total_cmp(&-f32::INFINITY));
+        assert_eq!(Ordering::Greater, (-2.5_f32).total_cmp(&-f32::MAX));
+        assert_eq!(Ordering::Greater, (-1.5_f32).total_cmp(&-2.5));
+        assert_eq!(Ordering::Greater, (-1.0_f32).total_cmp(&-1.5));
+        assert_eq!(Ordering::Greater, (-0.5_f32).total_cmp(&-1.0));
+        assert_eq!(Ordering::Greater, (-f32::MIN_POSITIVE).total_cmp(&-0.5));
+        assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f32::MIN_POSITIVE));
+        assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm()));
+        assert_eq!(Ordering::Greater, (-0.0_f32).total_cmp(&-min_subnorm()));
+        assert_eq!(Ordering::Greater, 0.0_f32.total_cmp(&-0.0));
+        assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0));
+        assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm()));
+        assert_eq!(Ordering::Greater, f32::MIN_POSITIVE.total_cmp(&max_subnorm()));
+        assert_eq!(Ordering::Greater, 0.5_f32.total_cmp(&f32::MIN_POSITIVE));
+        assert_eq!(Ordering::Greater, 1.0_f32.total_cmp(&0.5));
+        assert_eq!(Ordering::Greater, 1.5_f32.total_cmp(&1.0));
+        assert_eq!(Ordering::Greater, 2.5_f32.total_cmp(&1.5));
+        assert_eq!(Ordering::Greater, f32::MAX.total_cmp(&2.5));
+        assert_eq!(Ordering::Greater, f32::INFINITY.total_cmp(&f32::MAX));
+        assert_eq!(Ordering::Greater, s_nan().total_cmp(&f32::INFINITY));
+        assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan()));
+
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::INFINITY));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MAX));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f32::MIN_POSITIVE));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm()));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm()));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm()));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm()));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MIN_POSITIVE));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::MAX));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f32::INFINITY));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan()));
+
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::INFINITY));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MAX));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f32::MIN_POSITIVE));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm()));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm()));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm()));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm()));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MIN_POSITIVE));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::MAX));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f32::INFINITY));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
+    }
 }
diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs
index fe64d27b1efc8..eae8a04bfc637 100644
--- a/src/libstd/f64.rs
+++ b/src/libstd/f64.rs
@@ -1554,4 +1554,147 @@ mod tests {
     fn test_clamp_max_is_nan() {
         let _ = 1.0f64.clamp(3.0, NAN);
     }
+
+    #[test]
+    fn test_total_cmp() {
+        use core::cmp::Ordering;
+
+        fn quiet_bit_mask() -> u64 {
+            1 << (f64::MANTISSA_DIGITS - 2)
+        }
+
+        fn min_subnorm() -> f64 {
+            f64::MIN_POSITIVE / f64::powf(2.0, f64::MANTISSA_DIGITS as f64 - 1.0)
+        }
+
+        fn max_subnorm() -> f64 {
+            f64::MIN_POSITIVE - min_subnorm()
+        }
+
+        fn q_nan() -> f64 {
+            f64::from_bits(f64::NAN.to_bits() | quiet_bit_mask())
+        }
+
+        fn s_nan() -> f64 {
+            f64::from_bits((f64::NAN.to_bits() & !quiet_bit_mask()) + 42)
+        }
+
+        assert_eq!(Ordering::Equal, (-q_nan()).total_cmp(&-q_nan()));
+        assert_eq!(Ordering::Equal, (-s_nan()).total_cmp(&-s_nan()));
+        assert_eq!(Ordering::Equal, (-f64::INFINITY).total_cmp(&-f64::INFINITY));
+        assert_eq!(Ordering::Equal, (-f64::MAX).total_cmp(&-f64::MAX));
+        assert_eq!(Ordering::Equal, (-2.5_f64).total_cmp(&-2.5));
+        assert_eq!(Ordering::Equal, (-1.0_f64).total_cmp(&-1.0));
+        assert_eq!(Ordering::Equal, (-1.5_f64).total_cmp(&-1.5));
+        assert_eq!(Ordering::Equal, (-0.5_f64).total_cmp(&-0.5));
+        assert_eq!(Ordering::Equal, (-f64::MIN_POSITIVE).total_cmp(&-f64::MIN_POSITIVE));
+        assert_eq!(Ordering::Equal, (-max_subnorm()).total_cmp(&-max_subnorm()));
+        assert_eq!(Ordering::Equal, (-min_subnorm()).total_cmp(&-min_subnorm()));
+        assert_eq!(Ordering::Equal, (-0.0_f64).total_cmp(&-0.0));
+        assert_eq!(Ordering::Equal, 0.0_f64.total_cmp(&0.0));
+        assert_eq!(Ordering::Equal, min_subnorm().total_cmp(&min_subnorm()));
+        assert_eq!(Ordering::Equal, max_subnorm().total_cmp(&max_subnorm()));
+        assert_eq!(Ordering::Equal, f64::MIN_POSITIVE.total_cmp(&f64::MIN_POSITIVE));
+        assert_eq!(Ordering::Equal, 0.5_f64.total_cmp(&0.5));
+        assert_eq!(Ordering::Equal, 1.0_f64.total_cmp(&1.0));
+        assert_eq!(Ordering::Equal, 1.5_f64.total_cmp(&1.5));
+        assert_eq!(Ordering::Equal, 2.5_f64.total_cmp(&2.5));
+        assert_eq!(Ordering::Equal, f64::MAX.total_cmp(&f64::MAX));
+        assert_eq!(Ordering::Equal, f64::INFINITY.total_cmp(&f64::INFINITY));
+        assert_eq!(Ordering::Equal, s_nan().total_cmp(&s_nan()));
+        assert_eq!(Ordering::Equal, q_nan().total_cmp(&q_nan()));
+
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY));
+        assert_eq!(Ordering::Less, (-f64::INFINITY).total_cmp(&-f64::MAX));
+        assert_eq!(Ordering::Less, (-f64::MAX).total_cmp(&-2.5));
+        assert_eq!(Ordering::Less, (-2.5_f64).total_cmp(&-1.5));
+        assert_eq!(Ordering::Less, (-1.5_f64).total_cmp(&-1.0));
+        assert_eq!(Ordering::Less, (-1.0_f64).total_cmp(&-0.5));
+        assert_eq!(Ordering::Less, (-0.5_f64).total_cmp(&-f64::MIN_POSITIVE));
+        assert_eq!(Ordering::Less, (-f64::MIN_POSITIVE).total_cmp(&-max_subnorm()));
+        assert_eq!(Ordering::Less, (-max_subnorm()).total_cmp(&-min_subnorm()));
+        assert_eq!(Ordering::Less, (-min_subnorm()).total_cmp(&-0.0));
+        assert_eq!(Ordering::Less, (-0.0_f64).total_cmp(&0.0));
+        assert_eq!(Ordering::Less, 0.0_f64.total_cmp(&min_subnorm()));
+        assert_eq!(Ordering::Less, min_subnorm().total_cmp(&max_subnorm()));
+        assert_eq!(Ordering::Less, max_subnorm().total_cmp(&f64::MIN_POSITIVE));
+        assert_eq!(Ordering::Less, f64::MIN_POSITIVE.total_cmp(&0.5));
+        assert_eq!(Ordering::Less, 0.5_f64.total_cmp(&1.0));
+        assert_eq!(Ordering::Less, 1.0_f64.total_cmp(&1.5));
+        assert_eq!(Ordering::Less, 1.5_f64.total_cmp(&2.5));
+        assert_eq!(Ordering::Less, 2.5_f64.total_cmp(&f64::MAX));
+        assert_eq!(Ordering::Less, f64::MAX.total_cmp(&f64::INFINITY));
+        assert_eq!(Ordering::Less, f64::INFINITY.total_cmp(&s_nan()));
+        assert_eq!(Ordering::Less, s_nan().total_cmp(&q_nan()));
+
+        assert_eq!(Ordering::Greater, (-s_nan()).total_cmp(&-q_nan()));
+        assert_eq!(Ordering::Greater, (-f64::INFINITY).total_cmp(&-s_nan()));
+        assert_eq!(Ordering::Greater, (-f64::MAX).total_cmp(&-f64::INFINITY));
+        assert_eq!(Ordering::Greater, (-2.5_f64).total_cmp(&-f64::MAX));
+        assert_eq!(Ordering::Greater, (-1.5_f64).total_cmp(&-2.5));
+        assert_eq!(Ordering::Greater, (-1.0_f64).total_cmp(&-1.5));
+        assert_eq!(Ordering::Greater, (-0.5_f64).total_cmp(&-1.0));
+        assert_eq!(Ordering::Greater, (-f64::MIN_POSITIVE).total_cmp(&-0.5));
+        assert_eq!(Ordering::Greater, (-max_subnorm()).total_cmp(&-f64::MIN_POSITIVE));
+        assert_eq!(Ordering::Greater, (-min_subnorm()).total_cmp(&-max_subnorm()));
+        assert_eq!(Ordering::Greater, (-0.0_f64).total_cmp(&-min_subnorm()));
+        assert_eq!(Ordering::Greater, 0.0_f64.total_cmp(&-0.0));
+        assert_eq!(Ordering::Greater, min_subnorm().total_cmp(&0.0));
+        assert_eq!(Ordering::Greater, max_subnorm().total_cmp(&min_subnorm()));
+        assert_eq!(Ordering::Greater, f64::MIN_POSITIVE.total_cmp(&max_subnorm()));
+        assert_eq!(Ordering::Greater, 0.5_f64.total_cmp(&f64::MIN_POSITIVE));
+        assert_eq!(Ordering::Greater, 1.0_f64.total_cmp(&0.5));
+        assert_eq!(Ordering::Greater, 1.5_f64.total_cmp(&1.0));
+        assert_eq!(Ordering::Greater, 2.5_f64.total_cmp(&1.5));
+        assert_eq!(Ordering::Greater, f64::MAX.total_cmp(&2.5));
+        assert_eq!(Ordering::Greater, f64::INFINITY.total_cmp(&f64::MAX));
+        assert_eq!(Ordering::Greater, s_nan().total_cmp(&f64::INFINITY));
+        assert_eq!(Ordering::Greater, q_nan().total_cmp(&s_nan()));
+
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-s_nan()));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::INFINITY));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MAX));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-2.5));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.5));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-1.0));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.5));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-f64::MIN_POSITIVE));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-max_subnorm()));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-min_subnorm()));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&-0.0));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.0));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&min_subnorm()));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&max_subnorm()));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MIN_POSITIVE));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&0.5));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.0));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&1.5));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&2.5));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::MAX));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&f64::INFINITY));
+        assert_eq!(Ordering::Less, (-q_nan()).total_cmp(&s_nan()));
+
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::INFINITY));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MAX));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-2.5));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.5));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-1.0));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.5));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-f64::MIN_POSITIVE));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-max_subnorm()));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-min_subnorm()));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&-0.0));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.0));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&min_subnorm()));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&max_subnorm()));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MIN_POSITIVE));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&0.5));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.0));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&1.5));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&2.5));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::MAX));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&f64::INFINITY));
+        assert_eq!(Ordering::Less, (-s_nan()).total_cmp(&s_nan()));
+    }
 }
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index ac07af5e278fb..468ac7bb5e873 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -308,6 +308,7 @@
 #![feature(test)]
 #![feature(thread_local)]
 #![feature(toowned_clone_into)]
+#![feature(total_cmp)]
 #![feature(trace_macros)]
 #![feature(track_caller)]
 #![feature(try_reserve)]

From b6eec22dd49b74eda4257d0fd634fa179de9c2ec Mon Sep 17 00:00:00 2001
From: Pyry Kontio <pyry.kontio@drasa.eu>
Date: Tue, 26 May 2020 02:44:55 +0900
Subject: [PATCH 04/26] Fix typo in src/libcore/num/f32.rs

Co-authored-by: bluss <bluss@users.noreply.github.com>
---
 src/libcore/num/f32.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs
index 47f489c40abb1..56c77c8aea0c0 100644
--- a/src/libcore/num/f32.rs
+++ b/src/libcore/num/f32.rs
@@ -874,7 +874,7 @@ impl f32 {
         // flip the exponent and mantissa bits in case of negative numbers.
         // We effectively convert the numbers to "two's complement" form.
         if left < 0 {
-            // i32::MAX corresponds the bit pattern of "all ones expect for the sign bit"
+            // i32::MAX corresponds the bit pattern of "all ones except for the sign bit"
             left ^= i32::MAX
         };
         if right < 0 {

From d6650e0d2b1027de341ba7f48937eab499fca3ee Mon Sep 17 00:00:00 2001
From: Pyry Kontio <pyry.kontio@drasa.eu>
Date: Tue, 26 May 2020 02:45:06 +0900
Subject: [PATCH 05/26] Fix typo in src/libcore/num/f32.rs

Co-authored-by: bluss <bluss@users.noreply.github.com>
---
 src/libcore/num/f32.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs
index 56c77c8aea0c0..813e3077c3b8a 100644
--- a/src/libcore/num/f32.rs
+++ b/src/libcore/num/f32.rs
@@ -858,7 +858,7 @@ impl f32 {
         let mut left = self.to_bits() as i32;
         let mut right = other.to_bits() as i32;
 
-        // In case of negatives, flip all the bits expect the sign
+        // In case of negatives, flip all the bits except the sign
         // to achieve a similar layout as two's complement integers
         //
         // Why does this work? IEEE 754 floats consist of three fields:

From bd68de89b55d7f7d7adc41fdcd0a5bac1f805692 Mon Sep 17 00:00:00 2001
From: Pyry Kontio <pyry.kontio@drasa.eu>
Date: Tue, 26 May 2020 02:48:56 +0900
Subject: [PATCH 06/26] remove unneeded and unidiomatic must_use

---
 src/libcore/num/f32.rs | 1 -
 src/libcore/num/f64.rs | 1 -
 2 files changed, 2 deletions(-)

diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs
index 813e3077c3b8a..36b70587385cb 100644
--- a/src/libcore/num/f32.rs
+++ b/src/libcore/num/f32.rs
@@ -851,7 +851,6 @@ impl f32 {
     /// #     .zip([-5.0, 0.1, 10.0, 99.0, f32::INFINITY, f32::NAN].iter())
     /// #     .all(|(a, b)| a.to_bits() == b.to_bits()))
     /// ```
-    #[must_use = "method returns a new number and does not mutate the original value"]
     #[unstable(feature = "total_cmp", issue = "none")]
     #[inline]
     pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs
index 9c02180838014..61711df3c65ef 100644
--- a/src/libcore/num/f64.rs
+++ b/src/libcore/num/f64.rs
@@ -865,7 +865,6 @@ impl f64 {
     /// #     .zip([-5.0, 0.1, 10.0, 99.0, f64::INFINITY, f64::NAN].iter())
     /// #     .all(|(a, b)| a.to_bits() == b.to_bits()))
     /// ```
-    #[must_use = "method returns a new number and does not mutate the original value"]
     #[unstable(feature = "total_cmp", issue = "none")]
     #[inline]
     pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {

From 6973fd716b51b01debf39edd8e43f0059be3d053 Mon Sep 17 00:00:00 2001
From: Pyry Kontio <pyry.kontio@drasa.eu>
Date: Tue, 26 May 2020 04:50:53 +0900
Subject: [PATCH 07/26] Add bit twiddling

---
 src/libcore/num/f32.rs | 18 ++++++++++--------
 src/libcore/num/f64.rs | 16 +++++++++-------
 2 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs
index 36b70587385cb..538cca712ca95 100644
--- a/src/libcore/num/f32.rs
+++ b/src/libcore/num/f32.rs
@@ -857,7 +857,7 @@ impl f32 {
         let mut left = self.to_bits() as i32;
         let mut right = other.to_bits() as i32;
 
-        // In case of negatives, flip all the bits except the sign
+        // In case of negatives, flip all the bits expect the sign
         // to achieve a similar layout as two's complement integers
         //
         // Why does this work? IEEE 754 floats consist of three fields:
@@ -872,13 +872,15 @@ impl f32 {
         // To easily compare the floats as signed integers, we need to
         // flip the exponent and mantissa bits in case of negative numbers.
         // We effectively convert the numbers to "two's complement" form.
-        if left < 0 {
-            // i32::MAX corresponds the bit pattern of "all ones except for the sign bit"
-            left ^= i32::MAX
-        };
-        if right < 0 {
-            right ^= i32::MAX
-        };
+        //
+        // To do the flipping, we construct a mask and XOR against it.
+        // We branchlessly calculate an "all-ones expect for the sign bit"
+        // mask from negative-signed values: right shifting sign-extends
+        // the integer, so we "fill" the mask with sign bits, and then
+        // convert to unsigned to push one more zero bit.
+        // On positive values, the mask is all zeros, so it's a no-op.
+        left ^= (((left >> 31) as u32) >> 1) as i32;
+        right ^= (((right >> 31) as u32) >> 1) as i32;
 
         left.cmp(&right)
     }
diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs
index 61711df3c65ef..b3ebceb77c2e5 100644
--- a/src/libcore/num/f64.rs
+++ b/src/libcore/num/f64.rs
@@ -886,13 +886,15 @@ impl f64 {
         // To easily compare the floats as signed integers, we need to
         // flip the exponent and mantissa bits in case of negative numbers.
         // We effectively convert the numbers to "two's complement" form.
-        if left < 0 {
-            // i64::MAX corresponds the bit pattern of "all ones expect for the sign bit"
-            left ^= i64::MAX
-        };
-        if right < 0 {
-            right ^= i64::MAX
-        };
+        //
+        // To do the flipping, we construct a mask and XOR against it.
+        // We branchlessly calculate an "all-ones expect for the sign bit"
+        // mask from negative-signed values: right shifting sign-extends
+        // the integer, so we "fill" the mask with sign bits, and then
+        // convert to unsigned to push one more zero bit.
+        // On positive values, the mask is all zeros, so it's a no-op.
+        left ^= (((left >> 63) as u64) >> 1) as i64;
+        right ^= (((right >> 63) as u64) >> 1) as i64;
 
         left.cmp(&right)
     }

From 8bc31ff9055c50e9d882dd56909dc0b40c3a6453 Mon Sep 17 00:00:00 2001
From: Pyry Kontio <pyry.kontio@drasa.eu>
Date: Tue, 26 May 2020 04:54:50 +0900
Subject: [PATCH 08/26] Fix the same typos again orz

---
 src/libcore/num/f32.rs | 4 ++--
 src/libcore/num/f64.rs | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs
index 538cca712ca95..c54bfb27dfe5c 100644
--- a/src/libcore/num/f32.rs
+++ b/src/libcore/num/f32.rs
@@ -857,7 +857,7 @@ impl f32 {
         let mut left = self.to_bits() as i32;
         let mut right = other.to_bits() as i32;
 
-        // In case of negatives, flip all the bits expect the sign
+        // In case of negatives, flip all the bits except the sign
         // to achieve a similar layout as two's complement integers
         //
         // Why does this work? IEEE 754 floats consist of three fields:
@@ -874,7 +874,7 @@ impl f32 {
         // We effectively convert the numbers to "two's complement" form.
         //
         // To do the flipping, we construct a mask and XOR against it.
-        // We branchlessly calculate an "all-ones expect for the sign bit"
+        // We branchlessly calculate an "all-ones except for the sign bit"
         // mask from negative-signed values: right shifting sign-extends
         // the integer, so we "fill" the mask with sign bits, and then
         // convert to unsigned to push one more zero bit.
diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs
index b3ebceb77c2e5..18d5d720a0544 100644
--- a/src/libcore/num/f64.rs
+++ b/src/libcore/num/f64.rs
@@ -871,7 +871,7 @@ impl f64 {
         let mut left = self.to_bits() as i64;
         let mut right = other.to_bits() as i64;
 
-        // In case of negatives, flip all the bits expect the sign
+        // In case of negatives, flip all the bits except the sign
         // to achieve a similar layout as two's complement integers
         //
         // Why does this work? IEEE 754 floats consist of three fields:
@@ -888,7 +888,7 @@ impl f64 {
         // We effectively convert the numbers to "two's complement" form.
         //
         // To do the flipping, we construct a mask and XOR against it.
-        // We branchlessly calculate an "all-ones expect for the sign bit"
+        // We branchlessly calculate an "all-ones except for the sign bit"
         // mask from negative-signed values: right shifting sign-extends
         // the integer, so we "fill" the mask with sign bits, and then
         // convert to unsigned to push one more zero bit.

From c3dc8c455cee65db8928913da029725046c14a1b Mon Sep 17 00:00:00 2001
From: Dhruv Jauhar <dhruv.jauhar@databricks.com>
Date: Mon, 25 May 2020 12:57:47 -0700
Subject: [PATCH 09/26] Rename upvar_list to closure_captures

As part of supporting RFC 2229, we will be capturing all the places that
are mentioned in a closure. Currently the upvar_list field gives access
to a FxIndexMap<HirId, Upvar> map. Eventually this will change, with the
upvar_list having a more general structure that expresses captured
paths, not just the mentioned upvars. We will make those changes in
subsequent PRs.

This commit modifies the name of the upvar_list map to closure_captures
in TypeckTables.

Co-authored-by: Dhruv Jauhar <dhruvjhr@gmail.com>
Co-authored-by: Aman Arora <me@aman-arora.com>
---
 src/librustc_middle/ty/context.rs      | 8 ++++----
 src/librustc_mir/borrow_check/mod.rs   | 2 +-
 src/librustc_mir/interpret/validity.rs | 2 +-
 src/librustc_mir_build/build/mod.rs    | 4 ++--
 src/librustc_mir_build/hair/cx/expr.rs | 2 +-
 src/librustc_typeck/check/upvar.rs     | 8 ++++----
 src/librustc_typeck/check/writeback.rs | 4 ++--
 7 files changed, 15 insertions(+), 15 deletions(-)

diff --git a/src/librustc_middle/ty/context.rs b/src/librustc_middle/ty/context.rs
index 5b53ab1778e3f..f2d1e6f42f615 100644
--- a/src/librustc_middle/ty/context.rs
+++ b/src/librustc_middle/ty/context.rs
@@ -419,7 +419,7 @@ pub struct TypeckTables<'tcx> {
     /// The upvarID contains the HIR node ID and it also contains the full path
     /// leading to the member of the struct or tuple that is used instead of the
     /// entire variable.
-    pub upvar_list: ty::UpvarListMap,
+    pub closure_captures: ty::UpvarListMap,
 
     /// Stores the type, expression, span and optional scope span of all types
     /// that are live across the yield of this generator (if a generator).
@@ -447,7 +447,7 @@ impl<'tcx> TypeckTables<'tcx> {
             used_trait_imports: Lrc::new(Default::default()),
             tainted_by_errors: None,
             concrete_opaque_types: Default::default(),
-            upvar_list: Default::default(),
+            closure_captures: Default::default(),
             generator_interior_types: Default::default(),
         }
     }
@@ -688,7 +688,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckTables<'tcx> {
             ref used_trait_imports,
             tainted_by_errors,
             ref concrete_opaque_types,
-            ref upvar_list,
+            ref closure_captures,
             ref generator_interior_types,
         } = *self;
 
@@ -721,7 +721,7 @@ impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TypeckTables<'tcx> {
             used_trait_imports.hash_stable(hcx, hasher);
             tainted_by_errors.hash_stable(hcx, hasher);
             concrete_opaque_types.hash_stable(hcx, hasher);
-            upvar_list.hash_stable(hcx, hasher);
+            closure_captures.hash_stable(hcx, hasher);
             generator_interior_types.hash_stable(hcx, hasher);
         })
     }
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index a0c1d96bb4743..65e62dbd9dd49 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -142,7 +142,7 @@ fn do_mir_borrowck<'a, 'tcx>(
         infcx.set_tainted_by_errors();
     }
     let upvars: Vec<_> = tables
-        .upvar_list
+        .closure_captures
         .get(&def_id.to_def_id())
         .into_iter()
         .flat_map(|v| v.values())
diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs
index c83555d65faf0..e962dfb2b3e86 100644
--- a/src/librustc_mir/interpret/validity.rs
+++ b/src/librustc_mir/interpret/validity.rs
@@ -227,7 +227,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
                 let mut name = None;
                 if let Some(def_id) = def_id.as_local() {
                     let tables = self.ecx.tcx.typeck_tables_of(def_id);
-                    if let Some(upvars) = tables.upvar_list.get(&def_id.to_def_id()) {
+                    if let Some(upvars) = tables.closure_captures.get(&def_id.to_def_id()) {
                         // Sometimes the index is beyond the number of upvars (seen
                         // for a generator).
                         if let Some((&var_hir_id, _)) = upvars.get_index(field) {
diff --git a/src/librustc_mir_build/build/mod.rs b/src/librustc_mir_build/build/mod.rs
index b30b57ea9217a..3d821aa55a1f8 100644
--- a/src/librustc_mir_build/build/mod.rs
+++ b/src/librustc_mir_build/build/mod.rs
@@ -790,11 +790,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let hir_tables = self.hir.tables();
 
         // In analyze_closure() in upvar.rs we gathered a list of upvars used by a
-        // closure and we stored in a map called upvar_list in TypeckTables indexed
+        // indexed closure and we stored in a map called closure_captures in TypeckTables
         // with the closure's DefId. Here, we run through that vec of UpvarIds for
         // the given closure and use the necessary information to create upvar
         // debuginfo and to fill `self.upvar_mutbls`.
-        if let Some(upvars) = hir_tables.upvar_list.get(&fn_def_id) {
+        if let Some(upvars) = hir_tables.closure_captures.get(&fn_def_id) {
             let closure_env_arg = Local::new(1);
             let mut closure_env_projs = vec![];
             let mut closure_ty = self.local_decls[closure_env_arg].ty;
diff --git a/src/librustc_mir_build/hair/cx/expr.rs b/src/librustc_mir_build/hair/cx/expr.rs
index 114bf5710402f..fd3c48b38badb 100644
--- a/src/librustc_mir_build/hair/cx/expr.rs
+++ b/src/librustc_mir_build/hair/cx/expr.rs
@@ -884,7 +884,7 @@ fn convert_var<'tcx>(
 ) -> ExprKind<'tcx> {
     let upvar_index = cx
         .tables()
-        .upvar_list
+        .closure_captures
         .get(&cx.body_owner)
         .and_then(|upvars| upvars.get_full(&var_hir_id).map(|(i, _, _)| i));
 
diff --git a/src/librustc_typeck/check/upvar.rs b/src/librustc_typeck/check/upvar.rs
index 8707e4fe84a22..19a23e5a59478 100644
--- a/src/librustc_typeck/check/upvar.rs
+++ b/src/librustc_typeck/check/upvar.rs
@@ -112,7 +112,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
 
         if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
-            let mut upvar_list: FxIndexMap<hir::HirId, ty::UpvarId> =
+            let mut closure_captures: FxIndexMap<hir::HirId, ty::UpvarId> =
                 FxIndexMap::with_capacity_and_hasher(upvars.len(), Default::default());
             for (&var_hir_id, _) in upvars.iter() {
                 let upvar_id = ty::UpvarId {
@@ -122,7 +122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 debug!("seed upvar_id {:?}", upvar_id);
                 // Adding the upvar Id to the list of Upvars, which will be added
                 // to the map for the closure at the end of the for loop.
-                upvar_list.insert(var_hir_id, upvar_id);
+                closure_captures.insert(var_hir_id, upvar_id);
 
                 let capture_kind = match capture_clause {
                     hir::CaptureBy::Value => ty::UpvarCapture::ByValue,
@@ -140,8 +140,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // Add the vector of upvars to the map keyed with the closure id.
             // This gives us an easier access to them without having to call
             // tcx.upvars again..
-            if !upvar_list.is_empty() {
-                self.tables.borrow_mut().upvar_list.insert(closure_def_id, upvar_list);
+            if !closure_captures.is_empty() {
+                self.tables.borrow_mut().closure_captures.insert(closure_def_id, closure_captures);
             }
         }
 
diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs
index 154ca391aa5fd..3473dc7a58d04 100644
--- a/src/librustc_typeck/check/writeback.rs
+++ b/src/librustc_typeck/check/writeback.rs
@@ -74,8 +74,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports);
         wbcx.tables.used_trait_imports = used_trait_imports;
 
-        wbcx.tables.upvar_list =
-            mem::replace(&mut self.tables.borrow_mut().upvar_list, Default::default());
+        wbcx.tables.closure_captures =
+            mem::replace(&mut self.tables.borrow_mut().closure_captures, Default::default());
 
         if self.is_tainted_by_errors() {
             // FIXME(eddyb) keep track of `ErrorReported` from where the error was emitted.

From 66da7350963edf3d40461e6f5bafde95974908fc Mon Sep 17 00:00:00 2001
From: Pyry Kontio <pyry.kontio@drasa.eu>
Date: Tue, 26 May 2020 11:59:23 +0900
Subject: [PATCH 10/26] Add tracing issue for total_cmp

---
 src/libcore/num/f32.rs | 2 +-
 src/libcore/num/f64.rs | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs
index c54bfb27dfe5c..6313de31ce4d5 100644
--- a/src/libcore/num/f32.rs
+++ b/src/libcore/num/f32.rs
@@ -851,7 +851,7 @@ impl f32 {
     /// #     .zip([-5.0, 0.1, 10.0, 99.0, f32::INFINITY, f32::NAN].iter())
     /// #     .all(|(a, b)| a.to_bits() == b.to_bits()))
     /// ```
-    #[unstable(feature = "total_cmp", issue = "none")]
+    #[unstable(feature = "total_cmp", issue = "72599")]
     #[inline]
     pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
         let mut left = self.to_bits() as i32;
diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs
index 18d5d720a0544..d42e5392c5863 100644
--- a/src/libcore/num/f64.rs
+++ b/src/libcore/num/f64.rs
@@ -865,7 +865,7 @@ impl f64 {
     /// #     .zip([-5.0, 0.1, 10.0, 99.0, f64::INFINITY, f64::NAN].iter())
     /// #     .all(|(a, b)| a.to_bits() == b.to_bits()))
     /// ```
-    #[unstable(feature = "total_cmp", issue = "none")]
+    #[unstable(feature = "total_cmp", issue = "72599")]
     #[inline]
     pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering {
         let mut left = self.to_bits() as i64;

From ca722b935835f7ce3acfbb840141cc17941fc63b Mon Sep 17 00:00:00 2001
From: Yuki Okushi <huyuumi.dev@gmail.com>
Date: Mon, 25 May 2020 22:29:06 +0900
Subject: [PATCH 11/26] Add test for #56445

---
 src/test/ui/impl-trait/issue-56445.rs | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)
 create mode 100644 src/test/ui/impl-trait/issue-56445.rs

diff --git a/src/test/ui/impl-trait/issue-56445.rs b/src/test/ui/impl-trait/issue-56445.rs
new file mode 100644
index 0000000000000..a34d7bae3a6ca
--- /dev/null
+++ b/src/test/ui/impl-trait/issue-56445.rs
@@ -0,0 +1,26 @@
+// Regression test for https://github.com/rust-lang/rust/issues/56445#issuecomment-629426939
+// check-pass
+
+#![crate_type = "lib"]
+
+use std::marker::PhantomData;
+
+pub struct S<'a>
+{
+    pub m1: PhantomData<&'a u8>,
+    pub m2: [u8; S::size()],
+}
+
+impl<'a> S<'a>
+{
+    pub const fn size() -> usize { 1 }
+
+    pub fn new() -> Self
+    {
+        Self
+        {
+            m1: PhantomData,
+            m2: [0; Self::size()],
+        }
+    }
+}

From 125f0abb42159d3db9d86208e03006d068585bbf Mon Sep 17 00:00:00 2001
From: Yuki Okushi <huyuumi.dev@gmail.com>
Date: Mon, 25 May 2020 22:29:25 +0900
Subject: [PATCH 12/26] Add test for #68532

---
 src/test/ui/impl-trait/issue-68532.rs | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 src/test/ui/impl-trait/issue-68532.rs

diff --git a/src/test/ui/impl-trait/issue-68532.rs b/src/test/ui/impl-trait/issue-68532.rs
new file mode 100644
index 0000000000000..01a7af0aee40e
--- /dev/null
+++ b/src/test/ui/impl-trait/issue-68532.rs
@@ -0,0 +1,13 @@
+// check-pass
+
+pub struct A<'a>(&'a ());
+
+impl<'a> A<'a> {
+    const N: usize = 68;
+
+    pub fn foo(&self) {
+        let _b = [0; Self::N];
+    }
+}
+
+fn main() {}

From 4b87f97ca8862ad97272ecafcd4c27334ecb81aa Mon Sep 17 00:00:00 2001
From: Yuki Okushi <huyuumi.dev@gmail.com>
Date: Mon, 25 May 2020 22:29:34 +0900
Subject: [PATCH 13/26] Add test for #70121

---
 .../ui/type-alias-impl-trait/issue-70121.rs   | 23 +++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 src/test/ui/type-alias-impl-trait/issue-70121.rs

diff --git a/src/test/ui/type-alias-impl-trait/issue-70121.rs b/src/test/ui/type-alias-impl-trait/issue-70121.rs
new file mode 100644
index 0000000000000..dff0d89d465dd
--- /dev/null
+++ b/src/test/ui/type-alias-impl-trait/issue-70121.rs
@@ -0,0 +1,23 @@
+// check-pass
+
+#![feature(type_alias_impl_trait)]
+
+pub type Successors<'a> = impl Iterator<Item = &'a ()>;
+
+pub fn f<'a>() -> Successors<'a> {
+    None.into_iter()
+}
+
+pub trait Tr {
+    type Item;
+}
+
+impl<'a> Tr for &'a () {
+    type Item = Successors<'a>;
+}
+
+pub fn kazusa<'a>() -> <&'a () as Tr>::Item {
+    None.into_iter()
+}
+
+fn main() {}

From 6315d0c99eead181e3725623c6a9d01950620640 Mon Sep 17 00:00:00 2001
From: Yuki Okushi <huyuumi.dev@gmail.com>
Date: Wed, 27 May 2020 00:48:29 +0900
Subject: [PATCH 14/26] Add test for #71042

---
 ...ssue-71042-opaquely-typed-constant-used-in-pattern.rs | 9 +++++++++
 ...-71042-opaquely-typed-constant-used-in-pattern.stderr | 8 ++++++++
 2 files changed, 17 insertions(+)
 create mode 100644 src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.rs
 create mode 100644 src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.stderr

diff --git a/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.rs b/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.rs
new file mode 100644
index 0000000000000..c5e4a72fb9ff1
--- /dev/null
+++ b/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.rs
@@ -0,0 +1,9 @@
+#![feature(impl_trait_in_bindings)]
+#![allow(incomplete_features)]
+
+fn main() {
+    const C: impl Copy = 0;
+    match C {
+        C | _ => {} //~ ERROR: opaque types cannot be used in patterns
+    }
+}
diff --git a/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.stderr b/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.stderr
new file mode 100644
index 0000000000000..7695223f2cf98
--- /dev/null
+++ b/src/test/ui/pattern/issue-71042-opaquely-typed-constant-used-in-pattern.stderr
@@ -0,0 +1,8 @@
+error: opaque types cannot be used in patterns
+  --> $DIR/issue-71042-opaquely-typed-constant-used-in-pattern.rs:7:9
+   |
+LL |         C | _ => {}
+   |         ^
+
+error: aborting due to previous error
+

From 6ddbef170bf0b79b868088580e28c862b691bff3 Mon Sep 17 00:00:00 2001
From: Stanislav Tkach <stanislav.tkach@gmail.com>
Date: Tue, 26 May 2020 23:06:46 +0300
Subject: [PATCH 15/26] Simplify suggestion

---
 src/librustc_typeck/check/callee.rs                   | 11 ++++-------
 src/test/ui/error-codes/E0040.stderr                  |  8 ++++----
 src/test/ui/explicit/explicit-call-to-dtor.stderr     |  8 ++++----
 .../explicit/explicit-call-to-supertrait-dtor.stderr  |  8 ++++----
 4 files changed, 16 insertions(+), 19 deletions(-)

diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs
index a32174a83337d..f4e46a0493151 100644
--- a/src/librustc_typeck/check/callee.rs
+++ b/src/librustc_typeck/check/callee.rs
@@ -38,14 +38,11 @@ pub fn check_legal_trait_for_method_call(
         let suggestion =
             if snippet.is_empty() { "drop".to_string() } else { format!("drop({})", snippet) };
 
-        let suggestion_span =
-            receiver.and_then(|s| tcx.sess.source_map().merge_spans(s, span)).unwrap_or(span);
-
         err.span_suggestion(
-            suggestion_span,
-            "consider using `drop` function",
-            suggestion,
-            Applicability::MaybeIncorrect,
+            span,
+            &format!("consider using `drop` function: `{}`", suggestion),
+            String::new(),
+            Applicability::Unspecified,
         );
 
         err.emit();
diff --git a/src/test/ui/error-codes/E0040.stderr b/src/test/ui/error-codes/E0040.stderr
index 3b864d4ea4b2c..69cf28b29704f 100644
--- a/src/test/ui/error-codes/E0040.stderr
+++ b/src/test/ui/error-codes/E0040.stderr
@@ -2,10 +2,10 @@ error[E0040]: explicit use of destructor method
   --> $DIR/E0040.rs:13:7
    |
 LL |     x.drop();
-   |     --^^^^
-   |     | |
-   |     | explicit destructor calls not allowed
-   |     help: consider using `drop` function: `drop(x)`
+   |       ^^^^
+   |       |
+   |       explicit destructor calls not allowed
+   |       help: consider using `drop` function: `drop(x)`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/explicit/explicit-call-to-dtor.stderr b/src/test/ui/explicit/explicit-call-to-dtor.stderr
index 33ce235b30fda..5ebe4ee4b90f8 100644
--- a/src/test/ui/explicit/explicit-call-to-dtor.stderr
+++ b/src/test/ui/explicit/explicit-call-to-dtor.stderr
@@ -2,10 +2,10 @@ error[E0040]: explicit use of destructor method
   --> $DIR/explicit-call-to-dtor.rs:13:7
    |
 LL |     x.drop();
-   |     --^^^^
-   |     | |
-   |     | explicit destructor calls not allowed
-   |     help: consider using `drop` function: `drop(x)`
+   |       ^^^^
+   |       |
+   |       explicit destructor calls not allowed
+   |       help: consider using `drop` function: `drop(x)`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr b/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr
index 2e7bfac71cd32..cd3fb3119a5cf 100644
--- a/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr
+++ b/src/test/ui/explicit/explicit-call-to-supertrait-dtor.stderr
@@ -2,10 +2,10 @@ error[E0040]: explicit use of destructor method
   --> $DIR/explicit-call-to-supertrait-dtor.rs:17:14
    |
 LL |         self.drop();
-   |         -----^^^^
-   |         |    |
-   |         |    explicit destructor calls not allowed
-   |         help: consider using `drop` function: `drop(self)`
+   |              ^^^^
+   |              |
+   |              explicit destructor calls not allowed
+   |              help: consider using `drop` function: `drop(self)`
 
 error: aborting due to previous error
 

From 822ad87325d6436a6aea5ae61ca34d9ad45dd839 Mon Sep 17 00:00:00 2001
From: Joshua Nelson <jyn514@gmail.com>
Date: Sun, 17 May 2020 20:15:15 -0400
Subject: [PATCH 16/26] Add Peekable::next_if

Prior art:

`rust_analyzer` uses
[`Parser::eat`](https://github.com/rust-analyzer/rust-analyzer/blob/50f4ae798b7c54d417ee88455b87fd0477473150/crates/ra_parser/src/parser.rs#L94), which is `next_if` specialized to
`|y| next_if(|x| x == y)`.

Basically every other parser I've run into in Rust has an equivalent of
Parser::eat; see for example

- [cranelift](https://github.com/bytecodealliance/wasmtime/blob/94190d57244b26baf36629c88104b0ba516510cf/cranelift/reader/src/parser.rs#L498)
- [rcc](https://github.com/jyn514/rcc/blob/a8159c3904a0c950fbba817bf9109023fad69033/src/parse/mod.rs#L231)
- [crunch](https://github.com/Kixiron/crunch-lang/blob/8521874fab8a7d62bfa7dea8bd1da94b63e31be8/crates/crunch-parser/src/parser/mod.rs#L213-L241)
---
 src/libcore/iter/adapters/mod.rs | 63 ++++++++++++++++++++++++++++++++
 src/libcore/tests/iter.rs        | 24 ++++++++++++
 src/libcore/tests/lib.rs         |  1 +
 3 files changed, 88 insertions(+)

diff --git a/src/libcore/iter/adapters/mod.rs b/src/libcore/iter/adapters/mod.rs
index e9fc1b612dd39..3c6842b07b3bf 100644
--- a/src/libcore/iter/adapters/mod.rs
+++ b/src/libcore/iter/adapters/mod.rs
@@ -1574,6 +1574,69 @@ impl<I: Iterator> Peekable<I> {
         let iter = &mut self.iter;
         self.peeked.get_or_insert_with(|| iter.next()).as_ref()
     }
+
+    /// Consume the next value of this iterator if a condition is true.
+    ///
+    /// If `func` returns `true` for the next value of this iterator, consume and return it.
+    /// Otherwise, return `None`.
+    ///
+    /// # Examples
+    /// Consume a number if it's equal to 0.
+    /// ```
+    /// #![feature(peekable_next_if)]
+    /// let mut iter = (0..5).peekable();
+    /// // The first item of the iterator is 0; consume it.
+    /// assert_eq!(iter.next_if(|&x| x == 0), Some(0));
+    /// // The next item returned is now 1, so `consume` will return `false`.
+    /// assert_eq!(iter.next_if(|&x| x == 0), None);
+    /// // `next_if` saves the value of the next item if it was not equal to `expected`.
+    /// assert_eq!(iter.next(), Some(1));
+    /// ```
+    ///
+    /// Consume any number less than 10.
+    /// ```
+    /// #![feature(peekable_next_if)]
+    /// let mut iter = (1..20).peekable();
+    /// // Consume all numbers less than 10
+    /// while iter.next_if(|&x| x < 10).is_some() {}
+    /// // The next value returned will be 10
+    /// assert_eq!(iter.next(), Some(10));
+    /// ```
+    #[unstable(feature = "peekable_next_if", issue = "72480")]
+    pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option<I::Item> {
+        match self.next() {
+            Some(matched) if func(&matched) => Some(matched),
+            other => {
+                // Since we called `self.next()`, we consumed `self.peeked`.
+                assert!(self.peeked.is_none());
+                self.peeked = Some(other);
+                None
+            }
+        }
+    }
+
+    /// Consume the next item if it is equal to `expected`.
+    ///
+    /// # Example
+    /// Consume a number if it's equal to 0.
+    /// ```
+    /// #![feature(peekable_next_if)]
+    /// let mut iter = (0..5).peekable();
+    /// // The first item of the iterator is 0; consume it.
+    /// assert_eq!(iter.next_if_eq(&0), Some(0));
+    /// // The next item returned is now 1, so `consume` will return `false`.
+    /// assert_eq!(iter.next_if_eq(&0), None);
+    /// // `next_if_eq` saves the value of the next item if it was not equal to `expected`.
+    /// assert_eq!(iter.next(), Some(1));
+    /// ```
+    #[unstable(feature = "peekable_next_if", issue = "72480")]
+    pub fn next_if_eq<R>(&mut self, expected: &R) -> Option<I::Item>
+    where
+        R: ?Sized,
+        I::Item: PartialEq<R>,
+    {
+        self.next_if(|next| next == expected)
+    }
 }
 
 /// An iterator that rejects elements while `predicate` returns `true`.
diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs
index 52cf068f0a567..0265235a65a35 100644
--- a/src/libcore/tests/iter.rs
+++ b/src/libcore/tests/iter.rs
@@ -813,6 +813,30 @@ fn test_iterator_peekable_rfold() {
     assert_eq!(i, xs.len());
 }
 
+#[test]
+fn test_iterator_peekable_next_if_eq() {
+    // first, try on references
+    let xs = vec!["Heart", "of", "Gold"];
+    let mut it = xs.into_iter().peekable();
+    // try before `peek()`
+    assert_eq!(it.next_if_eq(&"trillian"), None);
+    assert_eq!(it.next_if_eq(&"Heart"), Some("Heart"));
+    // try after peek()
+    assert_eq!(it.peek(), Some(&"of"));
+    assert_eq!(it.next_if_eq(&"of"), Some("of"));
+    assert_eq!(it.next_if_eq(&"zaphod"), None);
+    // make sure `next()` still behaves
+    assert_eq!(it.next(), Some("Gold"));
+
+    // make sure comparison works for owned values
+    let xs = vec![String::from("Ludicrous"), "speed".into()];
+    let mut it = xs.into_iter().peekable();
+    // make sure basic functionality works
+    assert_eq!(it.next_if_eq("Ludicrous"), Some("Ludicrous".into()));
+    assert_eq!(it.next_if_eq("speed"), Some("speed".into()));
+    assert_eq!(it.next_if_eq(""), None);
+}
+
 /// This is an iterator that follows the Iterator contract,
 /// but it is not fused. After having returned None once, it will start
 /// producing elements if .next() is called again.
diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs
index d636542d699f9..36dba6965fa24 100644
--- a/src/libcore/tests/lib.rs
+++ b/src/libcore/tests/lib.rs
@@ -44,6 +44,7 @@
 #![feature(leading_trailing_ones)]
 #![feature(const_forget)]
 #![feature(option_unwrap_none)]
+#![feature(peekable_next_if)]
 
 extern crate test;
 

From 42a4f5ac50821d0a82c9593f049a2c3f21e922e9 Mon Sep 17 00:00:00 2001
From: Ivan Tham <pickfire@riseup.net>
Date: Fri, 29 May 2020 00:40:41 +0800
Subject: [PATCH 17/26] Fix grammar in liballoc raw_vec

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

diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs
index 2bd4733db420b..56e284a12fa83 100644
--- a/src/liballoc/raw_vec.rs
+++ b/src/liballoc/raw_vec.rs
@@ -315,7 +315,7 @@ impl<T, A: AllocRef> RawVec<T, A> {
     /// `used_capacity + needed_extra_capacity` elements. If it doesn't already,
     /// will reallocate the minimum possible amount of memory necessary.
     /// Generally this will be exactly the amount of memory necessary,
-    /// but in principle the allocator is free to give back more than
+    /// but in principle the allocator is free to give back more than what
     /// we asked for.
     ///
     /// If `used_capacity` exceeds `self.capacity()`, this may fail to actually allocate

From 813ce7a688f716e53f3dd22a89ec059af3b67c13 Mon Sep 17 00:00:00 2001
From: Nathan West <Lucretiel@gmail.com>
Date: Wed, 20 May 2020 15:21:24 -0400
Subject: [PATCH 18/26] `SocketAddr(V4|V6)?`::Display now correctly pads its
 content

IpAddr and friends pad when displaying; SocketAddr now does this as well
---
 src/libstd/net/addr.rs | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs
index 08536de4d55c3..8870c405a2674 100644
--- a/src/libstd/net/addr.rs
+++ b/src/libstd/net/addr.rs
@@ -2,7 +2,7 @@ use crate::cmp::Ordering;
 use crate::convert::TryInto;
 use crate::fmt;
 use crate::hash;
-use crate::io;
+use crate::io::{self, Write};
 use crate::iter;
 use crate::mem;
 use crate::net::{htons, ntohs, IpAddr, Ipv4Addr, Ipv6Addr};
@@ -600,7 +600,17 @@ impl fmt::Display for SocketAddr {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl fmt::Display for SocketAddrV4 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}:{}", self.ip(), self.port())
+        const IPV4_SOCKET_BUF_LEN: usize = 21;
+        let mut buf = [0; IPV4_SOCKET_BUF_LEN];
+        let mut buf_slice = &mut buf[..];
+
+        // Unwrap is fine because writing to a buffer is infallible
+        write!(buf_slice, "{}:{}", self.ip(), self.port()).unwrap();
+        let len = IPV4_SOCKET_BUF_LEN - buf_slice.len();
+
+        // This unsafe is OK because we know what is being written to the buffer
+        let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
+        f.pad(buf)
     }
 }
 
@@ -614,7 +624,21 @@ impl fmt::Debug for SocketAddrV4 {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl fmt::Display for SocketAddrV6 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "[{}]:{}", self.ip(), self.port())
+        const IPV6_SOCKET_BUF_LEN: usize = (4 * 8)  // The address
+            + 7  // The colon separators
+            + 2  // The brackets
+            + 1 + 5; // The port
+
+        let mut buf = [0; IPV6_SOCKET_BUF_LEN];
+        let mut buf_slice = &mut buf[..];
+
+        // Unwrap is fine because writing to a buffer is infallible
+        write!(buf_slice, "[{}]:{}", self.ip(), self.port()).unwrap();
+        let len = IPV6_SOCKET_BUF_LEN - buf_slice.len();
+
+        // This unsafe is OK because we know what is being written to the buffer
+        let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
+        f.pad(buf)
     }
 }
 

From defbd845a33cf3c61c3af77aae474964f92e34bb Mon Sep 17 00:00:00 2001
From: Nathan West <Lucretiel@gmail.com>
Date: Wed, 20 May 2020 16:29:36 -0400
Subject: [PATCH 19/26] Added fast-path, tests

---
 src/libstd/net/addr.rs | 74 ++++++++++++++++++++++++++++++------------
 1 file changed, 54 insertions(+), 20 deletions(-)

diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs
index 8870c405a2674..2febe157a506d 100644
--- a/src/libstd/net/addr.rs
+++ b/src/libstd/net/addr.rs
@@ -600,17 +600,23 @@ impl fmt::Display for SocketAddr {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl fmt::Display for SocketAddrV4 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        const IPV4_SOCKET_BUF_LEN: usize = 21;
-        let mut buf = [0; IPV4_SOCKET_BUF_LEN];
-        let mut buf_slice = &mut buf[..];
-
-        // Unwrap is fine because writing to a buffer is infallible
-        write!(buf_slice, "{}:{}", self.ip(), self.port()).unwrap();
-        let len = IPV4_SOCKET_BUF_LEN - buf_slice.len();
-
-        // This unsafe is OK because we know what is being written to the buffer
-        let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
-        f.pad(buf)
+        // Fast path: if there's no alignment stuff, write to the output buffer
+        // directly
+        if f.precision().is_none() && f.width().is_none() {
+            write!(f, "{}:{}", self.ip(), self.port())
+        } else {
+            const IPV4_SOCKET_BUF_LEN: usize = 21;
+            let mut buf = [0; IPV4_SOCKET_BUF_LEN];
+            let mut buf_slice = &mut buf[..];
+
+            // Unwrap is fine because writing to a buffer is infallible
+            write!(buf_slice, "{}:{}", self.ip(), self.port()).unwrap();
+            let len = IPV4_SOCKET_BUF_LEN - buf_slice.len();
+
+            // This unsafe is OK because we know what is being written to the buffer
+            let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
+            f.pad(buf)
+        }
     }
 }
 
@@ -624,21 +630,27 @@ impl fmt::Debug for SocketAddrV4 {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl fmt::Display for SocketAddrV6 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        const IPV6_SOCKET_BUF_LEN: usize = (4 * 8)  // The address
+        // Fast path: if there's no alignment stuff, write to the output
+        // buffer directly
+        if f.precision().is_none() && f.width().is_none() {
+            write!(f, "[{}]:{}", self.ip(), self.port())
+        } else {
+            const IPV6_SOCKET_BUF_LEN: usize = (4 * 8)  // The address
             + 7  // The colon separators
             + 2  // The brackets
             + 1 + 5; // The port
 
-        let mut buf = [0; IPV6_SOCKET_BUF_LEN];
-        let mut buf_slice = &mut buf[..];
+            let mut buf = [0; IPV6_SOCKET_BUF_LEN];
+            let mut buf_slice = &mut buf[..];
 
-        // Unwrap is fine because writing to a buffer is infallible
-        write!(buf_slice, "[{}]:{}", self.ip(), self.port()).unwrap();
-        let len = IPV6_SOCKET_BUF_LEN - buf_slice.len();
+            // Unwrap is fine because writing to a buffer is infallible
+            write!(buf_slice, "[{}]:{}", self.ip(), self.port()).unwrap();
+            let len = IPV6_SOCKET_BUF_LEN - buf_slice.len();
 
-        // This unsafe is OK because we know what is being written to the buffer
-        let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
-        f.pad(buf)
+            // This unsafe is OK because we know what is being written to the buffer
+            let buf = unsafe { crate::str::from_utf8_unchecked(&buf[..len]) };
+            f.pad(buf)
+        }
     }
 }
 
@@ -1192,6 +1204,28 @@ mod tests {
         assert!(v6.is_ipv6());
     }
 
+    #[test]
+    fn socket_v4_to_str() {
+        let socket = SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 1), 8080);
+
+        assert_eq!(format!("{}", socket), "192.168.0.1:8080");
+        assert_eq!(format!("{:<20}", socket), "192.168.0.1:8080    ");
+        assert_eq!(format!("{:>20}", socket), "    192.168.0.1:8080");
+        assert_eq!(format!("{:^20}", socket), "  192.168.0.1:8080  ");
+        assert_eq!(format!("{:.10}", socket), "192.168.0.");
+    }
+
+    #[test]
+    fn socket_v6_to_str() {
+        let socket: SocketAddrV6 = "[2a02:6b8:0:1::1]:53".parse().unwrap();
+
+        assert_eq!(format!("{}", socket), "[2a02:6b8:0:1::1]:53");
+        assert_eq!(format!("{:<24}", socket), "[2a02:6b8:0:1::1]:53    ");
+        assert_eq!(format!("{:>24}", socket), "    [2a02:6b8:0:1::1]:53");
+        assert_eq!(format!("{:^24}", socket), "  [2a02:6b8:0:1::1]:53  ");
+        assert_eq!(format!("{:.15}", socket), "[2a02:6b8:0:1::");
+    }
+
     #[test]
     fn compare() {
         let v4_1 = "224.120.45.1:23456".parse::<SocketAddrV4>().unwrap();

From 06a97a027a21f6fe67f91b0630291fbb62d2de83 Mon Sep 17 00:00:00 2001
From: Nathan West <Lucretiel@gmail.com>
Date: Fri, 22 May 2020 15:59:38 -0400
Subject: [PATCH 20/26] Clarify comment message & MAX_LENGTH const

---
 src/libstd/net/addr.rs | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs
index 2febe157a506d..b780340884e1f 100644
--- a/src/libstd/net/addr.rs
+++ b/src/libstd/net/addr.rs
@@ -605,11 +605,14 @@ impl fmt::Display for SocketAddrV4 {
         if f.precision().is_none() && f.width().is_none() {
             write!(f, "{}:{}", self.ip(), self.port())
         } else {
-            const IPV4_SOCKET_BUF_LEN: usize = 21;
+            const IPV4_SOCKET_BUF_LEN: usize = (3 * 4)  // the segments
+                + 3  // the separators
+                + 1 + 5; // the port
             let mut buf = [0; IPV4_SOCKET_BUF_LEN];
             let mut buf_slice = &mut buf[..];
 
-            // Unwrap is fine because writing to a buffer is infallible
+            // Unwrap is fine because writing to a sufficiently-sized
+            // buffer is infallible
             write!(buf_slice, "{}:{}", self.ip(), self.port()).unwrap();
             let len = IPV4_SOCKET_BUF_LEN - buf_slice.len();
 
@@ -643,7 +646,8 @@ impl fmt::Display for SocketAddrV6 {
             let mut buf = [0; IPV6_SOCKET_BUF_LEN];
             let mut buf_slice = &mut buf[..];
 
-            // Unwrap is fine because writing to a buffer is infallible
+            // Unwrap is fine because writing to a sufficiently-sized
+            // buffer is infallible
             write!(buf_slice, "[{}]:{}", self.ip(), self.port()).unwrap();
             let len = IPV6_SOCKET_BUF_LEN - buf_slice.len();
 

From 12c03db157ced04b5436288781ae0a1728d78e02 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Fri, 29 May 2020 11:06:48 +0200
Subject: [PATCH 21/26] Add missing empty line in E0619 explanation

---
 src/librustc_error_codes/error_codes/E0619.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/librustc_error_codes/error_codes/E0619.md b/src/librustc_error_codes/error_codes/E0619.md
index 8727692c0a5b8..f516de43095bd 100644
--- a/src/librustc_error_codes/error_codes/E0619.md
+++ b/src/librustc_error_codes/error_codes/E0619.md
@@ -1,4 +1,5 @@
 #### Note: this error code is no longer emitted by the compiler.
+
 The type-checker needed to know the type of an expression, but that type had not
 yet been inferred.
 

From 3f13d97d275c806ea6616071566d8c4f875f18a8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= <tomasz.miasko@gmail.com>
Date: Wed, 20 May 2020 00:00:00 +0000
Subject: [PATCH 22/26] liveness: Log information about used variables

---
 src/librustc_passes/liveness.rs | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/src/librustc_passes/liveness.rs b/src/librustc_passes/liveness.rs
index ece78d0251281..03ec824366203 100644
--- a/src/librustc_passes/liveness.rs
+++ b/src/librustc_passes/liveness.rs
@@ -357,7 +357,7 @@ fn visit_fn<'tcx>(
     sp: Span,
     id: hir::HirId,
 ) {
-    debug!("visit_fn");
+    debug!("visit_fn {:?}", id);
 
     // swap in a new set of IR maps for this function body:
     let def_id = ir.tcx.hir().local_def_id(id);
@@ -777,12 +777,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
 
     fn write_vars<F>(&self, wr: &mut dyn Write, ln: LiveNode, mut test: F) -> io::Result<()>
     where
-        F: FnMut(usize) -> LiveNode,
+        F: FnMut(usize) -> bool,
     {
         let node_base_idx = self.idx(ln, Variable(0));
         for var_idx in 0..self.ir.num_vars {
             let idx = node_base_idx + var_idx;
-            if test(idx).is_valid() {
+            if test(idx) {
                 write!(wr, " {:?}", Variable(var_idx as u32))?;
             }
         }
@@ -795,9 +795,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         {
             let wr = &mut wr as &mut dyn Write;
             write!(wr, "[ln({:?}) of kind {:?} reads", ln.get(), self.ir.lnk(ln));
-            self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx));
+            self.write_vars(wr, ln, |idx| self.rwu_table.get_reader(idx).is_valid());
             write!(wr, "  writes");
-            self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx));
+            self.write_vars(wr, ln, |idx| self.rwu_table.get_writer(idx).is_valid());
+            write!(wr, "  uses");
+            self.write_vars(wr, ln, |idx| self.rwu_table.get_used(idx));
+
             write!(wr, "  precedes {:?}]", self.successors[ln.get()]);
         }
         String::from_utf8(wr).unwrap()

From b3342b4920f2c89e0e41a8c365f576c1d9cdc004 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= <tomasz.miasko@gmail.com>
Date: Thu, 21 May 2020 00:00:00 +0000
Subject: [PATCH 23/26] liveness: Remove unused clean_exit_var

---
 src/librustc_passes/liveness.rs | 15 +--------------
 1 file changed, 1 insertion(+), 14 deletions(-)

diff --git a/src/librustc_passes/liveness.rs b/src/librustc_passes/liveness.rs
index 03ec824366203..db97445f50b9c 100644
--- a/src/librustc_passes/liveness.rs
+++ b/src/librustc_passes/liveness.rs
@@ -85,13 +85,6 @@
 //!   the function, whether it be by explicit return, panic, or other means.
 //!
 //! - `fallthrough_ln`: a live node that represents a fallthrough
-//!
-//! - `clean_exit_var`: a synthetic variable that is only 'read' from the
-//!   fallthrough node. It is only live if the function could converge
-//!   via means other than an explicit `return` expression. That is, it is
-//!   only dead if the end of the function's block can never be reached.
-//!   It is the responsibility of typeck to ensure that there are no
-//!   `return` expressions in a function declared as diverging.
 
 use self::LiveNodeKind::*;
 use self::VarKind::*;
@@ -253,7 +246,6 @@ struct LocalInfo {
 enum VarKind {
     Param(HirId, Symbol),
     Local(LocalInfo),
-    CleanExit,
 }
 
 struct IrMaps<'tcx> {
@@ -309,7 +301,6 @@ impl IrMaps<'tcx> {
             Local(LocalInfo { id: node_id, .. }) | Param(node_id, _) => {
                 self.variable_map.insert(node_id, v);
             }
-            CleanExit => {}
         }
 
         debug!("{:?} is {:?}", v, vk);
@@ -329,14 +320,13 @@ impl IrMaps<'tcx> {
     fn variable_name(&self, var: Variable) -> String {
         match self.var_kinds[var.get()] {
             Local(LocalInfo { name, .. }) | Param(_, name) => name.to_string(),
-            CleanExit => "<clean-exit>".to_owned(),
         }
     }
 
     fn variable_is_shorthand(&self, var: Variable) -> bool {
         match self.var_kinds[var.get()] {
             Local(LocalInfo { is_shorthand, .. }) => is_shorthand,
-            Param(..) | CleanExit => false,
+            Param(..) => false,
         }
     }
 
@@ -649,7 +639,6 @@ impl RWUTable {
 struct Specials {
     exit_ln: LiveNode,
     fallthrough_ln: LiveNode,
-    clean_exit_var: Variable,
 }
 
 const ACC_READ: u32 = 1;
@@ -680,7 +669,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         let specials = Specials {
             exit_ln: ir.add_live_node(ExitNode),
             fallthrough_ln: ir.add_live_node(ExitNode),
-            clean_exit_var: ir.add_variable(CleanExit),
         };
 
         let tables = ir.tcx.typeck_tables_of(def_id);
@@ -913,7 +901,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         // explicitly return:
         let s = self.s;
         self.init_from_succ(s.fallthrough_ln, s.exit_ln);
-        self.acc(s.fallthrough_ln, s.clean_exit_var, ACC_READ);
 
         let entry_ln = self.propagate_through_expr(body, s.fallthrough_ln);
 

From 7c6301496bbc5d1531369e94a17450c9ad0bbbe6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= <tomasz.miasko@gmail.com>
Date: Thu, 21 May 2020 00:00:00 +0000
Subject: [PATCH 24/26] liveness: Remove unused fallthrough_ln

---
 src/librustc_passes/liveness.rs | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/src/librustc_passes/liveness.rs b/src/librustc_passes/liveness.rs
index db97445f50b9c..33ee08aebc31f 100644
--- a/src/librustc_passes/liveness.rs
+++ b/src/librustc_passes/liveness.rs
@@ -83,8 +83,6 @@
 //!
 //! - `exit_ln`: a live node that is generated to represent every 'exit' from
 //!   the function, whether it be by explicit return, panic, or other means.
-//!
-//! - `fallthrough_ln`: a live node that represents a fallthrough
 
 use self::LiveNodeKind::*;
 use self::VarKind::*;
@@ -638,7 +636,6 @@ impl RWUTable {
 #[derive(Copy, Clone)]
 struct Specials {
     exit_ln: LiveNode,
-    fallthrough_ln: LiveNode,
 }
 
 const ACC_READ: u32 = 1;
@@ -668,7 +665,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         //   an implicit return
         let specials = Specials {
             exit_ln: ir.add_live_node(ExitNode),
-            fallthrough_ln: ir.add_live_node(ExitNode),
         };
 
         let tables = ir.tcx.typeck_tables_of(def_id);
@@ -897,12 +893,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
     fn compute(&mut self, body: &hir::Expr<'_>) -> LiveNode {
         debug!("compute: using id for body, {:?}", body);
 
-        // the fallthrough exit is only for those cases where we do not
-        // explicitly return:
         let s = self.s;
-        self.init_from_succ(s.fallthrough_ln, s.exit_ln);
-
-        let entry_ln = self.propagate_through_expr(body, s.fallthrough_ln);
+        let entry_ln = self.propagate_through_expr(body, s.exit_ln);
 
         // hack to skip the loop unless debug! is enabled:
         debug!(

From 74fcbfb49149c07ad9beea0ddd47d11b125e0e13 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= <tomasz.miasko@gmail.com>
Date: Thu, 21 May 2020 00:00:00 +0000
Subject: [PATCH 25/26] liveness: Include upvars in the analysis

---
 src/librustc_passes/liveness.rs | 69 ++++++++++++++++-----------------
 1 file changed, 33 insertions(+), 36 deletions(-)

diff --git a/src/librustc_passes/liveness.rs b/src/librustc_passes/liveness.rs
index 33ee08aebc31f..00dc8a41350b3 100644
--- a/src/librustc_passes/liveness.rs
+++ b/src/librustc_passes/liveness.rs
@@ -244,6 +244,7 @@ struct LocalInfo {
 enum VarKind {
     Param(HirId, Symbol),
     Local(LocalInfo),
+    Upvar(HirId, Symbol),
 }
 
 struct IrMaps<'tcx> {
@@ -296,7 +297,7 @@ impl IrMaps<'tcx> {
         self.num_vars += 1;
 
         match vk {
-            Local(LocalInfo { id: node_id, .. }) | Param(node_id, _) => {
+            Local(LocalInfo { id: node_id, .. }) | Param(node_id, _) | Upvar(node_id, _) => {
                 self.variable_map.insert(node_id, v);
             }
         }
@@ -317,14 +318,14 @@ impl IrMaps<'tcx> {
 
     fn variable_name(&self, var: Variable) -> String {
         match self.var_kinds[var.get()] {
-            Local(LocalInfo { name, .. }) | Param(_, name) => name.to_string(),
+            Local(LocalInfo { name, .. }) | Param(_, name) | Upvar(_, name) => name.to_string(),
         }
     }
 
     fn variable_is_shorthand(&self, var: Variable) -> bool {
         match self.var_kinds[var.get()] {
             Local(LocalInfo { is_shorthand, .. }) => is_shorthand,
-            Param(..) => false,
+            Param(..) | Upvar(..) => false,
         }
     }
 
@@ -365,6 +366,14 @@ fn visit_fn<'tcx>(
 
     let body = ir.tcx.hir().body(body_id);
 
+    if let Some(upvars) = ir.tcx.upvars_mentioned(def_id) {
+        for (&var_hir_id, _upvar) in upvars {
+            debug!("adding upvar {:?}", var_hir_id);
+            let var_name = ir.tcx.hir().name(var_hir_id);
+            fn_maps.add_variable(Upvar(var_hir_id, var_name));
+        }
+    }
+
     for param in body.params {
         let is_shorthand = match param.pat.kind {
             rustc_hir::PatKind::Struct(..) => true,
@@ -450,11 +459,8 @@ fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) {
         // live nodes required for uses or definitions of variables:
         hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
             debug!("expr {}: path that leads to {:?}", expr.hir_id, path.res);
-            if let Res::Local(var_hir_id) = path.res {
-                let upvars = ir.tcx.upvars_mentioned(ir.body_owner);
-                if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hir_id)) {
-                    ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
-                }
+            if let Res::Local(_var_hir_id) = path.res {
+                ir.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
             }
             intravisit::walk_expr(ir, expr);
         }
@@ -470,16 +476,9 @@ fn visit_expr<'tcx>(ir: &mut IrMaps<'tcx>, expr: &'tcx Expr<'tcx>) {
             let mut call_caps = Vec::new();
             let closure_def_id = ir.tcx.hir().local_def_id(expr.hir_id);
             if let Some(upvars) = ir.tcx.upvars_mentioned(closure_def_id) {
-                let parent_upvars = ir.tcx.upvars_mentioned(ir.body_owner);
-                call_caps.extend(upvars.iter().filter_map(|(&var_id, upvar)| {
-                    let has_parent =
-                        parent_upvars.map_or(false, |upvars| upvars.contains_key(&var_id));
-                    if !has_parent {
-                        let upvar_ln = ir.add_live_node(UpvarNode(upvar.span));
-                        Some(CaptureInfo { ln: upvar_ln, var_hid: var_id })
-                    } else {
-                        None
-                    }
+                call_caps.extend(upvars.iter().map(|(&var_id, upvar)| {
+                    let upvar_ln = ir.add_live_node(UpvarNode(upvar.span));
+                    CaptureInfo { ln: upvar_ln, var_hid: var_id }
                 }));
             }
             ir.set_captures(expr.hir_id, call_caps);
@@ -894,6 +893,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         debug!("compute: using id for body, {:?}", body);
 
         let s = self.s;
+
+        if let Some(upvars) = self.ir.tcx.upvars_mentioned(self.ir.body_owner) {
+            for (&var_hir_id, upvar) in upvars.iter().rev() {
+                let var = self.variable(var_hir_id, upvar.span);
+                self.acc(s.exit_ln, var, ACC_READ | ACC_USE);
+            }
+        }
+
         let entry_ln = self.propagate_through_expr(body, s.exit_ln);
 
         // hack to skip the loop unless debug! is enabled:
@@ -1345,14 +1352,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         acc: u32,
     ) -> LiveNode {
         match path.res {
-            Res::Local(hid) => {
-                let upvars = self.ir.tcx.upvars_mentioned(self.ir.body_owner);
-                if !upvars.map_or(false, |upvars| upvars.contains_key(&hid)) {
-                    self.access_var(hir_id, hid, succ, acc, path.span)
-                } else {
-                    succ
-                }
-            }
+            Res::Local(hid) => self.access_var(hir_id, hid, succ, acc, path.span),
             _ => succ,
         }
     }
@@ -1511,16 +1511,13 @@ impl<'tcx> Liveness<'_, 'tcx> {
         match expr.kind {
             hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => {
                 if let Res::Local(var_hid) = path.res {
-                    let upvars = self.ir.tcx.upvars_mentioned(self.ir.body_owner);
-                    if !upvars.map_or(false, |upvars| upvars.contains_key(&var_hid)) {
-                        // Assignment to an immutable variable or argument: only legal
-                        // if there is no later assignment. If this local is actually
-                        // mutable, then check for a reassignment to flag the mutability
-                        // as being used.
-                        let ln = self.live_node(expr.hir_id, expr.span);
-                        let var = self.variable(var_hid, expr.span);
-                        self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var);
-                    }
+                    // Assignment to an immutable variable or argument: only legal
+                    // if there is no later assignment. If this local is actually
+                    // mutable, then check for a reassignment to flag the mutability
+                    // as being used.
+                    let ln = self.live_node(expr.hir_id, expr.span);
+                    let var = self.variable(var_hid, expr.span);
+                    self.warn_about_dead_assign(vec![expr.span], expr.hir_id, ln, var);
                 }
             }
             _ => {

From 4dc56614b27337fe770943760f41a58bbab7c7c6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= <tomasz.miasko@gmail.com>
Date: Fri, 22 May 2020 00:00:00 +0000
Subject: [PATCH 26/26] liveness: Warn about unused captured variables

---
 src/librustc_passes/liveness.rs               | 224 +++++++++++++-----
 .../closure-immutable-outer-variable.fixed    |   2 +-
 .../closure-immutable-outer-variable.rs       |   2 +-
 .../closure-immutable-outer-variable.stderr   |   4 +-
 src/test/ui/issues/issue-11958.rs             |   3 +-
 src/test/ui/issues/issue-11958.stderr         |  20 ++
 src/test/ui/liveness/liveness-upvars.rs       | 108 +++++++++
 src/test/ui/liveness/liveness-upvars.stderr   | 150 ++++++++++++
 .../unboxed-closures-counter-not-moved.rs     |   6 +-
 .../unboxed-closures-counter-not-moved.stderr |  27 +++
 .../unboxed-closures-move-mutable.rs          |   4 +-
 .../unboxed-closures-move-mutable.stderr      |  19 ++
 12 files changed, 505 insertions(+), 64 deletions(-)
 create mode 100644 src/test/ui/issues/issue-11958.stderr
 create mode 100644 src/test/ui/liveness/liveness-upvars.rs
 create mode 100644 src/test/ui/liveness/liveness-upvars.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr
 create mode 100644 src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr

diff --git a/src/librustc_passes/liveness.rs b/src/librustc_passes/liveness.rs
index 00dc8a41350b3..55978afc59437 100644
--- a/src/librustc_passes/liveness.rs
+++ b/src/librustc_passes/liveness.rs
@@ -76,13 +76,10 @@
 //!   is not just used to generate a new value. For example, `x += 1` is
 //!   a read but not a use. This is used to generate better warnings.
 //!
-//! ## Special Variables
+//! ## Special nodes and variables
 //!
-//! We generate various special variables for various, well, special purposes.
-//! These are described in the `specials` struct:
-//!
-//! - `exit_ln`: a live node that is generated to represent every 'exit' from
-//!   the function, whether it be by explicit return, panic, or other means.
+//! We generate various special nodes for various, well, special purposes.
+//! These are described in the `Specials` struct.
 
 use self::LiveNodeKind::*;
 use self::VarKind::*;
@@ -131,6 +128,7 @@ enum LiveNodeKind {
     UpvarNode(Span),
     ExprNode(Span),
     VarDefNode(Span),
+    ClosureNode,
     ExitNode,
 }
 
@@ -140,6 +138,7 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String {
         UpvarNode(s) => format!("Upvar node [{}]", sm.span_to_string(s)),
         ExprNode(s) => format!("Expr node [{}]", sm.span_to_string(s)),
         VarDefNode(s) => format!("Var def node [{}]", sm.span_to_string(s)),
+        ClosureNode => "Closure node".to_owned(),
         ExitNode => "Exit node".to_owned(),
     }
 }
@@ -396,10 +395,12 @@ fn visit_fn<'tcx>(
 
     // compute liveness
     let mut lsets = Liveness::new(&mut fn_maps, def_id);
-    let entry_ln = lsets.compute(&body.value);
+    let entry_ln = lsets.compute(fk, &body, sp, id);
+    lsets.log_liveness(entry_ln, id);
 
     // check for various error conditions
     lsets.visit_body(body);
+    lsets.warn_about_unused_upvars(entry_ln);
     lsets.warn_about_unused_args(body, entry_ln);
 }
 
@@ -634,6 +635,12 @@ impl RWUTable {
 
 #[derive(Copy, Clone)]
 struct Specials {
+    /// A live node representing a point of execution before closure entry &
+    /// after closure exit. Used to calculate liveness of captured variables
+    /// through calls to the same closure. Used for Fn & FnMut closures only.
+    closure_ln: LiveNode,
+    /// A live node representing every 'exit' from the function, whether it be
+    /// by explicit return, panic, or other means.
     exit_ln: LiveNode,
 }
 
@@ -658,11 +665,8 @@ struct Liveness<'a, 'tcx> {
 
 impl<'a, 'tcx> Liveness<'a, 'tcx> {
     fn new(ir: &'a mut IrMaps<'tcx>, def_id: LocalDefId) -> Liveness<'a, 'tcx> {
-        // Special nodes and variables:
-        // - exit_ln represents the end of the fn, either by return or panic
-        // - implicit_ret_var is a pseudo-variable that represents
-        //   an implicit return
         let specials = Specials {
+            closure_ln: ir.add_live_node(ClosureNode),
             exit_ln: ir.add_live_node(ExitNode),
         };
 
@@ -789,6 +793,20 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         String::from_utf8(wr).unwrap()
     }
 
+    fn log_liveness(&self, entry_ln: LiveNode, hir_id: hir::HirId) {
+        // hack to skip the loop unless debug! is enabled:
+        debug!(
+            "^^ liveness computation results for body {} (entry={:?})",
+            {
+                for ln_idx in 0..self.ir.num_live_nodes {
+                    debug!("{:?}", self.ln_str(LiveNode(ln_idx as u32)));
+                }
+                hir_id
+            },
+            entry_ln
+        );
+    }
+
     fn init_empty(&mut self, ln: LiveNode, succ_ln: LiveNode) {
         self.successors[ln.get()] = succ_ln;
 
@@ -889,33 +907,87 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         self.rwu_table.assign_unpacked(idx, rwu);
     }
 
-    fn compute(&mut self, body: &hir::Expr<'_>) -> LiveNode {
-        debug!("compute: using id for body, {:?}", body);
+    fn compute(
+        &mut self,
+        fk: FnKind<'_>,
+        body: &hir::Body<'_>,
+        span: Span,
+        id: hir::HirId,
+    ) -> LiveNode {
+        debug!("compute: using id for body, {:?}", body.value);
 
-        let s = self.s;
+        // # Liveness of captured variables
+        //
+        // When computing the liveness for captured variables we take into
+        // account how variable is captured (ByRef vs ByValue) and what is the
+        // closure kind (Generator / FnOnce vs Fn / FnMut).
+        //
+        // Variables captured by reference are assumed to be used on the exit
+        // from the closure.
+        //
+        // In FnOnce closures, variables captured by value are known to be dead
+        // on exit since it is impossible to call the closure again.
+        //
+        // In Fn / FnMut closures, variables captured by value are live on exit
+        // if they are live on the entry to the closure, since only the closure
+        // itself can access them on subsequent calls.
 
         if let Some(upvars) = self.ir.tcx.upvars_mentioned(self.ir.body_owner) {
+            // Mark upvars captured by reference as used after closure exits.
             for (&var_hir_id, upvar) in upvars.iter().rev() {
-                let var = self.variable(var_hir_id, upvar.span);
-                self.acc(s.exit_ln, var, ACC_READ | ACC_USE);
+                let upvar_id = ty::UpvarId {
+                    var_path: ty::UpvarPath { hir_id: var_hir_id },
+                    closure_expr_id: self.ir.body_owner.expect_local(),
+                };
+                match self.tables.upvar_capture(upvar_id) {
+                    ty::UpvarCapture::ByRef(_) => {
+                        let var = self.variable(var_hir_id, upvar.span);
+                        self.acc(self.s.exit_ln, var, ACC_READ | ACC_USE);
+                    }
+                    ty::UpvarCapture::ByValue => {}
+                }
             }
         }
 
-        let entry_ln = self.propagate_through_expr(body, s.exit_ln);
+        let succ = self.propagate_through_expr(&body.value, self.s.exit_ln);
 
-        // hack to skip the loop unless debug! is enabled:
-        debug!(
-            "^^ liveness computation results for body {} (entry={:?})",
-            {
-                for ln_idx in 0..self.ir.num_live_nodes {
-                    debug!("{:?}", self.ln_str(LiveNode(ln_idx as u32)));
-                }
-                body.hir_id
+        match fk {
+            FnKind::Method(..) | FnKind::ItemFn(..) => return succ,
+            FnKind::Closure(..) => {}
+        }
+
+        let ty = self.tables.node_type(id);
+        match ty.kind {
+            ty::Closure(_def_id, substs) => match substs.as_closure().kind() {
+                ty::ClosureKind::Fn => {}
+                ty::ClosureKind::FnMut => {}
+                ty::ClosureKind::FnOnce => return succ,
             },
-            entry_ln
-        );
+            ty::Generator(..) => return succ,
+            _ => {
+                span_bug!(span, "type of closure expr {:?} is not a closure {:?}", id, ty,);
+            }
+        };
 
-        entry_ln
+        // Propagate through calls to the closure.
+        let mut first_merge = true;
+        loop {
+            self.init_from_succ(self.s.closure_ln, succ);
+            for param in body.params {
+                param.pat.each_binding(|_bm, hir_id, _x, ident| {
+                    let var = self.variable(hir_id, ident.span);
+                    self.define(self.s.closure_ln, var);
+                })
+            }
+
+            if !self.merge_from_succ(self.s.exit_ln, self.s.closure_ln, first_merge) {
+                break;
+            }
+            first_merge = false;
+            assert_eq!(succ, self.propagate_through_expr(&body.value, self.s.exit_ln));
+        }
+
+        succ
     }
 
     fn propagate_through_block(&mut self, blk: &hir::Block<'_>, succ: LiveNode) -> LiveNode {
@@ -1533,11 +1605,60 @@ impl<'tcx> Liveness<'_, 'tcx> {
         if name.is_empty() || name.as_bytes()[0] == b'_' { None } else { Some(name) }
     }
 
+    fn warn_about_unused_upvars(&self, entry_ln: LiveNode) {
+        let upvars = match self.ir.tcx.upvars_mentioned(self.ir.body_owner) {
+            None => return,
+            Some(upvars) => upvars,
+        };
+        for (&var_hir_id, upvar) in upvars.iter() {
+            let var = self.variable(var_hir_id, upvar.span);
+            let upvar_id = ty::UpvarId {
+                var_path: ty::UpvarPath { hir_id: var_hir_id },
+                closure_expr_id: self.ir.body_owner.expect_local(),
+            };
+            match self.tables.upvar_capture(upvar_id) {
+                ty::UpvarCapture::ByValue => {}
+                ty::UpvarCapture::ByRef(..) => continue,
+            };
+            if self.used_on_entry(entry_ln, var) {
+                if self.live_on_entry(entry_ln, var).is_none() {
+                    if let Some(name) = self.should_warn(var) {
+                        self.ir.tcx.struct_span_lint_hir(
+                            lint::builtin::UNUSED_ASSIGNMENTS,
+                            var_hir_id,
+                            vec![upvar.span],
+                            |lint| {
+                                lint.build(&format!("value captured by `{}` is never read", name))
+                                    .help("did you mean to capture by reference instead?")
+                                    .emit();
+                            },
+                        );
+                    }
+                }
+            } else {
+                if let Some(name) = self.should_warn(var) {
+                    self.ir.tcx.struct_span_lint_hir(
+                        lint::builtin::UNUSED_VARIABLES,
+                        var_hir_id,
+                        vec![upvar.span],
+                        |lint| {
+                            lint.build(&format!("unused variable: `{}`", name))
+                                .help("did you mean to capture by reference instead?")
+                                .emit();
+                        },
+                    );
+                }
+            }
+        }
+    }
+
     fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) {
         for p in body.params {
             self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| {
                 if self.live_on_entry(ln, var).is_none() {
-                    self.report_dead_assign(hir_id, spans, var, true);
+                    self.report_unsed_assign(hir_id, spans, var, |name| {
+                        format!("value passed to `{}` is never read", name)
+                    });
                 }
             });
         }
@@ -1651,35 +1772,30 @@ impl<'tcx> Liveness<'_, 'tcx> {
 
     fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
         if self.live_on_exit(ln, var).is_none() {
-            self.report_dead_assign(hir_id, spans, var, false);
+            self.report_unsed_assign(hir_id, spans, var, |name| {
+                format!("value assigned to `{}` is never read", name)
+            });
         }
     }
 
-    fn report_dead_assign(&self, hir_id: HirId, spans: Vec<Span>, var: Variable, is_param: bool) {
+    fn report_unsed_assign(
+        &self,
+        hir_id: HirId,
+        spans: Vec<Span>,
+        var: Variable,
+        message: impl Fn(&str) -> String,
+    ) {
         if let Some(name) = self.should_warn(var) {
-            if is_param {
-                self.ir.tcx.struct_span_lint_hir(
-                    lint::builtin::UNUSED_ASSIGNMENTS,
-                    hir_id,
-                    spans,
-                    |lint| {
-                        lint.build(&format!("value passed to `{}` is never read", name))
-                            .help("maybe it is overwritten before being read?")
-                            .emit();
-                    },
-                )
-            } else {
-                self.ir.tcx.struct_span_lint_hir(
-                    lint::builtin::UNUSED_ASSIGNMENTS,
-                    hir_id,
-                    spans,
-                    |lint| {
-                        lint.build(&format!("value assigned to `{}` is never read", name))
-                            .help("maybe it is overwritten before being read?")
-                            .emit();
-                    },
-                )
-            }
+            self.ir.tcx.struct_span_lint_hir(
+                lint::builtin::UNUSED_ASSIGNMENTS,
+                hir_id,
+                spans,
+                |lint| {
+                    lint.build(&message(&name))
+                        .help("maybe it is overwritten before being read?")
+                        .emit();
+                },
+            )
         }
     }
 }
diff --git a/src/test/ui/closures/closure-immutable-outer-variable.fixed b/src/test/ui/closures/closure-immutable-outer-variable.fixed
index 102f1f94a36e1..1b0feede34ecf 100644
--- a/src/test/ui/closures/closure-immutable-outer-variable.fixed
+++ b/src/test/ui/closures/closure-immutable-outer-variable.fixed
@@ -8,6 +8,6 @@ fn foo(mut f: Box<dyn FnMut()>) {
 
 fn main() {
     let mut y = true;
-    foo(Box::new(move || y = false) as Box<_>);
+    foo(Box::new(move || y = !y) as Box<_>);
     //~^ ERROR cannot assign to `y`, as it is not declared as mutable
 }
diff --git a/src/test/ui/closures/closure-immutable-outer-variable.rs b/src/test/ui/closures/closure-immutable-outer-variable.rs
index 6eb43b372c96c..50ec1c6148a04 100644
--- a/src/test/ui/closures/closure-immutable-outer-variable.rs
+++ b/src/test/ui/closures/closure-immutable-outer-variable.rs
@@ -8,6 +8,6 @@ fn foo(mut f: Box<dyn FnMut()>) {
 
 fn main() {
     let y = true;
-    foo(Box::new(move || y = false) as Box<_>);
+    foo(Box::new(move || y = !y) as Box<_>);
     //~^ ERROR cannot assign to `y`, as it is not declared as mutable
 }
diff --git a/src/test/ui/closures/closure-immutable-outer-variable.stderr b/src/test/ui/closures/closure-immutable-outer-variable.stderr
index 7e60f3cd8ffa4..799097889cd30 100644
--- a/src/test/ui/closures/closure-immutable-outer-variable.stderr
+++ b/src/test/ui/closures/closure-immutable-outer-variable.stderr
@@ -3,8 +3,8 @@ error[E0594]: cannot assign to `y`, as it is not declared as mutable
    |
 LL |     let y = true;
    |         - help: consider changing this to be mutable: `mut y`
-LL |     foo(Box::new(move || y = false) as Box<_>);
-   |                          ^^^^^^^^^ cannot assign
+LL |     foo(Box::new(move || y = !y) as Box<_>);
+   |                          ^^^^^^ cannot assign
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-11958.rs b/src/test/ui/issues/issue-11958.rs
index 8fe8a8c606189..a7af01e25b4e2 100644
--- a/src/test/ui/issues/issue-11958.rs
+++ b/src/test/ui/issues/issue-11958.rs
@@ -1,5 +1,4 @@
 // run-pass
-#![forbid(warnings)]
 
 // We shouldn't need to rebind a moved upvar as mut if it's already
 // marked as mut
@@ -7,4 +6,6 @@
 pub fn main() {
     let mut x = 1;
     let _thunk = Box::new(move|| { x = 2; });
+    //~^ WARN value assigned to `x` is never read
+    //~| WARN unused variable: `x`
 }
diff --git a/src/test/ui/issues/issue-11958.stderr b/src/test/ui/issues/issue-11958.stderr
new file mode 100644
index 0000000000000..25de6ff4c118c
--- /dev/null
+++ b/src/test/ui/issues/issue-11958.stderr
@@ -0,0 +1,20 @@
+warning: value assigned to `x` is never read
+  --> $DIR/issue-11958.rs:8:36
+   |
+LL |     let _thunk = Box::new(move|| { x = 2; });
+   |                                    ^
+   |
+   = note: `#[warn(unused_assignments)]` on by default
+   = help: maybe it is overwritten before being read?
+
+warning: unused variable: `x`
+  --> $DIR/issue-11958.rs:8:36
+   |
+LL |     let _thunk = Box::new(move|| { x = 2; });
+   |                                    ^
+   |
+   = note: `#[warn(unused_variables)]` on by default
+   = help: did you mean to capture by reference instead?
+
+warning: 2 warnings emitted
+
diff --git a/src/test/ui/liveness/liveness-upvars.rs b/src/test/ui/liveness/liveness-upvars.rs
new file mode 100644
index 0000000000000..b2837e74b8c51
--- /dev/null
+++ b/src/test/ui/liveness/liveness-upvars.rs
@@ -0,0 +1,108 @@
+// edition:2018
+// check-pass
+#![warn(unused)]
+#![allow(unreachable_code)]
+
+pub fn unintentional_copy_one() {
+    let mut last = None;
+    let mut f = move |s| {
+        last = Some(s); //~  WARN value assigned to `last` is never read
+                        //~| WARN unused variable: `last`
+    };
+    f("a");
+    f("b");
+    f("c");
+    dbg!(last.unwrap());
+}
+
+pub fn unintentional_copy_two() {
+    let mut sum = 0;
+    (1..10).for_each(move |x| {
+        sum += x; //~ WARN unused variable: `sum`
+    });
+    dbg!(sum);
+}
+
+pub fn f() {
+    let mut c = 0;
+
+    // Captured by value, but variable is dead on entry.
+    move || {
+        c = 1; //~ WARN value captured by `c` is never read
+        println!("{}", c);
+    };
+    let _ = async move {
+        c = 1; //~ WARN value captured by `c` is never read
+        println!("{}", c);
+    };
+
+    // Read and written to, but never actually used.
+    move || {
+        c += 1; //~ WARN unused variable: `c`
+    };
+    let _ = async move {
+        c += 1; //~  WARN value assigned to `c` is never read
+                //~| WARN unused variable: `c`
+    };
+
+    move || {
+        println!("{}", c);
+        // Value is read by closure itself on later invocations.
+        c += 1;
+    };
+    let b = Box::new(42);
+    move || {
+        println!("{}", c);
+        // Never read because this is FnOnce closure.
+        c += 1; //~  WARN value assigned to `c` is never read
+        drop(b);
+    };
+    let _ = async move {
+        println!("{}", c);
+        // Never read because this is a generator.
+        c += 1; //~  WARN value assigned to `c` is never read
+    };
+}
+
+pub fn nested() {
+    let mut d = None;
+    let mut e = None;
+    || {
+        || {
+            d = Some("d1"); //~ WARN value assigned to `d` is never read
+            d = Some("d2");
+        };
+        move || {
+            e = Some("e1"); //~  WARN value assigned to `e` is never read
+                            //~| WARN unused variable: `e`
+            e = Some("e2"); //~  WARN value assigned to `e` is never read
+        };
+    };
+}
+
+pub fn g<T: Default>(mut v: T) {
+    |r| {
+        if r {
+            v = T::default(); //~ WARN value assigned to `v` is never read
+        } else {
+            drop(v);
+        }
+    };
+}
+
+pub fn h<T: Copy + Default + std::fmt::Debug>() {
+    let mut z = T::default();
+    move |b| {
+        loop {
+            if b {
+                z = T::default(); //~  WARN value assigned to `z` is never read
+                                  //~| WARN unused variable: `z`
+            } else {
+                return;
+            }
+        }
+        dbg!(z);
+    };
+}
+
+fn main() {}
diff --git a/src/test/ui/liveness/liveness-upvars.stderr b/src/test/ui/liveness/liveness-upvars.stderr
new file mode 100644
index 0000000000000..14fed91786436
--- /dev/null
+++ b/src/test/ui/liveness/liveness-upvars.stderr
@@ -0,0 +1,150 @@
+warning: value assigned to `last` is never read
+  --> $DIR/liveness-upvars.rs:9:9
+   |
+LL |         last = Some(s);
+   |         ^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/liveness-upvars.rs:3:9
+   |
+LL | #![warn(unused)]
+   |         ^^^^^^
+   = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]`
+   = help: maybe it is overwritten before being read?
+
+warning: unused variable: `last`
+  --> $DIR/liveness-upvars.rs:9:9
+   |
+LL |         last = Some(s);
+   |         ^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/liveness-upvars.rs:3:9
+   |
+LL | #![warn(unused)]
+   |         ^^^^^^
+   = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
+   = help: did you mean to capture by reference instead?
+
+warning: unused variable: `sum`
+  --> $DIR/liveness-upvars.rs:21:9
+   |
+LL |         sum += x;
+   |         ^^^
+   |
+   = help: did you mean to capture by reference instead?
+
+warning: value captured by `c` is never read
+  --> $DIR/liveness-upvars.rs:31:9
+   |
+LL |         c = 1;
+   |         ^
+   |
+   = help: did you mean to capture by reference instead?
+
+warning: value captured by `c` is never read
+  --> $DIR/liveness-upvars.rs:35:9
+   |
+LL |         c = 1;
+   |         ^
+   |
+   = help: did you mean to capture by reference instead?
+
+warning: unused variable: `c`
+  --> $DIR/liveness-upvars.rs:41:9
+   |
+LL |         c += 1;
+   |         ^
+   |
+   = help: did you mean to capture by reference instead?
+
+warning: value assigned to `c` is never read
+  --> $DIR/liveness-upvars.rs:44:9
+   |
+LL |         c += 1;
+   |         ^
+   |
+   = help: maybe it is overwritten before being read?
+
+warning: unused variable: `c`
+  --> $DIR/liveness-upvars.rs:44:9
+   |
+LL |         c += 1;
+   |         ^
+   |
+   = help: did you mean to capture by reference instead?
+
+warning: value assigned to `c` is never read
+  --> $DIR/liveness-upvars.rs:57:9
+   |
+LL |         c += 1;
+   |         ^
+   |
+   = help: maybe it is overwritten before being read?
+
+warning: value assigned to `c` is never read
+  --> $DIR/liveness-upvars.rs:63:9
+   |
+LL |         c += 1;
+   |         ^
+   |
+   = help: maybe it is overwritten before being read?
+
+warning: value assigned to `d` is never read
+  --> $DIR/liveness-upvars.rs:72:13
+   |
+LL |             d = Some("d1");
+   |             ^
+   |
+   = help: maybe it is overwritten before being read?
+
+warning: value assigned to `e` is never read
+  --> $DIR/liveness-upvars.rs:76:13
+   |
+LL |             e = Some("e1");
+   |             ^
+   |
+   = help: maybe it is overwritten before being read?
+
+warning: value assigned to `e` is never read
+  --> $DIR/liveness-upvars.rs:78:13
+   |
+LL |             e = Some("e2");
+   |             ^
+   |
+   = help: maybe it is overwritten before being read?
+
+warning: unused variable: `e`
+  --> $DIR/liveness-upvars.rs:76:13
+   |
+LL |             e = Some("e1");
+   |             ^
+   |
+   = help: did you mean to capture by reference instead?
+
+warning: value assigned to `v` is never read
+  --> $DIR/liveness-upvars.rs:86:13
+   |
+LL |             v = T::default();
+   |             ^
+   |
+   = help: maybe it is overwritten before being read?
+
+warning: value assigned to `z` is never read
+  --> $DIR/liveness-upvars.rs:98:17
+   |
+LL |                 z = T::default();
+   |                 ^
+   |
+   = help: maybe it is overwritten before being read?
+
+warning: unused variable: `z`
+  --> $DIR/liveness-upvars.rs:98:17
+   |
+LL |                 z = T::default();
+   |                 ^
+   |
+   = help: did you mean to capture by reference instead?
+
+warning: 17 warnings emitted
+
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs
index fb24df3c24e87..390386e57fa72 100644
--- a/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs
+++ b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs
@@ -1,5 +1,4 @@
 // run-pass
-#![allow(unused_variables)]
 // Test that we mutate a counter on the stack only when we expect to.
 
 fn call<F>(f: F) where F : FnOnce() {
@@ -13,7 +12,7 @@ fn main() {
     call(|| {
         // Move `y`, but do not move `counter`, even though it is read
         // by value (note that it is also mutated).
-        for item in y {
+        for item in y { //~ WARN unused variable: `item`
             let v = counter;
             counter += v;
         }
@@ -22,7 +21,8 @@ fn main() {
 
     call(move || {
         // this mutates a moved copy, and hence doesn't affect original
-        counter += 1;
+        counter += 1; //~  WARN value assigned to `counter` is never read
+                      //~| WARN unused variable: `counter`
     });
     assert_eq!(counter, 88);
 }
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr
new file mode 100644
index 0000000000000..ba4b3dac6705e
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-counter-not-moved.stderr
@@ -0,0 +1,27 @@
+warning: unused variable: `item`
+  --> $DIR/unboxed-closures-counter-not-moved.rs:15:13
+   |
+LL |         for item in y {
+   |             ^^^^ help: if this is intentional, prefix it with an underscore: `_item`
+   |
+   = note: `#[warn(unused_variables)]` on by default
+
+warning: value assigned to `counter` is never read
+  --> $DIR/unboxed-closures-counter-not-moved.rs:24:9
+   |
+LL |         counter += 1;
+   |         ^^^^^^^
+   |
+   = note: `#[warn(unused_assignments)]` on by default
+   = help: maybe it is overwritten before being read?
+
+warning: unused variable: `counter`
+  --> $DIR/unboxed-closures-counter-not-moved.rs:24:9
+   |
+LL |         counter += 1;
+   |         ^^^^^^^
+   |
+   = help: did you mean to capture by reference instead?
+
+warning: 3 warnings emitted
+
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs
index 9b519e63a95cc..e5b19db782231 100644
--- a/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs
+++ b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.rs
@@ -13,11 +13,11 @@ fn set(x: &mut usize) { *x = 42; }
 fn main() {
     {
         let mut x = 0_usize;
-        move || x += 1;
+        move || x += 1; //~ WARN unused variable: `x`
     }
     {
         let mut x = 0_usize;
-        move || x += 1;
+        move || x += 1; //~ WARN unused variable: `x`
     }
     {
         let mut x = 0_usize;
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr
new file mode 100644
index 0000000000000..4dfd1bb307574
--- /dev/null
+++ b/src/test/ui/unboxed-closures/unboxed-closures-move-mutable.stderr
@@ -0,0 +1,19 @@
+warning: unused variable: `x`
+  --> $DIR/unboxed-closures-move-mutable.rs:16:17
+   |
+LL |         move || x += 1;
+   |                 ^
+   |
+   = note: `#[warn(unused_variables)]` on by default
+   = help: did you mean to capture by reference instead?
+
+warning: unused variable: `x`
+  --> $DIR/unboxed-closures-move-mutable.rs:20:17
+   |
+LL |         move || x += 1;
+   |                 ^
+   |
+   = help: did you mean to capture by reference instead?
+
+warning: 2 warnings emitted
+