From 9abcfa55c31881a3f72becd5bca9504b46c3ed8f Mon Sep 17 00:00:00 2001
From: Dan Gohman <dev@sunfishcode.online>
Date: Thu, 21 Jan 2021 15:58:50 -0800
Subject: [PATCH 01/24] Don't link with --export-dynamic on wasm32-wasi

Remove --export-dynamic from the link arguments on the wasm32-wasi
target, as it emits spurious exports and increases code size.

Leave it in place for wasm32-unknown-unknown and
wasm32-unknown-emscripten. Even though it isn't a great solution
there, users are likely depending on its behavior there.
---
 compiler/rustc_target/src/spec/wasm32_base.rs   |  9 ---------
 .../src/spec/wasm32_unknown_emscripten.rs       | 13 ++++++++++++-
 .../src/spec/wasm32_unknown_unknown.rs          | 17 ++++++++++++-----
 3 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/compiler/rustc_target/src/spec/wasm32_base.rs b/compiler/rustc_target/src/spec/wasm32_base.rs
index a7957d84cbeef..bfef3d37228f0 100644
--- a/compiler/rustc_target/src/spec/wasm32_base.rs
+++ b/compiler/rustc_target/src/spec/wasm32_base.rs
@@ -55,15 +55,6 @@ pub fn options() -> TargetOptions {
     // to do so.
     arg("--no-demangle");
 
-    // The symbol visibility story is a bit in flux right now with LLD.
-    // It's... not entirely clear to me what's going on, but this looks to
-    // make everything work when `export_symbols` isn't otherwise called for
-    // things like executables.
-    //
-    // This is really only here to get things working. If it can be removed and
-    // basic tests still work, then sounds like it should be removed!
-    arg("--export-dynamic");
-
     let mut pre_link_args = BTreeMap::new();
     pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Wasm), lld_args);
     pre_link_args.insert(LinkerFlavor::Gcc, clang_args);
diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs
index c12757b8f9812..9f69ce16c215d 100644
--- a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs
+++ b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs
@@ -2,6 +2,17 @@ use super::wasm32_base;
 use super::{LinkArgs, LinkerFlavor, PanicStrategy, Target, TargetOptions};
 
 pub fn target() -> Target {
+    let mut options = wasm32_base::options();
+
+    let clang_args = options.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap();
+
+    // Rust really needs a way for users to specify exports and imports in
+    // the source code. --export-dynamic isn't the right tool for this job,
+    // however it does have the side effect of automatically exporting a lot
+    // of symbols, which approximates what people want when compiling for
+    // wasm32-unknown-unknown expect, so use it for now.
+    clang_args.push("--export-dynamic".to_string());
+
     let mut post_link_args = LinkArgs::new();
     post_link_args.insert(
         LinkerFlavor::Em,
@@ -28,7 +39,7 @@ pub fn target() -> Target {
         panic_strategy: PanicStrategy::Unwind,
         post_link_args,
         os_family: Some("unix".to_string()),
-        ..wasm32_base::options()
+        ..options
     };
     Target {
         llvm_target: "wasm32-unknown-emscripten".to_string(),
diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs
index 6037aa5b4306e..5e89ba2520bdd 100644
--- a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs
+++ b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs
@@ -26,11 +26,18 @@ pub fn target() -> Target {
     // For now this target just never has an entry symbol no matter the output
     // type, so unconditionally pass this.
     clang_args.push("-Wl,--no-entry".to_string());
-    options
-        .pre_link_args
-        .get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm))
-        .unwrap()
-        .push("--no-entry".to_string());
+
+    // Rust really needs a way for users to specify exports and imports in
+    // the source code. --export-dynamic isn't the right tool for this job,
+    // however it does have the side effect of automatically exporting a lot
+    // of symbols, which approximates what people want when compiling for
+    // wasm32-unknown-unknown expect, so use it for now.
+    clang_args.push("-Wl,--export-dynamic".to_string());
+
+    // Add the flags to wasm-ld's args too.
+    let lld_args = options.pre_link_args.get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm)).unwrap();
+    lld_args.push("--no-entry".to_string());
+    lld_args.push("--export-dynamic".to_string());
 
     Target {
         llvm_target: "wasm32-unknown-unknown".to_string(),

From 77a9e3e8e112e2d33599942cef63b1e22b6e5d27 Mon Sep 17 00:00:00 2001
From: Bastian Kauschke <bastian_kauschke@hotmail.de>
Date: Sun, 24 Jan 2021 20:08:12 +0100
Subject: [PATCH 02/24] combine: stop eagerly evaluating consts

---
 compiler/rustc_infer/src/infer/combine.rs     | 28 +++++++++++++------
 compiler/rustc_middle/src/ty/relate.rs        | 11 +-------
 .../issues/issue-69654-run-pass.rs            |  2 +-
 .../issues/issue-69654-run-pass.stderr        | 15 ++++++++++
 .../ui/const-generics/issues/issue-69654.rs   |  1 +
 .../const-generics/issues/issue-69654.stderr  | 17 +++++++++--
 .../occurs-check/unused-substs-1.rs           |  3 +-
 .../occurs-check/unused-substs-1.stderr       | 17 +++++++++++
 .../occurs-check/unused-substs-2.rs           |  3 +-
 .../occurs-check/unused-substs-2.stderr       |  9 ++++++
 .../occurs-check/unused-substs-3.rs           |  3 +-
 .../occurs-check/unused-substs-3.stderr       | 12 ++++++++
 .../occurs-check/unused-substs-4.rs           |  3 +-
 .../occurs-check/unused-substs-4.stderr       |  9 ++++++
 14 files changed, 106 insertions(+), 27 deletions(-)
 create mode 100644 src/test/ui/const-generics/issues/issue-69654-run-pass.stderr
 create mode 100644 src/test/ui/const-generics/occurs-check/unused-substs-1.stderr
 create mode 100644 src/test/ui/const-generics/occurs-check/unused-substs-2.stderr
 create mode 100644 src/test/ui/const-generics/occurs-check/unused-substs-3.stderr
 create mode 100644 src/test/ui/const-generics/occurs-check/unused-substs-4.stderr

diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index b344086e95e77..718cd6aa8fbdd 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -543,10 +543,6 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
         true
     }
 
-    fn visit_ct_substs(&self) -> bool {
-        true
-    }
-
     fn binders<T>(
         &mut self,
         a: ty::Binder<T>,
@@ -737,6 +733,16 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
                     }
                 }
             }
+            ty::ConstKind::Unevaluated(def, substs, promoted)
+                if self.tcx().lazy_normalization() =>
+            {
+                assert_eq!(promoted, None);
+                let substs = self.relate_with_variance(ty::Variance::Invariant, substs, substs)?;
+                Ok(self.tcx().mk_const(ty::Const {
+                    ty: c.ty,
+                    val: ty::ConstKind::Unevaluated(def, substs, promoted),
+                }))
+            }
             _ => relate::super_relate_consts(self, c, c),
         }
     }
@@ -822,10 +828,6 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
         true
     }
 
-    fn visit_ct_substs(&self) -> bool {
-        true
-    }
-
     fn relate_with_variance<T: Relate<'tcx>>(
         &mut self,
         _variance: ty::Variance,
@@ -959,6 +961,16 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
                     }
                 }
             }
+            ty::ConstKind::Unevaluated(def, substs, promoted)
+                if self.tcx().lazy_normalization() =>
+            {
+                assert_eq!(promoted, None);
+                let substs = self.relate_with_variance(ty::Variance::Invariant, substs, substs)?;
+                Ok(self.tcx().mk_const(ty::Const {
+                    ty: c.ty,
+                    val: ty::ConstKind::Unevaluated(def, substs, promoted),
+                }))
+            }
             _ => relate::super_relate_consts(self, c, c),
         }
     }
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index 293b3c6b0470a..af7fc42971954 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -33,15 +33,6 @@ pub trait TypeRelation<'tcx>: Sized {
     /// relation. Just affects error messages.
     fn a_is_expected(&self) -> bool;
 
-    /// Whether we should look into the substs of unevaluated constants
-    /// even if `feature(const_evaluatable_checked)` is active.
-    ///
-    /// This is needed in `combine` to prevent accidentially creating
-    /// infinite types as we abuse `TypeRelation` to walk a type there.
-    fn visit_ct_substs(&self) -> bool {
-        false
-    }
-
     fn with_cause<F, R>(&mut self, _cause: Cause, f: F) -> R
     where
         F: FnOnce(&mut Self) -> R,
@@ -588,7 +579,7 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
         (
             ty::ConstKind::Unevaluated(a_def, a_substs, None),
             ty::ConstKind::Unevaluated(b_def, b_substs, None),
-        ) if tcx.features().const_evaluatable_checked && !relation.visit_ct_substs() => {
+        ) if tcx.features().const_evaluatable_checked => {
             if tcx.try_unify_abstract_consts(((a_def, a_substs), (b_def, b_substs))) {
                 Ok(a.val)
             } else {
diff --git a/src/test/ui/const-generics/issues/issue-69654-run-pass.rs b/src/test/ui/const-generics/issues/issue-69654-run-pass.rs
index bbfd2183b06e3..8c0398e8a13ea 100644
--- a/src/test/ui/const-generics/issues/issue-69654-run-pass.rs
+++ b/src/test/ui/const-generics/issues/issue-69654-run-pass.rs
@@ -1,4 +1,3 @@
-// run-pass
 #![feature(const_generics)]
 #![allow(incomplete_features, unused_braces)]
 
@@ -15,4 +14,5 @@ where
 
 fn main() {
     Foo::foo();
+    //~^ ERROR no function or associated item
 }
diff --git a/src/test/ui/const-generics/issues/issue-69654-run-pass.stderr b/src/test/ui/const-generics/issues/issue-69654-run-pass.stderr
new file mode 100644
index 0000000000000..a95cc0f2a1c32
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-69654-run-pass.stderr
@@ -0,0 +1,15 @@
+error[E0599]: no function or associated item named `foo` found for struct `Foo<{_: usize}>` in the current scope
+  --> $DIR/issue-69654-run-pass.rs:16:10
+   |
+LL | struct Foo<const N: usize> {}
+   | -------------------------- function or associated item `foo` not found for this
+...
+LL |     Foo::foo();
+   |          ^^^ function or associated item not found in `Foo<{_: usize}>`
+   |
+   = note: the method `foo` exists but the following trait bounds were not satisfied:
+           `[u8; _]: Bar<[(); _]>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/src/test/ui/const-generics/issues/issue-69654.rs b/src/test/ui/const-generics/issues/issue-69654.rs
index 7e775999ebd10..38fca98ad4f06 100644
--- a/src/test/ui/const-generics/issues/issue-69654.rs
+++ b/src/test/ui/const-generics/issues/issue-69654.rs
@@ -15,4 +15,5 @@ where
 
 fn main() {
     Foo::foo();
+    //~^ ERROR no function or associated item
 }
diff --git a/src/test/ui/const-generics/issues/issue-69654.stderr b/src/test/ui/const-generics/issues/issue-69654.stderr
index 70af7bf25d849..69cd0806fcd42 100644
--- a/src/test/ui/const-generics/issues/issue-69654.stderr
+++ b/src/test/ui/const-generics/issues/issue-69654.stderr
@@ -4,6 +4,19 @@ error[E0423]: expected value, found type parameter `T`
 LL | impl<T> Bar<T> for [u8; T] {}
    |                         ^ not a value
 
-error: aborting due to previous error
+error[E0599]: no function or associated item named `foo` found for struct `Foo<{_: usize}>` in the current scope
+  --> $DIR/issue-69654.rs:17:10
+   |
+LL | struct Foo<const N: usize> {}
+   | -------------------------- function or associated item `foo` not found for this
+...
+LL |     Foo::foo();
+   |          ^^^ function or associated item not found in `Foo<{_: usize}>`
+   |
+   = note: the method `foo` exists but the following trait bounds were not satisfied:
+           `[u8; _]: Bar<[(); _]>`
+
+error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0423`.
+Some errors have detailed explanations: E0423, E0599.
+For more information about an error, try `rustc --explain E0423`.
diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-1.rs b/src/test/ui/const-generics/occurs-check/unused-substs-1.rs
index f56687ecd9329..6ded9f13bc4fa 100644
--- a/src/test/ui/const-generics/occurs-check/unused-substs-1.rs
+++ b/src/test/ui/const-generics/occurs-check/unused-substs-1.rs
@@ -1,4 +1,3 @@
-// build-pass
 #![feature(const_generics)]
 #![allow(incomplete_features)]
 
@@ -10,5 +9,5 @@ where
     A<N>: Bar<N>;
 
 fn main() {
-    let _ = A;
+    let _ = A; //~ERROR the trait bound
 }
diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-1.stderr b/src/test/ui/const-generics/occurs-check/unused-substs-1.stderr
new file mode 100644
index 0000000000000..6830288acc0ad
--- /dev/null
+++ b/src/test/ui/const-generics/occurs-check/unused-substs-1.stderr
@@ -0,0 +1,17 @@
+error[E0277]: the trait bound `A<{_: usize}>: Bar<{_: usize}>` is not satisfied
+  --> $DIR/unused-substs-1.rs:12:13
+   |
+LL | / struct A<const N: usize>
+LL | | where
+LL | |     A<N>: Bar<N>;
+   | |_________________- required by `A`
+...
+LL |       let _ = A;
+   |               ^ the trait `Bar<{_: usize}>` is not implemented for `A<{_: usize}>`
+   |
+   = help: the following implementations were found:
+             <A<7_usize> as Bar<N>>
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-2.rs b/src/test/ui/const-generics/occurs-check/unused-substs-2.rs
index 12444ec5312d9..2d00141fbf70b 100644
--- a/src/test/ui/const-generics/occurs-check/unused-substs-2.rs
+++ b/src/test/ui/const-generics/occurs-check/unused-substs-2.rs
@@ -1,4 +1,3 @@
-// check-pass
 #![feature(const_generics)]
 #![allow(incomplete_features)]
 
@@ -24,4 +23,6 @@ fn main() {
     // `t` is `ty::Infer(TyVar(_#1t))`
     // `foo` contains `ty::Infer(TyVar(_#1t))` in its substs
     t = foo;
+    //~^ ERROR mismatched types
+    //~| NOTE cyclic type
 }
diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-2.stderr b/src/test/ui/const-generics/occurs-check/unused-substs-2.stderr
new file mode 100644
index 0000000000000..9532fc21a31b7
--- /dev/null
+++ b/src/test/ui/const-generics/occurs-check/unused-substs-2.stderr
@@ -0,0 +1,9 @@
+error[E0308]: mismatched types
+  --> $DIR/unused-substs-2.rs:25:9
+   |
+LL |     t = foo;
+   |         ^^^ cyclic type of infinite size
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-3.rs b/src/test/ui/const-generics/occurs-check/unused-substs-3.rs
index 187e27382fcf2..2e306f8c4c88f 100644
--- a/src/test/ui/const-generics/occurs-check/unused-substs-3.rs
+++ b/src/test/ui/const-generics/occurs-check/unused-substs-3.rs
@@ -1,4 +1,3 @@
-// check-pass
 #![feature(const_generics)]
 #![allow(incomplete_features)]
 
@@ -15,4 +14,6 @@ fn main() {
     // `t` is `ty::Infer(TyVar(_#1t))`
     // `foo` contains `ty::Infer(TyVar(_#1t))` in its substs
     t = foo;
+    //~^ ERROR mismatched types
+    //~| NOTE cyclic type
 }
diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-3.stderr b/src/test/ui/const-generics/occurs-check/unused-substs-3.stderr
new file mode 100644
index 0000000000000..2551d68f97474
--- /dev/null
+++ b/src/test/ui/const-generics/occurs-check/unused-substs-3.stderr
@@ -0,0 +1,12 @@
+error[E0308]: mismatched types
+  --> $DIR/unused-substs-3.rs:16:9
+   |
+LL |     t = foo;
+   |         ^^^
+   |         |
+   |         cyclic type of infinite size
+   |         help: try using a conversion method: `foo.to_vec()`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-4.rs b/src/test/ui/const-generics/occurs-check/unused-substs-4.rs
index 8e42ceb6d70e9..9c7f5ab91edb1 100644
--- a/src/test/ui/const-generics/occurs-check/unused-substs-4.rs
+++ b/src/test/ui/const-generics/occurs-check/unused-substs-4.rs
@@ -1,4 +1,3 @@
-// build-pass
 #![feature(const_generics)]
 #![allow(incomplete_features)]
 
@@ -8,5 +7,5 @@ fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] {
 
 fn main() {
     let mut arr = Default::default();
-    arr = bind(arr);
+    arr = bind(arr); //~ ERROR mismatched type
 }
diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-4.stderr b/src/test/ui/const-generics/occurs-check/unused-substs-4.stderr
new file mode 100644
index 0000000000000..5685eedbdeca8
--- /dev/null
+++ b/src/test/ui/const-generics/occurs-check/unused-substs-4.stderr
@@ -0,0 +1,9 @@
+error[E0308]: mismatched types
+  --> $DIR/unused-substs-4.rs:10:11
+   |
+LL |     arr = bind(arr);
+   |           ^^^^^^^^^ encountered a self-referencing constant
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.

From fe396531167cc60605db6c51bff169b3e2fd5d55 Mon Sep 17 00:00:00 2001
From: kadmin <julianknodt@gmail.com>
Date: Sun, 27 Dec 2020 02:30:06 +0000
Subject: [PATCH 03/24] Check that value is explicitly none

---
 .../src/transform/check_consts/qualifs.rs     |  3 +--
 src/test/ui/issues/issue-80371.rs             | 15 ++++++++++++
 src/test/ui/issues/issue-80371.stderr         | 23 +++++++++++++++++++
 3 files changed, 39 insertions(+), 2 deletions(-)
 create mode 100644 src/test/ui/issues/issue-80371.rs
 create mode 100644 src/test/ui/issues/issue-80371.stderr

diff --git a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs
index 0ce1980f10a11..4d159ed340380 100644
--- a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs
@@ -246,8 +246,7 @@ where
     };
 
     // Check the qualifs of the value of `const` items.
-    if let ty::ConstKind::Unevaluated(def, _, promoted) = constant.literal.val {
-        assert!(promoted.is_none());
+    if let ty::ConstKind::Unevaluated(def, _, None) = constant.literal.val {
         // Don't peek inside trait associated constants.
         if cx.tcx.trait_of_item(def.did).is_none() {
             let qualifs = if let Some((did, param_did)) = def.as_const_arg() {
diff --git a/src/test/ui/issues/issue-80371.rs b/src/test/ui/issues/issue-80371.rs
new file mode 100644
index 0000000000000..25463ea5ee841
--- /dev/null
+++ b/src/test/ui/issues/issue-80371.rs
@@ -0,0 +1,15 @@
+#![crate_type = "lib"]
+
+pub struct Header<'a> {
+    pub value: &'a [u8],
+}
+
+pub fn test() {
+    let headers = [Header{value: &[]}; 128];
+    //~^ ERROR the trait bound
+}
+
+pub fn test2() {
+    let headers = [Header{value: &[0]}; 128];
+    //~^ ERROR the trait bound
+}
diff --git a/src/test/ui/issues/issue-80371.stderr b/src/test/ui/issues/issue-80371.stderr
new file mode 100644
index 0000000000000..5e2052abba7c6
--- /dev/null
+++ b/src/test/ui/issues/issue-80371.stderr
@@ -0,0 +1,23 @@
+error[E0277]: the trait bound `Header<'_>: Copy` is not satisfied
+  --> $DIR/issue-80371.rs:8:19
+   |
+LL |     let headers = [Header{value: &[]}; 128];
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>`
+   |
+   = note: the `Copy` trait is required because the repeated element will be copied
+   = note: this array initializer can be evaluated at compile-time, see issue #49147 <https://github.com/rust-lang/rust/issues/49147> for more information
+   = help: add `#![feature(const_in_array_repeat_expressions)]` to the crate attributes to enable
+
+error[E0277]: the trait bound `Header<'_>: Copy` is not satisfied
+  --> $DIR/issue-80371.rs:13:19
+   |
+LL |     let headers = [Header{value: &[0]}; 128];
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>`
+   |
+   = note: the `Copy` trait is required because the repeated element will be copied
+   = note: this array initializer can be evaluated at compile-time, see issue #49147 <https://github.com/rust-lang/rust/issues/49147> for more information
+   = help: add `#![feature(const_in_array_repeat_expressions)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.

From d1727edd73ce28e8b7676adbc1544b71627ad597 Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Mon, 28 Dec 2020 06:42:21 -0500
Subject: [PATCH 04/24] Add lint for 2229 migrations

---
 compiler/rustc_lint_defs/src/builtin.rs | 46 +++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index a8bf1ce51bb74..f1e5a4d47c787 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -2968,6 +2968,7 @@ declare_lint_pass! {
         UNSUPPORTED_NAKED_FUNCTIONS,
         MISSING_ABI,
         SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
+        DISJOINT_CAPTURE_DROP_REORDER,
     ]
 }
 
@@ -2994,6 +2995,51 @@ declare_lint! {
     "detects doc comments that aren't used by rustdoc"
 }
 
+declare_lint! {
+    /// The `disjoint_capture_drop_reorder` lint detects variables that aren't completely
+    /// captured when the feature `capture_disjoint_fields` is enabled and it affects the Drop
+    /// order of at least one path starting at this variable.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// # #![deny(disjoint_capture_drop_reorder)]
+    /// # #![allow(unused)]
+    /// struct FancyInteger(i32);
+    ///
+    /// impl Drop for FancyInteger {
+    ///     fn drop(&mut self) {
+    ///         println!("Just dropped {}", self.0);
+    ///     }
+    /// }
+    ///
+    /// struct Point { x: FancyInteger, y: FancyInteger }
+    ///
+    /// fn main() {
+    ///   let p = Point { x: FancyInteger(10), y: FancyInteger(20) };
+    ///
+    ///   let c = || {
+    ///      let x = p.x;
+    ///   };
+    ///
+    ///   c();
+    ///
+    ///   // ... More code ...
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// In the above example `p.y` will be dropped at the end of `f` instead of with `c` if
+    /// the feature `capture_disjoint_fields` is enabled.
+    pub DISJOINT_CAPTURE_DROP_REORDER,
+    Allow,
+    "Drop reorder because of `capture_disjoint_fields`"
+
+}
+
 declare_lint_pass!(UnusedDocComment => [UNUSED_DOC_COMMENTS]);
 
 declare_lint! {

From 2c651eb75e5ebdef115f0dce3afb1a81a5a82e73 Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Sat, 21 Nov 2020 04:23:42 -0500
Subject: [PATCH 05/24] Process mentioned upvars for analysis first pass after
 ExprUseVisitor

- This allows us add fake information after handling migrations if
  needed.
- Capture analysis also priortizes what we see earlier, which means
  fake information should go in last.
---
 compiler/rustc_typeck/src/check/upvar.rs      | 87 +++++++++++--------
 .../escape-upvar-nested.stderr                |  8 +-
 .../escape-upvar-ref.stderr                   |  4 +-
 3 files changed, 58 insertions(+), 41 deletions(-)

diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index 6b2cba62fa6b7..ffdd67771a3f6 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -55,6 +55,11 @@ enum PlaceAncestryRelation {
     Divergent,
 }
 
+/// Intermediate format to store a captured `Place` and associated `ty::CaptureInfo`
+/// during capture analysis. Information in this map feeds into the minimum capture
+/// analysis pass.
+type InferredCaptureInformation<'tcx> = FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>;
+
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) {
         InferBorrowKindVisitor { fcx: self }.visit_body(body);
@@ -124,28 +129,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let local_def_id = closure_def_id.expect_local();
 
-        let mut capture_information: FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>> =
-            Default::default();
-        if !self.tcx.features().capture_disjoint_fields {
-            if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
-                for (&var_hir_id, _) in upvars.iter() {
-                    let place = self.place_for_root_variable(local_def_id, var_hir_id);
-
-                    debug!("seed place {:?}", place);
-
-                    let upvar_id = ty::UpvarId::new(var_hir_id, local_def_id);
-                    let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span);
-                    let info = ty::CaptureInfo {
-                        capture_kind_expr_id: None,
-                        path_expr_id: None,
-                        capture_kind,
-                    };
-
-                    capture_information.insert(place, info);
-                }
-            }
-        }
-
         let body_owner_def_id = self.tcx.hir().body_owner_def_id(body.id());
         assert_eq!(body_owner_def_id.to_def_id(), closure_def_id);
         let mut delegate = InferBorrowKind {
@@ -155,7 +138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             capture_clause,
             current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM,
             current_origin: None,
-            capture_information,
+            capture_information: Default::default(),
         };
         euv::ExprUseVisitor::new(
             &mut delegate,
@@ -172,6 +155,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
         self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span);
 
+        self.compute_min_captures(closure_def_id, delegate.capture_information);
+
+        // We now fake capture information for all variables that are mentioned within the closure
+        // We do this after handling migrations so that min_captures computes before
+        if !self.tcx.features().capture_disjoint_fields {
+            let mut capture_information: InferredCaptureInformation<'tcx> = Default::default();
+
+            if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
+                for var_hir_id in upvars.keys() {
+                    let place = self.place_for_root_variable(local_def_id, *var_hir_id);
+
+                    debug!("seed place {:?}", place);
+
+                    let upvar_id = ty::UpvarId::new(*var_hir_id, local_def_id);
+                    let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span);
+                    let fake_info = ty::CaptureInfo {
+                        capture_kind_expr_id: None,
+                        path_expr_id: None,
+                        capture_kind,
+                    };
+
+                    capture_information.insert(place, fake_info);
+                }
+            }
+
+            // This will update the min captures based on this new fake information.
+            self.compute_min_captures(closure_def_id, capture_information);
+        }
+
         if let Some(closure_substs) = infer_kind {
             // Unify the (as yet unbound) type variable in the closure
             // substs with the kind we inferred.
@@ -198,7 +210,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
 
-        self.compute_min_captures(closure_def_id, delegate);
         self.log_closure_min_capture_info(closure_def_id, span);
 
         self.min_captures_to_closure_captures_bridge(closure_def_id);
@@ -345,6 +356,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Places (and corresponding capture kind) that we need to keep track of to support all
     /// the required captured paths.
     ///
+    ///
+    /// Note: If this function is called multiple times for the same closure, it will update
+    ///       the existing min_capture map that is stored in TypeckResults.
+    ///
     /// Eg:
     /// ```rust,no_run
     /// struct Point { x: i32, y: i32 }
@@ -409,11 +424,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn compute_min_captures(
         &self,
         closure_def_id: DefId,
-        inferred_info: InferBorrowKind<'_, 'tcx>,
+        capture_information: InferredCaptureInformation<'tcx>,
     ) {
-        let mut root_var_min_capture_list: ty::RootVariableMinCaptureList<'_> = Default::default();
+        if capture_information.is_empty() {
+            return;
+        }
 
-        for (place, capture_info) in inferred_info.capture_information.into_iter() {
+        let mut typeck_results = self.typeck_results.borrow_mut();
+
+        let root_var_min_capture_list =
+            typeck_results.closure_min_captures.entry(closure_def_id).or_insert(Default::default());
+
+        for (place, capture_info) in capture_information.into_iter() {
             let var_hir_id = match place.base {
                 PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
                 base => bug!("Expected upvar, found={:?}", base),
@@ -501,13 +523,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list);
-
-        if !root_var_min_capture_list.is_empty() {
-            self.typeck_results
-                .borrow_mut()
-                .closure_min_captures
-                .insert(closure_def_id, root_var_min_capture_list);
-        }
     }
 
     fn init_capture_kind(
@@ -661,9 +676,11 @@ struct InferBorrowKind<'a, 'tcx> {
     ///
     /// For closure `fix_s`, (at a high level) the map contains
     ///
+    /// ```
     /// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow }
     /// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow }
-    capture_information: FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>,
+    /// ```
+    capture_information: InferredCaptureInformation<'tcx>,
 }
 
 impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr
index 1a8258376142a..e1b446fc61f61 100644
--- a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr
+++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr
@@ -7,10 +7,10 @@ LL |             let mut closure1 = || p = &y;
    = note: defining type: test::{closure#0}::{closure#0} with closure substs [
                i16,
                extern "rust-call" fn(()),
-               (&'_#1r i32, &'_#2r mut &'_#3r i32),
+               (&'_#1r mut &'_#2r i32, &'_#3r i32),
            ]
    = note: number of external vids: 4
-   = note: where '_#1r: '_#3r
+   = note: where '_#3r: '_#2r
 
 note: external requirements
   --> $DIR/escape-upvar-nested.rs:20:27
@@ -25,10 +25,10 @@ LL | |         };
    = note: defining type: test::{closure#0} with closure substs [
                i16,
                extern "rust-call" fn(()),
-               (&'_#1r i32, &'_#2r mut &'_#3r i32),
+               (&'_#1r mut &'_#2r i32, &'_#3r i32),
            ]
    = note: number of external vids: 4
-   = note: where '_#1r: '_#3r
+   = note: where '_#3r: '_#2r
 
 note: no external requirements
   --> $DIR/escape-upvar-nested.rs:13:1
diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr
index 29fd796882b6a..0ea1076c32ef4 100644
--- a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr
+++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr
@@ -7,10 +7,10 @@ LL |         let mut closure = || p = &y;
    = note: defining type: test::{closure#0} with closure substs [
                i16,
                extern "rust-call" fn(()),
-               (&'_#1r i32, &'_#2r mut &'_#3r i32),
+               (&'_#1r mut &'_#2r i32, &'_#3r i32),
            ]
    = note: number of external vids: 4
-   = note: where '_#1r: '_#3r
+   = note: where '_#3r: '_#2r
 
 note: no external requirements
   --> $DIR/escape-upvar-ref.rs:17:1

From 36352d23e64bf178e8ab8ac88a27702124334cca Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Tue, 15 Dec 2020 03:38:15 -0500
Subject: [PATCH 06/24] Migrations first pass

---
 compiler/rustc_typeck/src/check/upvar.rs     | 132 ++++++++++++++++++-
 compiler/rustc_typeck/src/check/writeback.rs |   6 +-
 2 files changed, 134 insertions(+), 4 deletions(-)

diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index ffdd67771a3f6..2a323e2160639 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -30,6 +30,7 @@
 //! then mean that all later passes would have to check for these figments
 //! and report an error, and it just seems like more mess in the end.)
 
+use super::writeback::Resolver;
 use super::FnCtxt;
 
 use crate::expr_use_visitor as euv;
@@ -40,7 +41,9 @@ use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_infer::infer::UpvarRegion;
 use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, ProjectionKind};
+use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts};
+use rustc_session::lint;
 use rustc_span::sym;
 use rustc_span::{MultiSpan, Span, Symbol};
 
@@ -97,7 +100,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         closure_hir_id: hir::HirId,
         span: Span,
-        body: &hir::Body<'_>,
+        body: &'tcx hir::Body<'tcx>,
         capture_clause: hir::CaptureBy,
     ) {
         debug!("analyze_closure(id={:?}, body.id={:?})", closure_hir_id, body.id());
@@ -157,6 +160,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         self.compute_min_captures(closure_def_id, delegate.capture_information);
 
+        let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
+        if should_do_migration_analysis(self.tcx, closure_hir_id) {
+            let need_migrations = self.compute_2229_migrations_first_pass(
+                closure_def_id,
+                span,
+                capture_clause,
+                body,
+                self.typeck_results.borrow().closure_min_captures.get(&closure_def_id),
+            );
+
+            if !need_migrations.is_empty() {
+                let need_migrations_hir_id =
+                    need_migrations.iter().map(|m| m.0).collect::<Vec<_>>();
+
+                let migrations_text =
+                    migration_suggestion_for_2229(self.tcx, &need_migrations_hir_id);
+
+                self.tcx.struct_span_lint_hir(
+                    lint::builtin::DISJOINT_CAPTURE_DROP_REORDER,
+                    closure_hir_id,
+                    span,
+                    |lint| {
+                        let mut diagnostics_builder = lint.build(
+                            "Drop order affected for closure because of `capture_disjoint_fields`",
+                        );
+                        diagnostics_builder.note(&migrations_text);
+                        diagnostics_builder.emit();
+                    },
+                );
+            }
+        }
+
         // We now fake capture information for all variables that are mentioned within the closure
         // We do this after handling migrations so that min_captures computes before
         if !self.tcx.features().capture_disjoint_fields {
@@ -525,6 +560,86 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list);
     }
 
+    /// Figures out the list of root variables (and their types) that aren't completely
+    /// captured by the closure when `capture_disjoint_fields` is enabled and drop order of
+    /// some path starting at that root variable **might** be affected.
+    ///
+    /// The output list would include a root variable if:
+    /// - It would have been moved into the closure when `capture_disjoint_fields` wasn't
+    ///   enabled, **and**
+    /// - It wasn't completely captured by the closure, **and**
+    /// - The type of the root variable needs Drop.
+    fn compute_2229_migrations_first_pass(
+        &self,
+        closure_def_id: DefId,
+        closure_span: Span,
+        closure_clause: hir::CaptureBy,
+        body: &'tcx hir::Body<'tcx>,
+        min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>,
+    ) -> Vec<(hir::HirId, Ty<'tcx>)> {
+        fn resolve_ty<T: TypeFoldable<'tcx>>(
+            fcx: &FnCtxt<'_, 'tcx>,
+            span: Span,
+            body: &'tcx hir::Body<'tcx>,
+            ty: T,
+        ) -> T {
+            let mut resolver = Resolver::new(fcx, &span, body);
+            ty.fold_with(&mut resolver)
+        }
+
+        let upvars = if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
+            upvars
+        } else {
+            return vec![];
+        };
+
+        let mut need_migrations = Vec::new();
+
+        for (&var_hir_id, _) in upvars.iter() {
+            let ty = resolve_ty(self, closure_span, body, self.node_ty(var_hir_id));
+
+            if !ty.needs_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) {
+                continue;
+            }
+
+            let root_var_min_capture_list = if let Some(root_var_min_capture_list) =
+                min_captures.and_then(|m| m.get(&var_hir_id))
+            {
+                root_var_min_capture_list
+            } else {
+                // The upvar is mentioned within the closure but no path starting from it is
+                // used.
+
+                match closure_clause {
+                    // Only migrate if closure is a move closure
+                    hir::CaptureBy::Value => need_migrations.push((var_hir_id, ty)),
+
+                    hir::CaptureBy::Ref => {}
+                }
+
+                continue;
+            };
+
+            let is_moved = root_var_min_capture_list
+                .iter()
+                .find(|capture| matches!(capture.info.capture_kind, ty::UpvarCapture::ByValue(_)))
+                .is_some();
+
+            // 1. If we capture more than one path starting at the root variabe then the root variable
+            //    isn't being captured in its entirety
+            // 2. If we only capture one path starting at the root variable, it's still possible
+            //    that it isn't the root variable completely.
+            if is_moved
+                && ((root_var_min_capture_list.len() > 1)
+                    || (root_var_min_capture_list[0].place.projections.len() > 0))
+            {
+                need_migrations.push((var_hir_id, ty));
+            }
+        }
+
+        need_migrations
+    }
+
     fn init_capture_kind(
         &self,
         capture_clause: hir::CaptureBy,
@@ -1039,6 +1154,21 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
     tcx.hir().name(var_hir_id)
 }
 
+fn should_do_migration_analysis(tcx: TyCtxt<'_>, closure_id: hir::HirId) -> bool {
+    let (level, _) =
+        tcx.lint_level_at_node(lint::builtin::DISJOINT_CAPTURE_DROP_REORDER, closure_id);
+
+    !matches!(level, lint::Level::Allow)
+}
+
+fn migration_suggestion_for_2229(tcx: TyCtxt<'_>, need_migrations: &Vec<hir::HirId>) -> String {
+    let need_migrations_strings =
+        need_migrations.iter().map(|v| format!("{}", var_name(tcx, *v))).collect::<Vec<_>>();
+    let migrations_list_concat = need_migrations_strings.join(", ");
+
+    format!("let ({}) = ({});", migrations_list_concat, migrations_list_concat)
+}
+
 /// Helper function to determine if we need to escalate CaptureKind from
 /// CaptureInfo A to B and returns the escalated CaptureInfo.
 /// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)
diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs
index b6d740a4fdb57..4d18b2cb3fc49 100644
--- a/compiler/rustc_typeck/src/check/writeback.rs
+++ b/compiler/rustc_typeck/src/check/writeback.rs
@@ -650,7 +650,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
     }
 }
 
-trait Locatable {
+crate trait Locatable {
     fn to_span(&self, tcx: TyCtxt<'_>) -> Span;
 }
 
@@ -668,7 +668,7 @@ impl Locatable for hir::HirId {
 
 /// The Resolver. This is the type folding engine that detects
 /// unresolved types and so forth.
-struct Resolver<'cx, 'tcx> {
+crate struct Resolver<'cx, 'tcx> {
     tcx: TyCtxt<'tcx>,
     infcx: &'cx InferCtxt<'cx, 'tcx>,
     span: &'cx dyn Locatable,
@@ -679,7 +679,7 @@ struct Resolver<'cx, 'tcx> {
 }
 
 impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
-    fn new(
+    crate fn new(
         fcx: &'cx FnCtxt<'cx, 'tcx>,
         span: &'cx dyn Locatable,
         body: &'tcx hir::Body<'tcx>,

From d30a5bf3282486f0e10c91c44dbb2de61383164c Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Tue, 29 Dec 2020 23:43:44 -0500
Subject: [PATCH 07/24] Tests for 2229 lint

---
 .../migrations/insignificant_drop.rs          | 130 +++++++++++++++++
 .../migrations/insignificant_drop.stderr      | 105 ++++++++++++++
 .../migrations/no_migrations.rs               |  84 +++++++++++
 .../migrations/significant_drop.rs            | 137 ++++++++++++++++++
 .../migrations/significant_drop.stderr        | 103 +++++++++++++
 5 files changed, 559 insertions(+)
 create mode 100644 src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs
 create mode 100644 src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr
 create mode 100644 src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs
 create mode 100644 src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs
 create mode 100644 src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr

diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs
new file mode 100644
index 0000000000000..591bcab60e5c4
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs
@@ -0,0 +1,130 @@
+#![deny(disjoint_capture_drop_reorder)]
+//~^ NOTE: the lint level is defined here
+
+// Test cases for types that implement a insignificant drop (stlib defined)
+
+// `t` needs Drop because one of its elements needs drop,
+// therefore precise capture might affect drop ordering
+fn test1_all_need_migration() {
+    let t = (String::new(), String::new());
+    let t1 = (String::new(), String::new());
+    let t2 = (String::new(), String::new());
+
+    let c = || {
+    //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
+    //~| NOTE: let (t, t1, t2) = (t, t1, t2);
+        let _t = t.0;
+        let _t1 = t1.0;
+        let _t2 = t2.0;
+    };
+
+    c();
+}
+
+// String implements drop and therefore should be migrated.
+// But in this test cases, `t2` is completely captured and when it is dropped won't be affected
+fn test2_only_precise_paths_need_migration() {
+    let t = (String::new(), String::new());
+    let t1 = (String::new(), String::new());
+    let t2 = (String::new(), String::new());
+
+    let c = || {
+    //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
+    //~| NOTE: let (t, t1) = (t, t1);
+        let _t = t.0;
+        let _t1 = t1.0;
+        let _t2 = t2;
+    };
+
+    c();
+}
+
+// If a variable would've not been captured by value then it would've not been
+// dropped with the closure and therefore doesn't need migration.
+fn test3_only_by_value_need_migration() {
+    let t = (String::new(), String::new());
+    let t1 = (String::new(), String::new());
+    let c = || {
+    //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
+    //~| NOTE: let (t) = (t);
+        let _t = t.0;
+        println!("{}", t1.1);
+    };
+
+    c();
+}
+
+// Copy types get copied into the closure instead of move. Therefore we don't need to
+// migrate then as their drop order isn't tied to the closure.
+fn test4_only_non_copy_types_need_migration() {
+    let t = (String::new(), String::new());
+
+    // `t1` is Copy because all of its elements are Copy
+    let t1 = (0i32, 0i32);
+
+    let c = || {
+    //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
+    //~| NOTE: let (t) = (t);
+        let _t = t.0;
+        let _t1 = t1.0;
+    };
+
+    c();
+}
+
+fn test5_only_drop_types_need_migration() {
+    struct S(i32, i32);
+
+    let t = (String::new(), String::new());
+
+    // `s` doesn't implement Drop or any elements within it, and doesn't need migration
+    let s = S(0i32, 0i32);
+
+    let c = || {
+    //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
+    //~| NOTE: let (t) = (t);
+        let _t = t.0;
+        let _s = s.0;
+    };
+
+    c();
+}
+
+// Since we are using a move closure here, both `t` and `t1` get moved
+// even though they are being used by ref inside the closure.
+fn test6_move_closures_non_copy_types_might_need_migration() {
+    let t = (String::new(), String::new());
+    let t1 = (String::new(), String::new());
+    let c = move || {
+    //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
+    //~| NOTE: let (t1, t) = (t1, t);
+        println!("{} {}", t1.1, t.1);
+    };
+
+    c();
+}
+
+// Test migration analysis in case of Drop + Non Drop aggregates.
+// Note we need migration here only because the non-copy (because Drop type) is captured,
+// otherwise we won't need to, since we can get away with just by ref capture in that case.
+fn test7_drop_non_drop_aggregate_need_migration() {
+    let t = (String::new(), 0i32);
+
+    let c = || {
+    //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
+    //~| NOTE: let (t) = (t);
+        let _t = t.0;
+    };
+
+    c();
+}
+
+fn main() {
+    test1_all_need_migration();
+    test2_only_precise_paths_need_migration();
+    test3_only_by_value_need_migration();
+    test4_only_non_copy_types_need_migration();
+    test5_only_drop_types_need_migration();
+    test6_move_closures_non_copy_types_might_need_migration();
+    test7_drop_non_drop_aggregate_need_migration();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr
new file mode 100644
index 0000000000000..bf80d72aa0b1f
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr
@@ -0,0 +1,105 @@
+error: Drop order affected for closure because of `capture_disjoint_fields`
+  --> $DIR/insignificant_drop.rs:13:13
+   |
+LL |       let c = || {
+   |  _____________^
+LL | |
+LL | |
+LL | |         let _t = t.0;
+LL | |         let _t1 = t1.0;
+LL | |         let _t2 = t2.0;
+LL | |     };
+   | |_____^
+   |
+note: the lint level is defined here
+  --> $DIR/insignificant_drop.rs:1:9
+   |
+LL | #![deny(disjoint_capture_drop_reorder)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: let (t, t1, t2) = (t, t1, t2);
+
+error: Drop order affected for closure because of `capture_disjoint_fields`
+  --> $DIR/insignificant_drop.rs:31:13
+   |
+LL |       let c = || {
+   |  _____________^
+LL | |
+LL | |
+LL | |         let _t = t.0;
+LL | |         let _t1 = t1.0;
+LL | |         let _t2 = t2;
+LL | |     };
+   | |_____^
+   |
+   = note: let (t, t1) = (t, t1);
+
+error: Drop order affected for closure because of `capture_disjoint_fields`
+  --> $DIR/insignificant_drop.rs:47:13
+   |
+LL |       let c = || {
+   |  _____________^
+LL | |
+LL | |
+LL | |         let _t = t.0;
+LL | |         println!("{}", t1.1);
+LL | |     };
+   | |_____^
+   |
+   = note: let (t) = (t);
+
+error: Drop order affected for closure because of `capture_disjoint_fields`
+  --> $DIR/insignificant_drop.rs:65:13
+   |
+LL |       let c = || {
+   |  _____________^
+LL | |
+LL | |
+LL | |         let _t = t.0;
+LL | |         let _t1 = t1.0;
+LL | |     };
+   | |_____^
+   |
+   = note: let (t) = (t);
+
+error: Drop order affected for closure because of `capture_disjoint_fields`
+  --> $DIR/insignificant_drop.rs:83:13
+   |
+LL |       let c = || {
+   |  _____________^
+LL | |
+LL | |
+LL | |         let _t = t.0;
+LL | |         let _s = s.0;
+LL | |     };
+   | |_____^
+   |
+   = note: let (t) = (t);
+
+error: Drop order affected for closure because of `capture_disjoint_fields`
+  --> $DIR/insignificant_drop.rs:98:13
+   |
+LL |       let c = move || {
+   |  _____________^
+LL | |
+LL | |
+LL | |         println!("{} {}", t1.1, t.1);
+LL | |     };
+   | |_____^
+   |
+   = note: let (t1, t) = (t1, t);
+
+error: Drop order affected for closure because of `capture_disjoint_fields`
+  --> $DIR/insignificant_drop.rs:113:13
+   |
+LL |       let c = || {
+   |  _____________^
+LL | |
+LL | |
+LL | |         let _t = t.0;
+LL | |     };
+   | |_____^
+   |
+   = note: let (t) = (t);
+
+error: aborting due to 7 previous errors
+
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs b/src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs
new file mode 100644
index 0000000000000..73592ce04c28f
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs
@@ -0,0 +1,84 @@
+// run-pass
+
+// Set of test cases that don't need migrations
+
+#![deny(disjoint_capture_drop_reorder)]
+
+
+// Copy types as copied by the closure instead of being moved into the closure
+// Therefore their drop order isn't tied to the closure and won't be requiring any
+// migrations.
+fn test1_only_copy_types() {
+    let t = (0i32, 0i32);
+
+    let c = || {
+        let _t = t.0;
+    };
+
+    c();
+}
+
+// Same as test1 but using a move closure
+fn test2_only_copy_types_move_closure() {
+    let t = (0i32, 0i32);
+
+    let c = move || {
+        println!("{}", t.0);
+    };
+
+    c();
+}
+
+// Don't need to migrate if captured by ref
+fn test3_only_copy_types_move_closure() {
+    let t = (String::new(), String::new());
+
+    let c = || {
+        println!("{}", t.0);
+    };
+
+    c();
+}
+
+// Test migration analysis in case of Insignificant Drop + Non Drop aggregates.
+// Note in this test the closure captures a non Drop type and therefore the variable
+// is only captured by ref.
+fn test4_insignificant_drop_non_drop_aggregate() {
+    let t = (String::new(), 0i32);
+
+    let c = || {
+        let _t = t.1;
+    };
+
+    c();
+}
+
+
+struct Foo(i32);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+// Test migration analysis in case of Significant Drop + Non Drop aggregates.
+// Note in this test the closure captures a non Drop type and therefore the variable
+// is only captured by ref.
+fn test5_significant_drop_non_drop_aggregate() {
+    let t = (Foo(0), 0i32);
+
+    let c = || {
+        let _t = t.1;
+    };
+
+    c();
+}
+
+fn main() {
+    test1_only_copy_types();
+    test2_only_copy_types_move_closure();
+    test3_only_copy_types_move_closure();
+    test4_insignificant_drop_non_drop_aggregate();
+    test5_significant_drop_non_drop_aggregate();
+
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs
new file mode 100644
index 0000000000000..47dd349991fcd
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs
@@ -0,0 +1,137 @@
+#![deny(disjoint_capture_drop_reorder)]
+//~^ NOTE: the lint level is defined here
+
+// Test cases for types that implement a significant drop (user defined)
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+#[derive(Debug)]
+struct ConstainsDropField(Foo, Foo);
+
+// `t` needs Drop because one of its elements needs drop,
+// therefore precise capture might affect drop ordering
+fn test1_all_need_migration() {
+    let t = (Foo(0), Foo(0));
+    let t1 = (Foo(0), Foo(0));
+    let t2 = (Foo(0), Foo(0));
+
+    let c = || {
+    //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
+    //~| NOTE: let (t, t1, t2) = (t, t1, t2);
+        let _t = t.0;
+        let _t1 = t1.0;
+        let _t2 = t2.0;
+    };
+
+    c();
+}
+
+// String implements drop and therefore should be migrated.
+// But in this test cases, `t2` is completely captured and when it is dropped won't be affected
+fn test2_only_precise_paths_need_migration() {
+    let t = (Foo(0), Foo(0));
+    let t1 = (Foo(0), Foo(0));
+    let t2 = (Foo(0), Foo(0));
+
+    let c = || {
+    //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
+    //~| NOTE: let (t, t1) = (t, t1);
+        let _t = t.0;
+        let _t1 = t1.0;
+        let _t2 = t2;
+    };
+
+    c();
+}
+
+// If a variable would've not been captured by value then it would've not been
+// dropped with the closure and therefore doesn't need migration.
+fn test3_only_by_value_need_migration() {
+    let t = (Foo(0), Foo(0));
+    let t1 = (Foo(0), Foo(0));
+    let c = || {
+    //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
+    //~| NOTE: let (t) = (t);
+        let _t = t.0;
+        println!("{:?}", t1.1);
+    };
+
+    c();
+}
+
+// The root variable might not implement drop themselves but some path starting
+// at the root variable might implement Drop.
+//
+// If this path isn't captured we need to migrate for the root variable.
+fn test4_type_contains_drop_need_migration() {
+    let t = ConstainsDropField(Foo(0), Foo(0));
+
+    let c = || {
+    //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
+    //~| NOTE: let (t) = (t);
+        let _t = t.0;
+    };
+
+    c();
+}
+
+// Test migration analysis in case of Drop + Non Drop aggregates.
+// Note we need migration here only because the non-copy (because Drop type) is captured,
+// otherwise we won't need to, since we can get away with just by ref capture in that case.
+fn test5_drop_non_drop_aggregate_need_migration() {
+    let t = (Foo(0), 0i32);
+
+    let c = || {
+    //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
+    //~| NOTE: let (t) = (t);
+        let _t = t.0;
+    };
+
+    c();
+}
+
+// Test migration analysis in case of Significant and Insignificant Drop aggregates.
+fn test6_significant_insignificant_drop_aggregate_need_migration() {
+    struct S(i32, i32);
+
+    let t = (Foo(0), String::new());
+
+    let c = || {
+    //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
+    //~| NOTE: let (t) = (t);
+        let _t = t.1;
+    };
+
+    c();
+}
+
+// Since we are using a move closure here, both `t` and `t1` get moved
+// even though they are being used by ref inside the closure.
+fn test7_move_closures_non_copy_types_might_need_migration() {
+    let t = (Foo(0), Foo(0));
+    let t1 = (Foo(0), Foo(0), Foo(0));
+
+    let c = move || {
+    //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
+    //~| NOTE: let (t1, t) = (t1, t);
+        println!("{:?} {:?}", t1.1, t.1);
+    };
+
+    c();
+}
+
+fn main() {
+    test1_all_need_migration();
+    test2_only_precise_paths_need_migration();
+    test3_only_by_value_need_migration();
+    test4_type_contains_drop_need_migration();
+    test5_drop_non_drop_aggregate_need_migration();
+    test6_significant_insignificant_drop_aggregate_need_migration();
+    test7_move_closures_non_copy_types_might_need_migration();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr
new file mode 100644
index 0000000000000..a2308e1ef9451
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr
@@ -0,0 +1,103 @@
+error: Drop order affected for closure because of `capture_disjoint_fields`
+  --> $DIR/significant_drop.rs:24:13
+   |
+LL |       let c = || {
+   |  _____________^
+LL | |
+LL | |
+LL | |         let _t = t.0;
+LL | |         let _t1 = t1.0;
+LL | |         let _t2 = t2.0;
+LL | |     };
+   | |_____^
+   |
+note: the lint level is defined here
+  --> $DIR/significant_drop.rs:1:9
+   |
+LL | #![deny(disjoint_capture_drop_reorder)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: let (t, t1, t2) = (t, t1, t2);
+
+error: Drop order affected for closure because of `capture_disjoint_fields`
+  --> $DIR/significant_drop.rs:42:13
+   |
+LL |       let c = || {
+   |  _____________^
+LL | |
+LL | |
+LL | |         let _t = t.0;
+LL | |         let _t1 = t1.0;
+LL | |         let _t2 = t2;
+LL | |     };
+   | |_____^
+   |
+   = note: let (t, t1) = (t, t1);
+
+error: Drop order affected for closure because of `capture_disjoint_fields`
+  --> $DIR/significant_drop.rs:58:13
+   |
+LL |       let c = || {
+   |  _____________^
+LL | |
+LL | |
+LL | |         let _t = t.0;
+LL | |         println!("{:?}", t1.1);
+LL | |     };
+   | |_____^
+   |
+   = note: let (t) = (t);
+
+error: Drop order affected for closure because of `capture_disjoint_fields`
+  --> $DIR/significant_drop.rs:75:13
+   |
+LL |       let c = || {
+   |  _____________^
+LL | |
+LL | |
+LL | |         let _t = t.0;
+LL | |     };
+   | |_____^
+   |
+   = note: let (t) = (t);
+
+error: Drop order affected for closure because of `capture_disjoint_fields`
+  --> $DIR/significant_drop.rs:90:13
+   |
+LL |       let c = || {
+   |  _____________^
+LL | |
+LL | |
+LL | |         let _t = t.0;
+LL | |     };
+   | |_____^
+   |
+   = note: let (t) = (t);
+
+error: Drop order affected for closure because of `capture_disjoint_fields`
+  --> $DIR/significant_drop.rs:105:13
+   |
+LL |       let c = || {
+   |  _____________^
+LL | |
+LL | |
+LL | |         let _t = t.1;
+LL | |     };
+   | |_____^
+   |
+   = note: let (t) = (t);
+
+error: Drop order affected for closure because of `capture_disjoint_fields`
+  --> $DIR/significant_drop.rs:120:13
+   |
+LL |       let c = move || {
+   |  _____________^
+LL | |
+LL | |
+LL | |         println!("{:?} {:?}", t1.1, t.1);
+LL | |     };
+   | |_____^
+   |
+   = note: let (t1, t) = (t1, t);
+
+error: aborting due to 7 previous errors
+

From 9ac023db3912690d79d723c98429db61c8c42212 Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Sat, 2 Jan 2021 21:28:28 -0500
Subject: [PATCH 08/24] Mark the lint doc as compile_fail

---
 compiler/rustc_lint_defs/src/builtin.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index f1e5a4d47c787..199be00990761 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -3002,7 +3002,7 @@ declare_lint! {
     ///
     /// ### Example
     ///
-    /// ```rust
+    /// ```rust,compile_fail
     /// # #![deny(disjoint_capture_drop_reorder)]
     /// # #![allow(unused)]
     /// struct FancyInteger(i32);

From f106e18cfabc9a07e9b696bcac80af15d32555de Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Tue, 19 Jan 2021 03:15:36 -0500
Subject: [PATCH 09/24] PR fixup

---
 compiler/rustc_typeck/src/check/upvar.rs      | 85 ++++++++++---------
 .../migrations/insignificant_drop.rs          |  2 +-
 .../migrations/significant_drop.rs            |  2 +-
 3 files changed, 48 insertions(+), 41 deletions(-)

diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index 2a323e2160639..fff1323ddcb75 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -162,34 +162,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
         if should_do_migration_analysis(self.tcx, closure_hir_id) {
-            let need_migrations = self.compute_2229_migrations_first_pass(
-                closure_def_id,
-                span,
-                capture_clause,
-                body,
-                self.typeck_results.borrow().closure_min_captures.get(&closure_def_id),
-            );
-
-            if !need_migrations.is_empty() {
-                let need_migrations_hir_id =
-                    need_migrations.iter().map(|m| m.0).collect::<Vec<_>>();
-
-                let migrations_text =
-                    migration_suggestion_for_2229(self.tcx, &need_migrations_hir_id);
-
-                self.tcx.struct_span_lint_hir(
-                    lint::builtin::DISJOINT_CAPTURE_DROP_REORDER,
-                    closure_hir_id,
-                    span,
-                    |lint| {
-                        let mut diagnostics_builder = lint.build(
-                            "Drop order affected for closure because of `capture_disjoint_fields`",
-                        );
-                        diagnostics_builder.note(&migrations_text);
-                        diagnostics_builder.emit();
-                    },
-                );
-            }
+            self.perform_2229_migration_anaysis(closure_def_id, capture_clause, span, body);
         }
 
         // We now fake capture information for all variables that are mentioned within the closure
@@ -560,6 +533,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list);
     }
 
+    /// Perform the migration analysis for RFC 2229, and emit lint
+    /// `disjoint_capture_drop_reorder` if needed.
+    fn perform_2229_migration_anaysis(
+        &self,
+        closure_def_id: DefId,
+        capture_clause: hir::CaptureBy,
+        span: Span,
+        body: &'tcx hir::Body<'tcx>,
+    ) {
+        let need_migrations = self.compute_2229_migrations_first_pass(
+            closure_def_id,
+            span,
+            capture_clause,
+            body,
+            self.typeck_results.borrow().closure_min_captures.get(&closure_def_id),
+        );
+
+        if !need_migrations.is_empty() {
+            let need_migrations_hir_id = need_migrations.iter().map(|m| m.0).collect::<Vec<_>>();
+
+            let migrations_text = migration_suggestion_for_2229(self.tcx, &need_migrations_hir_id);
+
+            let local_def_id = closure_def_id.expect_local();
+            let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
+            self.tcx.struct_span_lint_hir(
+                lint::builtin::DISJOINT_CAPTURE_DROP_REORDER,
+                closure_hir_id,
+                span,
+                |lint| {
+                    let mut diagnostics_builder = lint.build(
+                        "Drop order affected for closure because of `capture_disjoint_fields`",
+                    );
+                    diagnostics_builder.note(&migrations_text);
+                    diagnostics_builder.emit();
+                },
+            );
+        }
+    }
+
     /// Figures out the list of root variables (and their types) that aren't completely
     /// captured by the closure when `capture_disjoint_fields` is enabled and drop order of
     /// some path starting at that root variable **might** be affected.
@@ -622,17 +634,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             let is_moved = root_var_min_capture_list
                 .iter()
-                .find(|capture| matches!(capture.info.capture_kind, ty::UpvarCapture::ByValue(_)))
-                .is_some();
-
-            // 1. If we capture more than one path starting at the root variabe then the root variable
-            //    isn't being captured in its entirety
-            // 2. If we only capture one path starting at the root variable, it's still possible
-            //    that it isn't the root variable completely.
-            if is_moved
-                && ((root_var_min_capture_list.len() > 1)
-                    || (root_var_min_capture_list[0].place.projections.len() > 0))
-            {
+                .any(|capture| matches!(capture.info.capture_kind, ty::UpvarCapture::ByValue(_)));
+
+            let is_not_completely_captured =
+                root_var_min_capture_list.iter().any(|capture| capture.place.projections.len() > 0);
+
+            if is_moved && is_not_completely_captured {
                 need_migrations.push((var_hir_id, ty));
             }
         }
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs
index 591bcab60e5c4..298cc1217602e 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs
@@ -108,7 +108,7 @@ fn test6_move_closures_non_copy_types_might_need_migration() {
 // Note we need migration here only because the non-copy (because Drop type) is captured,
 // otherwise we won't need to, since we can get away with just by ref capture in that case.
 fn test7_drop_non_drop_aggregate_need_migration() {
-    let t = (String::new(), 0i32);
+    let t = (String::new(), String::new(), 0i32);
 
     let c = || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs
index 47dd349991fcd..40699f48fab92 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs
@@ -85,7 +85,7 @@ fn test4_type_contains_drop_need_migration() {
 // Note we need migration here only because the non-copy (because Drop type) is captured,
 // otherwise we won't need to, since we can get away with just by ref capture in that case.
 fn test5_drop_non_drop_aggregate_need_migration() {
-    let t = (Foo(0), 0i32);
+    let t = (Foo(0), Foo(0), 0i32);
 
     let c = || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`

From 11abaa13bb7b4cb684cd8eeda227313a2871891d Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Tue, 26 Jan 2021 04:08:23 -0500
Subject: [PATCH 10/24] New migration

---
 Cargo.lock                                         |  2 +-
 compiler/rustc_typeck/src/check/upvar.rs           |  2 +-
 .../migrations/insignificant_drop.rs               | 14 +++++++-------
 .../migrations/insignificant_drop.stderr           | 14 +++++++-------
 .../migrations/significant_drop.rs                 | 14 +++++++-------
 .../migrations/significant_drop.stderr             | 14 +++++++-------
 6 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index a4ba7704426e8..e7365e611cae7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5388,7 +5388,7 @@ dependencies = [
  "chrono",
  "lazy_static",
  "matchers",
- "parking_lot 0.11.0",
+ "parking_lot 0.9.0",
  "regex",
  "serde",
  "serde_json",
diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index fff1323ddcb75..2490836296ce4 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -1173,7 +1173,7 @@ fn migration_suggestion_for_2229(tcx: TyCtxt<'_>, need_migrations: &Vec<hir::Hir
         need_migrations.iter().map(|v| format!("{}", var_name(tcx, *v))).collect::<Vec<_>>();
     let migrations_list_concat = need_migrations_strings.join(", ");
 
-    format!("let ({}) = ({});", migrations_list_concat, migrations_list_concat)
+    format!("drop(&({}));", migrations_list_concat)
 }
 
 /// Helper function to determine if we need to escalate CaptureKind from
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs
index 298cc1217602e..cf25f3c3ba81e 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs
@@ -12,7 +12,7 @@ fn test1_all_need_migration() {
 
     let c = || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
-    //~| NOTE: let (t, t1, t2) = (t, t1, t2);
+    //~| NOTE: drop(&(t, t1, t2));
         let _t = t.0;
         let _t1 = t1.0;
         let _t2 = t2.0;
@@ -30,7 +30,7 @@ fn test2_only_precise_paths_need_migration() {
 
     let c = || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
-    //~| NOTE: let (t, t1) = (t, t1);
+    //~| NOTE: drop(&(t, t1));
         let _t = t.0;
         let _t1 = t1.0;
         let _t2 = t2;
@@ -46,7 +46,7 @@ fn test3_only_by_value_need_migration() {
     let t1 = (String::new(), String::new());
     let c = || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
-    //~| NOTE: let (t) = (t);
+    //~| NOTE: drop(&(t));
         let _t = t.0;
         println!("{}", t1.1);
     };
@@ -64,7 +64,7 @@ fn test4_only_non_copy_types_need_migration() {
 
     let c = || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
-    //~| NOTE: let (t) = (t);
+    //~| NOTE: drop(&(t));
         let _t = t.0;
         let _t1 = t1.0;
     };
@@ -82,7 +82,7 @@ fn test5_only_drop_types_need_migration() {
 
     let c = || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
-    //~| NOTE: let (t) = (t);
+    //~| NOTE: drop(&(t));
         let _t = t.0;
         let _s = s.0;
     };
@@ -97,7 +97,7 @@ fn test6_move_closures_non_copy_types_might_need_migration() {
     let t1 = (String::new(), String::new());
     let c = move || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
-    //~| NOTE: let (t1, t) = (t1, t);
+    //~| NOTE: drop(&(t1, t));
         println!("{} {}", t1.1, t.1);
     };
 
@@ -112,7 +112,7 @@ fn test7_drop_non_drop_aggregate_need_migration() {
 
     let c = || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
-    //~| NOTE: let (t) = (t);
+    //~| NOTE: drop(&(t));
         let _t = t.0;
     };
 
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr
index bf80d72aa0b1f..531759f61e01a 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr
@@ -16,7 +16,7 @@ note: the lint level is defined here
    |
 LL | #![deny(disjoint_capture_drop_reorder)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: let (t, t1, t2) = (t, t1, t2);
+   = note: drop(&(t, t1, t2));
 
 error: Drop order affected for closure because of `capture_disjoint_fields`
   --> $DIR/insignificant_drop.rs:31:13
@@ -31,7 +31,7 @@ LL | |         let _t2 = t2;
 LL | |     };
    | |_____^
    |
-   = note: let (t, t1) = (t, t1);
+   = note: drop(&(t, t1));
 
 error: Drop order affected for closure because of `capture_disjoint_fields`
   --> $DIR/insignificant_drop.rs:47:13
@@ -45,7 +45,7 @@ LL | |         println!("{}", t1.1);
 LL | |     };
    | |_____^
    |
-   = note: let (t) = (t);
+   = note: drop(&(t));
 
 error: Drop order affected for closure because of `capture_disjoint_fields`
   --> $DIR/insignificant_drop.rs:65:13
@@ -59,7 +59,7 @@ LL | |         let _t1 = t1.0;
 LL | |     };
    | |_____^
    |
-   = note: let (t) = (t);
+   = note: drop(&(t));
 
 error: Drop order affected for closure because of `capture_disjoint_fields`
   --> $DIR/insignificant_drop.rs:83:13
@@ -73,7 +73,7 @@ LL | |         let _s = s.0;
 LL | |     };
    | |_____^
    |
-   = note: let (t) = (t);
+   = note: drop(&(t));
 
 error: Drop order affected for closure because of `capture_disjoint_fields`
   --> $DIR/insignificant_drop.rs:98:13
@@ -86,7 +86,7 @@ LL | |         println!("{} {}", t1.1, t.1);
 LL | |     };
    | |_____^
    |
-   = note: let (t1, t) = (t1, t);
+   = note: drop(&(t1, t));
 
 error: Drop order affected for closure because of `capture_disjoint_fields`
   --> $DIR/insignificant_drop.rs:113:13
@@ -99,7 +99,7 @@ LL | |         let _t = t.0;
 LL | |     };
    | |_____^
    |
-   = note: let (t) = (t);
+   = note: drop(&(t));
 
 error: aborting due to 7 previous errors
 
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs
index 40699f48fab92..60d832849cc87 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs
@@ -23,7 +23,7 @@ fn test1_all_need_migration() {
 
     let c = || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
-    //~| NOTE: let (t, t1, t2) = (t, t1, t2);
+    //~| NOTE: drop(&(t, t1, t2));
         let _t = t.0;
         let _t1 = t1.0;
         let _t2 = t2.0;
@@ -41,7 +41,7 @@ fn test2_only_precise_paths_need_migration() {
 
     let c = || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
-    //~| NOTE: let (t, t1) = (t, t1);
+    //~| NOTE: drop(&(t, t1));
         let _t = t.0;
         let _t1 = t1.0;
         let _t2 = t2;
@@ -57,7 +57,7 @@ fn test3_only_by_value_need_migration() {
     let t1 = (Foo(0), Foo(0));
     let c = || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
-    //~| NOTE: let (t) = (t);
+    //~| NOTE: drop(&(t));
         let _t = t.0;
         println!("{:?}", t1.1);
     };
@@ -74,7 +74,7 @@ fn test4_type_contains_drop_need_migration() {
 
     let c = || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
-    //~| NOTE: let (t) = (t);
+    //~| NOTE: drop(&(t));
         let _t = t.0;
     };
 
@@ -89,7 +89,7 @@ fn test5_drop_non_drop_aggregate_need_migration() {
 
     let c = || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
-    //~| NOTE: let (t) = (t);
+    //~| NOTE: drop(&(t));
         let _t = t.0;
     };
 
@@ -104,7 +104,7 @@ fn test6_significant_insignificant_drop_aggregate_need_migration() {
 
     let c = || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
-    //~| NOTE: let (t) = (t);
+    //~| NOTE: drop(&(t));
         let _t = t.1;
     };
 
@@ -119,7 +119,7 @@ fn test7_move_closures_non_copy_types_might_need_migration() {
 
     let c = move || {
     //~^ERROR: Drop order affected for closure because of `capture_disjoint_fields`
-    //~| NOTE: let (t1, t) = (t1, t);
+    //~| NOTE: drop(&(t1, t));
         println!("{:?} {:?}", t1.1, t.1);
     };
 
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr
index a2308e1ef9451..5543dccf2753d 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr
@@ -16,7 +16,7 @@ note: the lint level is defined here
    |
 LL | #![deny(disjoint_capture_drop_reorder)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: let (t, t1, t2) = (t, t1, t2);
+   = note: drop(&(t, t1, t2));
 
 error: Drop order affected for closure because of `capture_disjoint_fields`
   --> $DIR/significant_drop.rs:42:13
@@ -31,7 +31,7 @@ LL | |         let _t2 = t2;
 LL | |     };
    | |_____^
    |
-   = note: let (t, t1) = (t, t1);
+   = note: drop(&(t, t1));
 
 error: Drop order affected for closure because of `capture_disjoint_fields`
   --> $DIR/significant_drop.rs:58:13
@@ -45,7 +45,7 @@ LL | |         println!("{:?}", t1.1);
 LL | |     };
    | |_____^
    |
-   = note: let (t) = (t);
+   = note: drop(&(t));
 
 error: Drop order affected for closure because of `capture_disjoint_fields`
   --> $DIR/significant_drop.rs:75:13
@@ -58,7 +58,7 @@ LL | |         let _t = t.0;
 LL | |     };
    | |_____^
    |
-   = note: let (t) = (t);
+   = note: drop(&(t));
 
 error: Drop order affected for closure because of `capture_disjoint_fields`
   --> $DIR/significant_drop.rs:90:13
@@ -71,7 +71,7 @@ LL | |         let _t = t.0;
 LL | |     };
    | |_____^
    |
-   = note: let (t) = (t);
+   = note: drop(&(t));
 
 error: Drop order affected for closure because of `capture_disjoint_fields`
   --> $DIR/significant_drop.rs:105:13
@@ -84,7 +84,7 @@ LL | |         let _t = t.1;
 LL | |     };
    | |_____^
    |
-   = note: let (t) = (t);
+   = note: drop(&(t));
 
 error: Drop order affected for closure because of `capture_disjoint_fields`
   --> $DIR/significant_drop.rs:120:13
@@ -97,7 +97,7 @@ LL | |         println!("{:?} {:?}", t1.1, t.1);
 LL | |     };
    | |_____^
    |
-   = note: let (t1, t) = (t1, t);
+   = note: drop(&(t1, t));
 
 error: aborting due to 7 previous errors
 

From b421cd56d945743defe3b2a32e2901648ac8dd2d Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Thu, 3 Dec 2020 23:40:09 -0500
Subject: [PATCH 11/24] Restrict precision of captures with
 `capture_disjoint_fields` set

- No Derefs in move closure, this will result in value behind a reference getting moved.
- No projections are applied to raw pointers, since these require unsafe blocks. We capture
  them completely.

Motivations for these are recorded here: https://hackmd.io/71qq-IOpTNqzMkPpAI1dVg?view
---
 compiler/rustc_typeck/src/check/upvar.rs | 70 +++++++++++++++++++++---
 1 file changed, 61 insertions(+), 9 deletions(-)

diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index 6b2cba62fa6b7..2252493577ff6 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -419,15 +419,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 base => bug!("Expected upvar, found={:?}", base),
             };
 
-            // Arrays are captured in entirety, drop Index projections and projections
-            // after Index projections.
-            let first_index_projection =
-                place.projections.split(|proj| ProjectionKind::Index == proj.kind).next();
-            let place = Place {
-                base_ty: place.base_ty,
-                base: place.base,
-                projections: first_index_projection.map_or(Vec::new(), |p| p.to_vec()),
-            };
+            let place = restrict_capture_precision(place, capture_info.capture_kind);
 
             let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) {
                 None => {
@@ -960,6 +952,66 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
     }
 }
 
+/// Truncate projections so that following rules are obeyed by the captured `place`:
+///
+/// - No Derefs in move closure, this will result in value behind a reference getting moved.
+/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
+///   them completely.
+/// - No Index projections are captured, since arrays are captured completely.
+fn restrict_capture_precision<'tcx>(
+    mut place: Place<'tcx>,
+    capture_kind: ty::UpvarCapture<'tcx>,
+) -> Place<'tcx> {
+    if place.projections.is_empty() {
+        // Nothing to do here
+        return place;
+    }
+
+    if place.base_ty.is_unsafe_ptr() {
+        place.projections.truncate(0);
+        return place;
+    }
+
+    let mut truncated_length = usize::MAX;
+    let mut first_deref_projection = usize::MAX;
+
+    for (i, proj) in place.projections.iter().enumerate() {
+        if proj.ty.is_unsafe_ptr() {
+            // Don't apply any projections on top of an unsafe ptr
+            truncated_length = truncated_length.min(i + 1);
+            break;
+        }
+        match proj.kind {
+            ProjectionKind::Index => {
+                // Arrays are completely captured, so we drop Index projections
+                truncated_length = truncated_length.min(i);
+                break;
+            }
+            ProjectionKind::Deref => {
+                // We only drop Derefs in case of move closures
+                // There might be an index projection or raw ptr ahead, so we don't stop here.
+                first_deref_projection = first_deref_projection.min(i);
+            }
+            ProjectionKind::Field(..) => {} // ignore
+            ProjectionKind::Subslice => {}  // We never capture this
+        }
+    }
+
+    let length = place
+        .projections
+        .len()
+        .min(truncated_length)
+        // In case of capture `ByValue` we want to not capture derefs
+        .min(match capture_kind {
+            ty::UpvarCapture::ByValue(..) => first_deref_projection,
+            ty::UpvarCapture::ByRef(..) => usize::MAX,
+        });
+
+    place.projections.truncate(length);
+
+    place
+}
+
 fn construct_place_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String {
     let variable_name = match place.base {
         PlaceBase::Upvar(upvar_id) => var_name(tcx, upvar_id.var_path.hir_id).to_string(),

From 34880825828260fad0a74621e4a13fe7da9a4a9d Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Wed, 2 Dec 2020 03:03:56 -0500
Subject: [PATCH 12/24] Compute mutability of closure captures

When `capture_disjoint_fields` is not enabled, checking if the root variable
binding is mutable would suffice.

However with the feature enabled, the captured place might be mutable
because it dereferences a mutable reference.

This PR computes the mutability of each capture after capture analysis
in rustc_typeck. We store this in `ty::CapturedPlace` and then use
`ty::CapturedPlace::mutability` in mir_build and borrow_check.
---
 compiler/rustc_middle/src/ty/mod.rs        |  8 +++-
 compiler/rustc_mir/src/borrow_check/mod.rs |  9 +---
 compiler/rustc_mir_build/src/build/mod.rs  | 11 +----
 compiler/rustc_typeck/src/check/upvar.rs   | 54 ++++++++++++++++++++--
 4 files changed, 60 insertions(+), 22 deletions(-)

diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 8e8caa46c3802..ddb78d91759f9 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -661,11 +661,17 @@ pub type RootVariableMinCaptureList<'tcx> = FxIndexMap<hir::HirId, MinCaptureLis
 /// Part of `MinCaptureInformationMap`; List of `CapturePlace`s.
 pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>;
 
-/// A `Place` and the corresponding `CaptureInfo`.
+/// A composite describing a `Place` that is captured by a closure.
 #[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
 pub struct CapturedPlace<'tcx> {
+    /// The `Place` that is captured.
     pub place: HirPlace<'tcx>,
+
+    /// `CaptureKind` and expression(s) that resulted in such capture of `place`.
     pub info: CaptureInfo<'tcx>,
+
+    /// Represents if `place` can be mutated or not.
+    pub mutability: hir::Mutability,
 }
 
 pub fn place_to_string_for_capture(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String {
diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs
index 7c7edfdb5fbaf..1a771157e288a 100644
--- a/compiler/rustc_mir/src/borrow_check/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/mod.rs
@@ -170,17 +170,12 @@ fn do_mir_borrowck<'a, 'tcx>(
                 ty::UpvarCapture::ByValue(_) => false,
                 ty::UpvarCapture::ByRef(..) => true,
             };
-            let mut upvar = Upvar {
+            Upvar {
                 name: tcx.hir().name(var_hir_id),
                 var_hir_id,
                 by_ref,
-                mutability: Mutability::Not,
-            };
-            let bm = *tables.pat_binding_modes().get(var_hir_id).expect("missing binding mode");
-            if bm == ty::BindByValue(hir::Mutability::Mut) {
-                upvar.mutability = Mutability::Mut;
+                mutability: captured_place.mutability,
             }
-            upvar
         })
         .collect();
 
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 996615995259d..e4891eb5a3c0c 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -851,22 +851,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         _ => bug!("Expected an upvar")
                     };
 
-                    let mut mutability = Mutability::Not;
+                    let mutability = captured_place.mutability;
 
                     // FIXME(project-rfc-2229#8): Store more precise information
                     let mut name = kw::Empty;
                     if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
                         if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
                             name = ident.name;
-                            match hir_typeck_results
-                                .extract_binding_mode(tcx.sess, pat.hir_id, pat.span)
-                            {
-                                Some(ty::BindByValue(hir::Mutability::Mut)) => {
-                                    mutability = Mutability::Mut;
-                                }
-                                Some(_) => mutability = Mutability::Not,
-                                _ => {}
-                            }
                         }
                     }
 
diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index 2252493577ff6..38330d5a49a94 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -252,8 +252,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let capture = captured_place.info.capture_kind;
 
                 debug!(
-                    "place={:?} upvar_ty={:?} capture={:?}",
-                    captured_place.place, upvar_ty, capture
+                    "final_upvar_tys: place={:?} upvar_ty={:?} capture={:?}, mutability={:?}",
+                    captured_place.place, upvar_ty, capture, captured_place.mutability,
                 );
 
                 match capture {
@@ -423,7 +423,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) {
                 None => {
-                    let min_cap_list = vec![ty::CapturedPlace { place, info: capture_info }];
+                    let mutability = self.determine_capture_mutability(&place);
+                    let min_cap_list =
+                        vec![ty::CapturedPlace { place, info: capture_info, mutability }];
                     root_var_min_capture_list.insert(var_hir_id, min_cap_list);
                     continue;
                 }
@@ -486,8 +488,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             // Only need to insert when we don't have an ancestor in the existing min capture list
             if !ancestor_found {
+                let mutability = self.determine_capture_mutability(&place);
                 let captured_place =
-                    ty::CapturedPlace { place: place.clone(), info: updated_capture_info };
+                    ty::CapturedPlace { place, info: updated_capture_info, mutability };
                 min_cap_list.push(captured_place);
             }
         }
@@ -607,6 +610,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
     }
+
+    /// A captured place is mutable if
+    /// 1. Projections don't include a Deref of an immut-borrow, **and**
+    /// 2. PlaceBase is mut or projections include a Deref of a mut-borrow.
+    fn determine_capture_mutability(&self, place: &Place<'tcx>) -> hir::Mutability {
+        let var_hir_id = match place.base {
+            PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
+            _ => unreachable!(),
+        };
+
+        let bm = *self
+            .typeck_results
+            .borrow()
+            .pat_binding_modes()
+            .get(var_hir_id)
+            .expect("missing binding mode");
+
+        let mut is_mutbl = match bm {
+            ty::BindByValue(mutability) => mutability,
+            ty::BindByReference(_) => hir::Mutability::Not,
+        };
+
+        for pointer_ty in place.deref_tys() {
+            match pointer_ty.kind() {
+                // We don't capture derefs of raw ptrs
+                ty::RawPtr(_) => unreachable!(),
+
+                // Derefencing a mut-ref allows us to mut the Place if we don't deref
+                // an immut-ref after on top of this.
+                ty::Ref(.., hir::Mutability::Mut) => is_mutbl = hir::Mutability::Mut,
+
+                // The place isn't mutable once we dereference a immutable reference.
+                ty::Ref(.., hir::Mutability::Not) => return hir::Mutability::Not,
+
+                // Dereferencing a box doesn't change mutability
+                ty::Adt(def, ..) if def.is_box() => {}
+
+                unexpected_ty => bug!("deref of unexpected pointer type {:?}", unexpected_ty),
+            }
+        }
+
+        is_mutbl
+    }
 }
 
 struct InferBorrowKind<'a, 'tcx> {

From 1373f988fa662aa43a22a9b13300aabe9ce2213b Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Sun, 13 Dec 2020 01:29:20 -0500
Subject: [PATCH 13/24] Test cases for handling mutable references

---
 .../diagnostics/cant-mutate-imm-borrow.rs     | 21 +++++++
 .../diagnostics/cant-mutate-imm-borrow.stderr | 31 ++++++++++
 .../diagnostics/mut_ref.rs                    | 40 +++++++++++++
 .../diagnostics/mut_ref.stderr                | 52 +++++++++++++++++
 .../2229_closure_analysis/run_pass/mut_ref.rs | 56 +++++++++++++++++++
 .../run_pass/mut_ref.stderr                   | 11 ++++
 .../run_pass/mut_ref_struct_mem.rs            | 23 ++++++++
 .../run_pass/mut_ref_struct_mem.stderr        | 11 ++++
 8 files changed, 245 insertions(+)
 create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs
 create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr
 create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs
 create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr
 create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs
 create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.stderr
 create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs
 create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.stderr

diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs
new file mode 100644
index 0000000000000..dd018a0defa1b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs
@@ -0,0 +1,21 @@
+// Test that if we deref an immutable borrow to access a Place,
+// then we can't mutate the final place.
+
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+
+fn main() {
+    let mut x = (format!(""), format!("X2"));
+    let mut y = (&x, "Y");
+    let z = (&mut y, "Z");
+
+    // `x.0` is mutable but we access `x` via `z.0.0`, which is an immutable reference and
+    // therefore can't be mutated.
+    let mut c = || {
+    //~^ ERROR: cannot borrow `z.0.0.0` as mutable, as it is behind a `&` reference
+        z.0.0.0 = format!("X1");
+        //~^ ERROR: cannot assign to `z`, as it is not declared as mutable
+    };
+
+    c();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr
new file mode 100644
index 0000000000000..948e2b731daf0
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr
@@ -0,0 +1,31 @@
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/cant-mutate-imm-borrow.rs:4:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error[E0594]: cannot assign to `z`, as it is not declared as mutable
+  --> $DIR/cant-mutate-imm-borrow.rs:16:9
+   |
+LL |     let z = (&mut y, "Z");
+   |         - help: consider changing this to be mutable: `mut z`
+...
+LL |         z.0.0.0 = format!("X1");
+   |         ^^^^^^^ cannot assign
+
+error[E0596]: cannot borrow `z.0.0.0` as mutable, as it is behind a `&` reference
+  --> $DIR/cant-mutate-imm-borrow.rs:14:17
+   |
+LL |     let mut c = || {
+   |                 ^^ cannot borrow as mutable
+LL |
+LL |         z.0.0.0 = format!("X1");
+   |         - mutable borrow occurs due to use of `z.0.0.0` in closure
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
+Some errors have detailed explanations: E0594, E0596.
+For more information about an error, try `rustc --explain E0594`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs
new file mode 100644
index 0000000000000..732c47298242a
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs
@@ -0,0 +1,40 @@
+// Test that we can't mutate a place if we need to deref an imm-borrow
+// to reach it.
+
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+
+fn imm_mut_ref() {
+    let mut x = String::new();
+    let y = String::new();
+    let mref_x = &mut x;
+    let ref_mref_x = &mref_x;
+
+    let c = || {
+    //~^ ERROR: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference
+        **ref_mref_x = y;
+        //~^ERROR: cannot assign to `ref_mref_x`, as it is not declared as mutable
+    };
+
+    c();
+}
+
+fn mut_imm_ref() {
+    let x = String::new();
+    let y = String::new();
+    let mut ref_x = &x;
+    let mref_ref_x = &mut ref_x;
+
+    let c = || {
+    //~^ ERROR: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference
+        **mref_ref_x = y;
+        //~^ERROR: cannot assign to `mref_ref_x`, as it is not declared as mutable
+    };
+
+    c();
+}
+
+fn main() {
+    imm_mut_ref();
+    mut_imm_ref();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr
new file mode 100644
index 0000000000000..42b3c5090ac66
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr
@@ -0,0 +1,52 @@
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/mut_ref.rs:4:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error[E0594]: cannot assign to `ref_mref_x`, as it is not declared as mutable
+  --> $DIR/mut_ref.rs:15:9
+   |
+LL |     let ref_mref_x = &mref_x;
+   |         ---------- help: consider changing this to be mutable: `mut ref_mref_x`
+...
+LL |         **ref_mref_x = y;
+   |         ^^^^^^^^^^^^ cannot assign
+
+error[E0596]: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference
+  --> $DIR/mut_ref.rs:13:13
+   |
+LL |     let ref_mref_x = &mref_x;
+   |                      ------- help: consider changing this to be a mutable reference: `&mut mref_x`
+LL | 
+LL |     let c = || {
+   |             ^^ `ref_mref_x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+LL |
+LL |         **ref_mref_x = y;
+   |           ---------- mutable borrow occurs due to use of `**ref_mref_x` in closure
+
+error[E0594]: cannot assign to `mref_ref_x`, as it is not declared as mutable
+  --> $DIR/mut_ref.rs:30:9
+   |
+LL |     let mref_ref_x = &mut ref_x;
+   |         ---------- help: consider changing this to be mutable: `mut mref_ref_x`
+...
+LL |         **mref_ref_x = y;
+   |         ^^^^^^^^^^^^ cannot assign
+
+error[E0596]: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference
+  --> $DIR/mut_ref.rs:28:13
+   |
+LL |     let c = || {
+   |             ^^ cannot borrow as mutable
+LL |
+LL |         **mref_ref_x = y;
+   |           ---------- mutable borrow occurs due to use of `**mref_ref_x` in closure
+
+error: aborting due to 4 previous errors; 1 warning emitted
+
+Some errors have detailed explanations: E0594, E0596.
+For more information about an error, try `rustc --explain E0594`.
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs
new file mode 100644
index 0000000000000..315622443c3cc
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs
@@ -0,0 +1,56 @@
+// run-pass
+
+// Test that we can mutate a place through a mut-borrow
+// that is captured by the closure
+
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+
+// Check that we can mutate when one deref is required
+fn mut_ref_1() {
+    let mut x = String::new();
+    let rx = &mut x;
+
+    let mut c = || {
+        *rx = String::new();
+    };
+
+    c();
+}
+
+// Similar example as mut_ref_1, we don't deref the imm-borrow here,
+// and so we are allowed to mutate.
+fn mut_ref_2() {
+    let x = String::new();
+    let y = String::new();
+    let mut ref_x = &x;
+    let m_ref_x = &mut ref_x;
+
+    let mut c = || {
+        *m_ref_x = &y;
+    };
+
+    c();
+}
+
+// Check that we can mutate when multiple derefs of mut-borrows are required to reach
+// the target place.
+// It works because all derefs are mutable, if either of them was an immutable
+// borrow, then we would not be able to deref.
+fn mut_mut_ref() {
+    let mut x = String::new();
+    let mut mref_x = &mut x;
+    let m_mref_x = &mut mref_x;
+
+    let mut c = || {
+        **m_mref_x = String::new();
+    };
+
+    c();
+}
+
+fn main() {
+    mut_ref_1();
+    mut_ref_2();
+    mut_mut_ref();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.stderr
new file mode 100644
index 0000000000000..4b37a0b405f5e
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref.stderr
@@ -0,0 +1,11 @@
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/mut_ref.rs:6:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs
new file mode 100644
index 0000000000000..82e723cc82562
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs
@@ -0,0 +1,23 @@
+// run-pass
+
+// Test that we can mutate a place through a mut-borrow
+// that is captured by the closure
+//
+// More specifically we test that the if the mutable reference isn't root variable of a capture
+// but rather accessed while acessing the precise capture.
+
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+
+fn main() {
+    let mut t = (10, 10);
+
+    let t1 = (&mut t, 10);
+
+    let mut c = || {
+        // Mutable because (*t.0) is mutable
+        t1.0.0 += 10;
+    };
+
+    c();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.stderr
new file mode 100644
index 0000000000000..418ab29098b2a
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.stderr
@@ -0,0 +1,11 @@
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/mut_ref_struct_mem.rs:9:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+warning: 1 warning emitted
+

From 0897db56098dd8e8355017f4364bc88f1e4f26c0 Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Mon, 14 Dec 2020 03:09:17 -0500
Subject: [PATCH 14/24] Test for restricting capture precision

---
 .../2229_closure_analysis/by_value.rs         |  41 +++++
 .../2229_closure_analysis/by_value.stderr     |  67 ++++++++
 .../2229_closure_analysis/move_closure.rs     |  72 +++++++++
 .../2229_closure_analysis/move_closure.stderr | 147 ++++++++++++++++++
 .../run_pass/by_value.rs                      |  28 ++++
 .../run_pass/by_value.stderr                  |  11 ++
 .../run_pass/move_closure.rs                  |  47 ++++++
 .../run_pass/move_closure.stderr              |  21 +++
 .../run_pass/unsafe_ptr.rs                    |  47 ++++++
 .../run_pass/unsafe_ptr.stderr                |  11 ++
 .../2229_closure_analysis/unsafe_ptr.rs       |  63 ++++++++
 .../2229_closure_analysis/unsafe_ptr.stderr   | 102 ++++++++++++
 12 files changed, 657 insertions(+)
 create mode 100644 src/test/ui/closures/2229_closure_analysis/by_value.rs
 create mode 100644 src/test/ui/closures/2229_closure_analysis/by_value.stderr
 create mode 100644 src/test/ui/closures/2229_closure_analysis/move_closure.rs
 create mode 100644 src/test/ui/closures/2229_closure_analysis/move_closure.stderr
 create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs
 create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/by_value.stderr
 create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs
 create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.stderr
 create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs
 create mode 100644 src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.stderr
 create mode 100644 src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs
 create mode 100644 src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr

diff --git a/src/test/ui/closures/2229_closure_analysis/by_value.rs b/src/test/ui/closures/2229_closure_analysis/by_value.rs
new file mode 100644
index 0000000000000..1007fb582e5ed
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/by_value.rs
@@ -0,0 +1,41 @@
+// Test that we handle derferences properly when only some of the captures are being moved with
+// `capture_disjoint_fields` enabled.
+
+
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+//~| NOTE: `#[warn(incomplete_features)]` on by default
+//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
+#![feature(rustc_attrs)]
+
+#[derive(Debug, Default)]
+struct SomeLargeType;
+struct MuchLargerType([SomeLargeType; 32]);
+
+// Ensure that we don't capture any derefs when moving captures into the closures,
+// i.e. only data from the enclosing stack is moved.
+fn big_box() {
+    let s = MuchLargerType(Default::default());
+    let b = Box::new(s);
+    let t = (b, 10);
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        let p = t.0.0;
+        //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
+        //~| NOTE: Min Capture t[(0, 0)] -> ByValue
+        println!("{} {:?}", t.1, p);
+        //~^ NOTE: Capturing t[(1, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture t[(1, 0)] -> ImmBorrow
+    };
+
+    c();
+}
+
+fn main() {
+    big_box();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/by_value.stderr b/src/test/ui/closures/2229_closure_analysis/by_value.stderr
new file mode 100644
index 0000000000000..fe04dbef6d8b5
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/by_value.stderr
@@ -0,0 +1,67 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/by_value.rs:22:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/by_value.rs:5:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error: First Pass analysis includes:
+  --> $DIR/by_value.rs:25:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let p = t.0.0;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
+  --> $DIR/by_value.rs:28:17
+   |
+LL |         let p = t.0.0;
+   |                 ^^^^^
+note: Capturing t[(1, 0)] -> ImmBorrow
+  --> $DIR/by_value.rs:31:29
+   |
+LL |         println!("{} {:?}", t.1, p);
+   |                             ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/by_value.rs:25:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let p = t.0.0;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[(0, 0)] -> ByValue
+  --> $DIR/by_value.rs:28:17
+   |
+LL |         let p = t.0.0;
+   |                 ^^^^^
+note: Min Capture t[(1, 0)] -> ImmBorrow
+  --> $DIR/by_value.rs:31:29
+   |
+LL |         println!("{} {:?}", t.1, p);
+   |                             ^^^
+
+error: aborting due to 3 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/move_closure.rs b/src/test/ui/closures/2229_closure_analysis/move_closure.rs
new file mode 100644
index 0000000000000..8bdc999ca3c3f
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/move_closure.rs
@@ -0,0 +1,72 @@
+// Test that move closures drop derefs with `capture_disjoint_fields` enabled.
+
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+//~| NOTE: `#[warn(incomplete_features)]` on by default
+//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
+#![feature(rustc_attrs)]
+
+// Test we truncate derefs properly
+fn simple_ref() {
+    let mut s = 10;
+    let ref_s = &mut s;
+
+    let mut c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    move || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        *ref_s += 10;
+        //~^ NOTE: Capturing ref_s[Deref] -> ByValue
+        //~| NOTE: Min Capture ref_s[] -> ByValue
+    };
+    c();
+}
+
+// Test we truncate derefs properly
+fn struct_contains_ref_to_another_struct() {
+    struct S(String);
+    struct T<'a>(&'a mut S);
+
+    let mut s = S("s".into());
+    let t = T(&mut s);
+
+    let mut c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    move || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        t.0.0 = "new s".into();
+        //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
+        //~| NOTE: Min Capture t[(0, 0)] -> ByValue
+    };
+
+    c();
+}
+
+// Test that we don't reduce precision when there is nothing deref.
+fn no_ref() {
+    struct S(String);
+    struct T(S);
+
+    let t = T(S("s".into()));
+    let mut c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    move || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        t.0.0 = "new S".into();
+        //~^ NOTE: Capturing t[(0, 0),(0, 0)] -> ByValue
+        //~| NOTE: Min Capture t[(0, 0),(0, 0)] -> ByValue
+    };
+    c();
+}
+
+fn main() {
+    simple_ref();
+    struct_contains_ref_to_another_struct();
+    no_ref();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/move_closure.stderr b/src/test/ui/closures/2229_closure_analysis/move_closure.stderr
new file mode 100644
index 0000000000000..a745f14598ee2
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/move_closure.stderr
@@ -0,0 +1,147 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/move_closure.rs:14:17
+   |
+LL |     let mut c = #[rustc_capture_analysis]
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/move_closure.rs:35:17
+   |
+LL |     let mut c = #[rustc_capture_analysis]
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/move_closure.rs:55:17
+   |
+LL |     let mut c = #[rustc_capture_analysis]
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/move_closure.rs:3:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error: First Pass analysis includes:
+  --> $DIR/move_closure.rs:17:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         *ref_s += 10;
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing ref_s[Deref] -> ByValue
+  --> $DIR/move_closure.rs:20:9
+   |
+LL |         *ref_s += 10;
+   |         ^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/move_closure.rs:17:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         *ref_s += 10;
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture ref_s[] -> ByValue
+  --> $DIR/move_closure.rs:20:9
+   |
+LL |         *ref_s += 10;
+   |         ^^^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/move_closure.rs:38:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         t.0.0 = "new s".into();
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
+  --> $DIR/move_closure.rs:41:9
+   |
+LL |         t.0.0 = "new s".into();
+   |         ^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/move_closure.rs:38:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         t.0.0 = "new s".into();
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[(0, 0)] -> ByValue
+  --> $DIR/move_closure.rs:41:9
+   |
+LL |         t.0.0 = "new s".into();
+   |         ^^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/move_closure.rs:58:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         t.0.0 = "new S".into();
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(0, 0),(0, 0)] -> ByValue
+  --> $DIR/move_closure.rs:61:9
+   |
+LL |         t.0.0 = "new S".into();
+   |         ^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/move_closure.rs:58:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         t.0.0 = "new S".into();
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[(0, 0),(0, 0)] -> ByValue
+  --> $DIR/move_closure.rs:61:9
+   |
+LL |         t.0.0 = "new S".into();
+   |         ^^^^^
+
+error: aborting due to 9 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs
new file mode 100644
index 0000000000000..9a93e6cf1e1ef
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.rs
@@ -0,0 +1,28 @@
+// run-pass
+
+// Test that ByValue captures compile sucessefully especially when the captures are
+// derefenced within the closure.
+
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+
+#[derive(Debug, Default)]
+struct SomeLargeType;
+struct MuchLargerType([SomeLargeType; 32]);
+
+fn big_box() {
+    let s = MuchLargerType(Default::default());
+    let b = Box::new(s);
+    let t = (b, 10);
+
+    let c = || {
+        let p = t.0.0;
+        println!("{} {:?}", t.1, p);
+    };
+
+    c();
+}
+
+fn main() {
+    big_box();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.stderr
new file mode 100644
index 0000000000000..98715c6b94365
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/by_value.stderr
@@ -0,0 +1,11 @@
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/by_value.rs:6:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs
new file mode 100644
index 0000000000000..83ed1c28462d3
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs
@@ -0,0 +1,47 @@
+// run-pass
+
+// Test that move closures compile properly with `capture_disjoint_fields` enabled.
+
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+
+fn simple_ref() {
+    let mut s = 10;
+    let ref_s = &mut s;
+
+    let mut c = move || {
+        *ref_s += 10;
+    };
+    c();
+}
+
+fn struct_contains_ref_to_another_struct() {
+    struct S(String);
+    struct T<'a>(&'a mut S);
+
+    let mut s = S("s".into());
+    let t = T(&mut s);
+
+    let mut c = move || {
+        t.0.0 = "new s".into();
+    };
+
+    c();
+}
+
+fn no_ref() {
+    struct S(String);
+    struct T(S);
+
+    let t = T(S("s".into()));
+    let mut c = move || {
+        t.0.0 = "new S".into();
+    };
+    c();
+}
+
+fn main() {
+    simple_ref();
+    struct_contains_ref_to_another_struct();
+    no_ref();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.stderr
new file mode 100644
index 0000000000000..724b683bfbf87
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.stderr
@@ -0,0 +1,21 @@
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/move_closure.rs:5:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+warning: variable does not need to be mutable
+  --> $DIR/move_closure.rs:36:9
+   |
+LL |     let mut t = T(S("s".into()));
+   |         ----^
+   |         |
+   |         help: remove this `mut`
+   |
+   = note: `#[warn(unused_mut)]` on by default
+
+warning: 2 warnings emitted
+
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs
new file mode 100644
index 0000000000000..f6e9862b26c11
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs
@@ -0,0 +1,47 @@
+// run-pass
+
+// Test that we can use raw ptrs when using `capture_disjoint_fields`.
+
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+
+#[derive(Debug)]
+struct S {
+    s: String,
+    t: String,
+}
+
+struct T(*const S);
+
+fn unsafe_imm() {
+    let s = "".into();
+    let t = "".into();
+    let my_speed: Box<S> = Box::new(S { s, t });
+
+    let p : *const S = Box::into_raw(my_speed);
+    let t = T(p);
+
+    let c = || unsafe {
+        println!("{:?}", (*t.0).s);
+    };
+
+    c();
+}
+
+fn unsafe_mut() {
+    let s = "".into();
+    let t = "".into();
+    let mut my_speed: Box<S> = Box::new(S { s, t });
+    let p : *mut S = &mut *my_speed;
+
+    let c = || {
+        let x = unsafe { &mut (*p).s };
+        *x = "s".into();
+    };
+    c();
+}
+
+fn main() {
+    unsafe_mut();
+    unsafe_imm();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.stderr
new file mode 100644
index 0000000000000..c64c8b72e8151
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.stderr
@@ -0,0 +1,11 @@
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/unsafe_ptr.rs:5:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs
new file mode 100644
index 0000000000000..79d3ecc2d2bed
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.rs
@@ -0,0 +1,63 @@
+// Test that we restrict precision of a capture when we access a raw ptr,
+// i.e. the capture doesn't deref the raw ptr.
+
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+//~| `#[warn(incomplete_features)]` on by default
+//~| see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct S {
+    s: String,
+    t: String,
+}
+
+struct T(*const S);
+
+fn unsafe_imm() {
+    let s = "".into();
+    let t = "".into();
+    let my_speed: Box<S> = Box::new(S { s, t });
+
+    let p : *const S = Box::into_raw(my_speed);
+    let t = T(p);
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+     || unsafe {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        println!("{:?}", (*t.0).s);
+        //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture t[(0, 0)] -> ImmBorrow
+    };
+
+    c();
+}
+
+fn unsafe_mut() {
+    let s = "".into();
+    let t = "".into();
+    let mut my_speed: Box<S> = Box::new(S { s, t });
+    let p : *mut S = &mut *my_speed;
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        let x = unsafe { &mut (*p).s };
+        //~^ NOTE: Capturing p[Deref,(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture p[] -> ImmBorrow
+        *x = "s".into();
+    };
+    c();
+}
+
+fn main() {
+    unsafe_mut();
+    unsafe_imm();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr
new file mode 100644
index 0000000000000..4508b2426e8ff
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/unsafe_ptr.stderr
@@ -0,0 +1,102 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/unsafe_ptr.rs:26:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/unsafe_ptr.rs:46:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/unsafe_ptr.rs:4:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error: First Pass analysis includes:
+  --> $DIR/unsafe_ptr.rs:29:6
+   |
+LL | /      || unsafe {
+LL | |
+LL | |
+LL | |         println!("{:?}", (*t.0).s);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
+  --> $DIR/unsafe_ptr.rs:32:26
+   |
+LL |         println!("{:?}", (*t.0).s);
+   |                          ^^^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/unsafe_ptr.rs:29:6
+   |
+LL | /      || unsafe {
+LL | |
+LL | |
+LL | |         println!("{:?}", (*t.0).s);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[(0, 0)] -> ImmBorrow
+  --> $DIR/unsafe_ptr.rs:32:26
+   |
+LL |         println!("{:?}", (*t.0).s);
+   |                          ^^^^^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/unsafe_ptr.rs:49:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let x = unsafe { &mut (*p).s };
+...  |
+LL | |         *x = "s".into();
+LL | |     };
+   | |_____^
+   |
+note: Capturing p[Deref,(0, 0)] -> ImmBorrow
+  --> $DIR/unsafe_ptr.rs:52:31
+   |
+LL |         let x = unsafe { &mut (*p).s };
+   |                               ^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/unsafe_ptr.rs:49:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let x = unsafe { &mut (*p).s };
+...  |
+LL | |         *x = "s".into();
+LL | |     };
+   | |_____^
+   |
+note: Min Capture p[] -> ImmBorrow
+  --> $DIR/unsafe_ptr.rs:52:31
+   |
+LL |         let x = unsafe { &mut (*p).s };
+   |                               ^^^^^^
+
+error: aborting due to 6 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0658`.

From 604cbdcfddb959cd1d7de2f9afa14a199561a428 Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Tue, 15 Dec 2020 23:00:19 -0500
Subject: [PATCH 15/24] Fix unused 'mut' warning for capture's root variable

---
 compiler/rustc_mir/src/borrow_check/mod.rs    | 33 ++++++++++++++++---
 .../run_pass/move_closure.rs                  | 25 +++++++++++---
 .../run_pass/move_closure.stderr              | 12 +------
 .../run_pass/mut_ref_struct_mem.rs            | 26 +++++++++++++--
 4 files changed, 75 insertions(+), 21 deletions(-)

diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs
index 1a771157e288a..6e1e5c65aea6d 100644
--- a/compiler/rustc_mir/src/borrow_check/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/mod.rs
@@ -1369,13 +1369,38 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'tcx>) {
         let propagate_closure_used_mut_place = |this: &mut Self, place: Place<'tcx>| {
-            if !place.projection.is_empty() {
-                if let Some(field) = this.is_upvar_field_projection(place.as_ref()) {
+            // We have three possiblities here:
+            // a. We are modifying something through a mut-ref
+            // b. We are modifying something that is local to our parent
+            // c. Current body is a nested clsoure, and we are modifying path starting from
+            //    a Place captured by our parent closure.
+
+            // Handle (c), the path being modified is exactly the path captured by our parent
+            if let Some(field) = this.is_upvar_field_projection(place.as_ref()) {
+                this.used_mut_upvars.push(field);
+                return;
+            }
+
+            for (place_ref, proj) in place.iter_projections().rev() {
+                // Handle (a)
+                if proj == ProjectionElem::Deref {
+                    match place_ref.ty(this.body(), this.infcx.tcx).ty.kind() {
+                        // We aren't modifying a variable directly
+                        ty::Ref(_, _, hir::Mutability::Mut) => return,
+
+                        _ => {}
+                    }
+                }
+
+                // Handle (c)
+                if let Some(field) = this.is_upvar_field_projection(place_ref) {
                     this.used_mut_upvars.push(field);
+                    return;
                 }
-            } else {
-                this.used_mut.insert(place.local);
             }
+
+            // Handle(b)
+            this.used_mut.insert(place.local);
         };
 
         // This relies on the current way that by-value
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs
index 83ed1c28462d3..4007a5a48aaec 100644
--- a/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs
@@ -29,19 +29,36 @@ fn struct_contains_ref_to_another_struct() {
     c();
 }
 
-fn no_ref() {
-    struct S(String);
-    struct T(S);
+#[derive(Debug)]
+struct S(String);
 
-    let t = T(S("s".into()));
+#[derive(Debug)]
+struct T(S);
+
+fn no_ref() {
+    let mut t = T(S("s".into()));
     let mut c = move || {
         t.0.0 = "new S".into();
     };
     c();
 }
 
+fn no_ref_nested() {
+    let mut t = T(S("s".into()));
+    let c = || {
+        println!("{:?}", t.0);
+        let mut c = move || {
+            t.0.0 = "new S".into();
+            println!("{:?}", t.0.0);
+        };
+        c();
+    };
+    c();
+}
+
 fn main() {
     simple_ref();
     struct_contains_ref_to_another_struct();
     no_ref();
+    no_ref_nested();
 }
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.stderr b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.stderr
index 724b683bfbf87..c1d8ba575d6fd 100644
--- a/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.stderr
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.stderr
@@ -7,15 +7,5 @@ LL | #![feature(capture_disjoint_fields)]
    = note: `#[warn(incomplete_features)]` on by default
    = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
 
-warning: variable does not need to be mutable
-  --> $DIR/move_closure.rs:36:9
-   |
-LL |     let mut t = T(S("s".into()));
-   |         ----^
-   |         |
-   |         help: remove this `mut`
-   |
-   = note: `#[warn(unused_mut)]` on by default
-
-warning: 2 warnings emitted
+warning: 1 warning emitted
 
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs
index 82e723cc82562..2dba923647a2e 100644
--- a/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs
@@ -2,14 +2,14 @@
 
 // Test that we can mutate a place through a mut-borrow
 // that is captured by the closure
-//
+
 // More specifically we test that the if the mutable reference isn't root variable of a capture
 // but rather accessed while acessing the precise capture.
 
 #![feature(capture_disjoint_fields)]
 //~^ WARNING: the feature `capture_disjoint_fields` is incomplete
 
-fn main() {
+fn mut_tuple() {
     let mut t = (10, 10);
 
     let t1 = (&mut t, 10);
@@ -21,3 +21,25 @@ fn main() {
 
     c();
 }
+
+fn mut_tuple_nested() {
+    let mut t = (10, 10);
+
+    let t1 = (&mut t, 10);
+
+    let mut c = || {
+        let mut c = || {
+            // Mutable because (*t.0) is mutable
+            t1.0.0 += 10;
+        };
+
+        c();
+    };
+
+    c();
+}
+
+fn main() {
+    mut_tuple();
+    mut_tuple_nested();
+}

From c748f32ee45ed42eaab4d6a62dc64720b5096c68 Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Sun, 13 Dec 2020 00:11:15 -0500
Subject: [PATCH 16/24] Fix incorrect use mut diagnostics

---
 compiler/rustc_middle/src/ty/mod.rs           |  9 +++++
 .../borrow_check/diagnostics/move_errors.rs   |  4 ++-
 .../diagnostics/mutability_errors.rs          | 32 +++++++++++++----
 .../src/borrow_check/diagnostics/var_name.rs  |  7 ++--
 compiler/rustc_mir/src/borrow_check/mod.rs    | 30 ++++++----------
 compiler/rustc_mir/src/borrow_check/nll.rs    |  2 +-
 .../rustc_mir/src/borrow_check/path_utils.rs  |  2 +-
 .../src/borrow_check/type_check/mod.rs        |  8 +++--
 .../diagnostics/cant-mutate-imm-borrow.rs     |  1 -
 .../diagnostics/cant-mutate-imm-borrow.stderr | 14 ++------
 .../diagnostics/cant-mutate-imm.rs            | 35 +++++++++++++++++++
 .../diagnostics/cant-mutate-imm.stderr        | 30 ++++++++++++++++
 .../diagnostics/mut_ref.rs                    |  2 --
 .../diagnostics/mut_ref.stderr                | 25 ++-----------
 14 files changed, 129 insertions(+), 72 deletions(-)
 create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs
 create mode 100644 src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr

diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index ddb78d91759f9..7681041863e9a 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -674,6 +674,15 @@ pub struct CapturedPlace<'tcx> {
     pub mutability: hir::Mutability,
 }
 
+impl CapturedPlace<'tcx> {
+    pub fn get_root_variable(&self) -> hir::HirId {
+        match self.place.base {
+            HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
+            base => bug!("Expected upvar, found={:?}", base),
+        }
+    }
+}
+
 pub fn place_to_string_for_capture(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String {
     let name = match place.base {
         HirPlaceBase::Upvar(upvar_id) => tcx.hir().name(upvar_id.var_path.hir_id).to_string(),
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs
index 350e0d045fa35..fb7694b7d88e9 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs
@@ -345,7 +345,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 };
 
                 let upvar = &self.upvars[upvar_field.unwrap().index()];
-                let upvar_hir_id = upvar.var_hir_id;
+                // FIXME(project-rfc-2229#8): Improve borrow-check diagnostics in case of precise
+                //                            capture.
+                let upvar_hir_id = upvar.place.get_root_variable();
                 let upvar_name = upvar.name;
                 let upvar_span = self.infcx.tcx.hir().span(upvar_hir_id);
 
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs
index 73196c732f5bb..74abe2d35ee74 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs
@@ -64,12 +64,29 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
                 ));
 
-                item_msg = format!("`{}`", access_place_desc.unwrap());
-                if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
-                    reason = ", as it is not declared as mutable".to_string();
+                let imm_borrow_derefed = self.upvars[upvar_index.index()]
+                    .place
+                    .place
+                    .deref_tys()
+                    .any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)));
+
+                // If the place is immutable then:
+                //
+                // - Either we deref a immutable ref to get to our final place.
+                //    - We don't capture derefs of raw ptrs
+                // - Or the final place is immut because the root variable of the capture
+                //   isn't marked mut and we should suggest that to the user.
+                if imm_borrow_derefed {
+                    // If we deref an immutable ref then the suggestion here doesn't help.
+                    return;
                 } else {
-                    let name = self.upvars[upvar_index.index()].name;
-                    reason = format!(", as `{}` is not declared as mutable", name);
+                    item_msg = format!("`{}`", access_place_desc.unwrap());
+                    if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
+                        reason = ", as it is not declared as mutable".to_string();
+                    } else {
+                        let name = self.upvars[upvar_index.index()].name;
+                        reason = format!(", as `{}` is not declared as mutable", name);
+                    }
                 }
             }
 
@@ -259,9 +276,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
                 ));
 
+                let captured_place = &self.upvars[upvar_index.index()].place;
+
                 err.span_label(span, format!("cannot {ACT}", ACT = act));
 
-                let upvar_hir_id = self.upvars[upvar_index.index()].var_hir_id;
+                let upvar_hir_id = captured_place.get_root_variable();
+
                 if let Some(Node::Binding(pat)) = self.infcx.tcx.hir().find(upvar_hir_id) {
                     if let hir::PatKind::Binding(
                         hir::BindingAnnotation::Unannotated,
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs
index a850b85e9bbae..4abc623fc5f37 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/var_name.rs
@@ -12,7 +12,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         tcx: TyCtxt<'tcx>,
         body: &Body<'tcx>,
         local_names: &IndexVec<Local, Option<Symbol>>,
-        upvars: &[Upvar],
+        upvars: &[Upvar<'tcx>],
         fr: RegionVid,
     ) -> Option<(Option<Symbol>, Span)> {
         debug!("get_var_name_and_span_for_region(fr={:?})", fr);
@@ -21,6 +21,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         debug!("get_var_name_and_span_for_region: attempting upvar");
         self.get_upvar_index_for_region(tcx, fr)
             .map(|index| {
+                // FIXME(project-rfc-2229#8): Use place span for diagnostics
                 let (name, span) = self.get_upvar_name_and_span_for_region(tcx, upvars, index);
                 (Some(name), span)
             })
@@ -59,10 +60,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     crate fn get_upvar_name_and_span_for_region(
         &self,
         tcx: TyCtxt<'tcx>,
-        upvars: &[Upvar],
+        upvars: &[Upvar<'tcx>],
         upvar_index: usize,
     ) -> (Symbol, Span) {
-        let upvar_hir_id = upvars[upvar_index].var_hir_id;
+        let upvar_hir_id = upvars[upvar_index].place.get_root_variable();
         debug!("get_upvar_name_and_span_for_region: upvar_hir_id={:?}", upvar_hir_id);
 
         let upvar_name = tcx.hir().name(upvar_hir_id);
diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs
index 6e1e5c65aea6d..b51cba15d32a7 100644
--- a/compiler/rustc_mir/src/borrow_check/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/mod.rs
@@ -5,11 +5,10 @@ use rustc_data_structures::graph::dominators::Dominators;
 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
-use rustc_hir::{HirId, Node};
+use rustc_hir::Node;
 use rustc_index::bit_set::BitSet;
 use rustc_index::vec::IndexVec;
 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
-use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
 use rustc_middle::mir::{
     traversal, Body, ClearCrossCrate, Local, Location, Mutability, Operand, Place, PlaceElem,
     PlaceRef, VarDebugInfoContents,
@@ -18,7 +17,7 @@ use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind
 use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
 use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
 use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt};
+use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt};
 use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT};
 use rustc_span::{Span, Symbol, DUMMY_SP};
 
@@ -73,16 +72,15 @@ crate use region_infer::RegionInferenceContext;
 
 // FIXME(eddyb) perhaps move this somewhere more centrally.
 #[derive(Debug)]
-crate struct Upvar {
+crate struct Upvar<'tcx> {
+    // FIXME(project-rfc-2229#8): ty::CapturePlace should have a to_string(), or similar
+    //                           then this should not be needed.
     name: Symbol,
 
-    // FIXME(project-rfc-2229#8): This should use Place or something similar
-    var_hir_id: HirId,
+    place: CapturedPlace<'tcx>,
 
     /// If true, the capture is behind a reference.
     by_ref: bool,
-
-    mutability: Mutability,
 }
 
 const DEREF_PROJECTION: &[PlaceElem<'_>; 1] = &[ProjectionElem::Deref];
@@ -161,21 +159,13 @@ fn do_mir_borrowck<'a, 'tcx>(
     let upvars: Vec<_> = tables
         .closure_min_captures_flattened(def.did.to_def_id())
         .map(|captured_place| {
-            let var_hir_id = match captured_place.place.base {
-                HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
-                _ => bug!("Expected upvar"),
-            };
+            let var_hir_id = captured_place.get_root_variable();
             let capture = captured_place.info.capture_kind;
             let by_ref = match capture {
                 ty::UpvarCapture::ByValue(_) => false,
                 ty::UpvarCapture::ByRef(..) => true,
             };
-            Upvar {
-                name: tcx.hir().name(var_hir_id),
-                var_hir_id,
-                by_ref,
-                mutability: captured_place.mutability,
-            }
+            Upvar { name: tcx.hir().name(var_hir_id), place: captured_place.clone(), by_ref }
         })
         .collect();
 
@@ -544,7 +534,7 @@ crate struct MirBorrowckCtxt<'cx, 'tcx> {
     dominators: Dominators<BasicBlock>,
 
     /// Information about upvars not necessarily preserved in types or MIR
-    upvars: Vec<Upvar>,
+    upvars: Vec<Upvar<'tcx>>,
 
     /// Names of local (user) variables (extracted from `var_debug_info`).
     local_names: IndexVec<Local, Option<Symbol>>,
@@ -2251,7 +2241,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                                  place={:?}",
                                 upvar, is_local_mutation_allowed, place
                             );
-                            match (upvar.mutability, is_local_mutation_allowed) {
+                            match (upvar.place.mutability, is_local_mutation_allowed) {
                                 (
                                     Mutability::Not,
                                     LocalMutationIsAllowed::No
diff --git a/compiler/rustc_mir/src/borrow_check/nll.rs b/compiler/rustc_mir/src/borrow_check/nll.rs
index 359c5f261a434..a0265b20d127b 100644
--- a/compiler/rustc_mir/src/borrow_check/nll.rs
+++ b/compiler/rustc_mir/src/borrow_check/nll.rs
@@ -165,7 +165,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
     flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>,
     move_data: &MoveData<'tcx>,
     borrow_set: &BorrowSet<'tcx>,
-    upvars: &[Upvar],
+    upvars: &[Upvar<'tcx>],
 ) -> NllOutput<'tcx> {
     let mut all_facts = AllFacts::enabled(infcx.tcx).then_some(AllFacts::default());
 
diff --git a/compiler/rustc_mir/src/borrow_check/path_utils.rs b/compiler/rustc_mir/src/borrow_check/path_utils.rs
index fa3ae2367e08e..80de3b4e363bf 100644
--- a/compiler/rustc_mir/src/borrow_check/path_utils.rs
+++ b/compiler/rustc_mir/src/borrow_check/path_utils.rs
@@ -143,7 +143,7 @@ pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool {
 /// of a closure type.
 pub(crate) fn is_upvar_field_projection(
     tcx: TyCtxt<'tcx>,
-    upvars: &[Upvar],
+    upvars: &[Upvar<'tcx>],
     place_ref: PlaceRef<'tcx>,
     body: &Body<'tcx>,
 ) -> Option<Field> {
diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs
index fb9820e853f8f..24bbd2b8c49c1 100644
--- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs
@@ -132,7 +132,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
     flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
     move_data: &MoveData<'tcx>,
     elements: &Rc<RegionValueElements>,
-    upvars: &[Upvar],
+    upvars: &[Upvar<'tcx>],
 ) -> MirTypeckResults<'tcx> {
     let implicit_region_bound = infcx.tcx.mk_region(ty::ReVar(universal_regions.fr_fn_body));
     let mut constraints = MirTypeckRegionConstraints {
@@ -821,7 +821,7 @@ struct BorrowCheckContext<'a, 'tcx> {
     all_facts: &'a mut Option<AllFacts>,
     borrow_set: &'a BorrowSet<'tcx>,
     constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
-    upvars: &'a [Upvar],
+    upvars: &'a [Upvar<'tcx>],
 }
 
 crate struct MirTypeckResults<'tcx> {
@@ -2490,7 +2490,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             body,
         );
         let category = if let Some(field) = field {
-            ConstraintCategory::ClosureUpvar(self.borrowck_context.upvars[field.index()].var_hir_id)
+            let var_hir_id = self.borrowck_context.upvars[field.index()].place.get_root_variable();
+            // FIXME(project-rfc-2229#8): Use Place for better diagnostics
+            ConstraintCategory::ClosureUpvar(var_hir_id)
         } else {
             ConstraintCategory::Boring
         };
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs
index dd018a0defa1b..1ea38e260b645 100644
--- a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs
@@ -14,7 +14,6 @@ fn main() {
     let mut c = || {
     //~^ ERROR: cannot borrow `z.0.0.0` as mutable, as it is behind a `&` reference
         z.0.0.0 = format!("X1");
-        //~^ ERROR: cannot assign to `z`, as it is not declared as mutable
     };
 
     c();
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr
index 948e2b731daf0..861bc44b78ded 100644
--- a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr
@@ -7,15 +7,6 @@ LL | #![feature(capture_disjoint_fields)]
    = note: `#[warn(incomplete_features)]` on by default
    = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
 
-error[E0594]: cannot assign to `z`, as it is not declared as mutable
-  --> $DIR/cant-mutate-imm-borrow.rs:16:9
-   |
-LL |     let z = (&mut y, "Z");
-   |         - help: consider changing this to be mutable: `mut z`
-...
-LL |         z.0.0.0 = format!("X1");
-   |         ^^^^^^^ cannot assign
-
 error[E0596]: cannot borrow `z.0.0.0` as mutable, as it is behind a `&` reference
   --> $DIR/cant-mutate-imm-borrow.rs:14:17
    |
@@ -25,7 +16,6 @@ LL |
 LL |         z.0.0.0 = format!("X1");
    |         - mutable borrow occurs due to use of `z.0.0.0` in closure
 
-error: aborting due to 2 previous errors; 1 warning emitted
+error: aborting due to previous error; 1 warning emitted
 
-Some errors have detailed explanations: E0594, E0596.
-For more information about an error, try `rustc --explain E0594`.
+For more information about this error, try `rustc --explain E0596`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs
new file mode 100644
index 0000000000000..997ecc7ddddf1
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs
@@ -0,0 +1,35 @@
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+
+// Ensure that diagnostics for mutability error (because the root variable
+// isn't mutable) work with `capture_disjoint_fields` enabled.
+
+fn mut_error_struct() {
+    let x = (10, 10);
+    let y = (x, 10);
+    let z = (y, 10);
+
+    let mut c = || {
+        z.0.0.0 = 20;
+        //~^ ERROR: cannot assign to `z`, as it is not declared as mutable
+    };
+
+    c();
+}
+
+fn mut_error_box() {
+    let x = (10, 10);
+    let bx = Box::new(x);
+
+    let mut c = || {
+        bx.0 = 20;
+        //~^ ERROR: cannot assign to `bx`, as it is not declared as mutable
+    };
+
+    c();
+}
+
+fn main() {
+    mut_error_struct();
+    mut_error_box();
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr
new file mode 100644
index 0000000000000..5e15635ac6e1b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr
@@ -0,0 +1,30 @@
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/cant-mutate-imm.rs:1:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error[E0594]: cannot assign to `z`, as it is not declared as mutable
+  --> $DIR/cant-mutate-imm.rs:13:9
+   |
+LL |     let z = (y, 10);
+   |         - help: consider changing this to be mutable: `mut z`
+...
+LL |         z.0.0.0 = 20;
+   |         ^^^^^^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `bx`, as it is not declared as mutable
+  --> $DIR/cant-mutate-imm.rs:25:9
+   |
+LL |     let bx = Box::new(x);
+   |         -- help: consider changing this to be mutable: `mut bx`
+...
+LL |         bx.0 = 20;
+   |         ^^^^^^^^^ cannot assign
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0594`.
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs
index 732c47298242a..676fde558dfbc 100644
--- a/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs
@@ -13,7 +13,6 @@ fn imm_mut_ref() {
     let c = || {
     //~^ ERROR: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference
         **ref_mref_x = y;
-        //~^ERROR: cannot assign to `ref_mref_x`, as it is not declared as mutable
     };
 
     c();
@@ -28,7 +27,6 @@ fn mut_imm_ref() {
     let c = || {
     //~^ ERROR: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference
         **mref_ref_x = y;
-        //~^ERROR: cannot assign to `mref_ref_x`, as it is not declared as mutable
     };
 
     c();
diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr
index 42b3c5090ac66..8cb2ed2235d55 100644
--- a/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr
+++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr
@@ -7,15 +7,6 @@ LL | #![feature(capture_disjoint_fields)]
    = note: `#[warn(incomplete_features)]` on by default
    = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
 
-error[E0594]: cannot assign to `ref_mref_x`, as it is not declared as mutable
-  --> $DIR/mut_ref.rs:15:9
-   |
-LL |     let ref_mref_x = &mref_x;
-   |         ---------- help: consider changing this to be mutable: `mut ref_mref_x`
-...
-LL |         **ref_mref_x = y;
-   |         ^^^^^^^^^^^^ cannot assign
-
 error[E0596]: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference
   --> $DIR/mut_ref.rs:13:13
    |
@@ -28,17 +19,8 @@ LL |
 LL |         **ref_mref_x = y;
    |           ---------- mutable borrow occurs due to use of `**ref_mref_x` in closure
 
-error[E0594]: cannot assign to `mref_ref_x`, as it is not declared as mutable
-  --> $DIR/mut_ref.rs:30:9
-   |
-LL |     let mref_ref_x = &mut ref_x;
-   |         ---------- help: consider changing this to be mutable: `mut mref_ref_x`
-...
-LL |         **mref_ref_x = y;
-   |         ^^^^^^^^^^^^ cannot assign
-
 error[E0596]: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference
-  --> $DIR/mut_ref.rs:28:13
+  --> $DIR/mut_ref.rs:27:13
    |
 LL |     let c = || {
    |             ^^ cannot borrow as mutable
@@ -46,7 +28,6 @@ LL |
 LL |         **mref_ref_x = y;
    |           ---------- mutable borrow occurs due to use of `**mref_ref_x` in closure
 
-error: aborting due to 4 previous errors; 1 warning emitted
+error: aborting due to 2 previous errors; 1 warning emitted
 
-Some errors have detailed explanations: E0594, E0596.
-For more information about an error, try `rustc --explain E0594`.
+For more information about this error, try `rustc --explain E0596`.

From ffd53277dc776dcf93bfa34c478bd99da2361515 Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Tue, 12 Jan 2021 14:54:12 -0500
Subject: [PATCH 17/24] Add fixme for precise path diagnostics

---
 compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs |  2 ++
 compiler/rustc_mir/src/borrow_check/mod.rs             | 10 +++++-----
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
index 6d98bf554f1cf..04ea3cbd8b66d 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs
@@ -215,6 +215,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             PlaceRef { local, projection: [proj_base @ .., elem] } => {
                 match elem {
                     ProjectionElem::Deref => {
+                        // FIXME(project-rfc_2229#36): print capture precisely here.
                         let upvar_field_projection = self.is_upvar_field_projection(place);
                         if let Some(field) = upvar_field_projection {
                             let var_index = field.index();
@@ -259,6 +260,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     ProjectionElem::Field(field, _ty) => {
                         autoderef = true;
 
+                        // FIXME(project-rfc_2229#36): print capture precisely here.
                         let upvar_field_projection = self.is_upvar_field_projection(place);
                         if let Some(field) = upvar_field_projection {
                             let var_index = field.index();
diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs
index b51cba15d32a7..c42e271f40e45 100644
--- a/compiler/rustc_mir/src/borrow_check/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/mod.rs
@@ -73,8 +73,7 @@ crate use region_infer::RegionInferenceContext;
 // FIXME(eddyb) perhaps move this somewhere more centrally.
 #[derive(Debug)]
 crate struct Upvar<'tcx> {
-    // FIXME(project-rfc-2229#8): ty::CapturePlace should have a to_string(), or similar
-    //                           then this should not be needed.
+    // FIXME(project-rfc_2229#36): print capture precisely here.
     name: Symbol,
 
     place: CapturedPlace<'tcx>,
@@ -2156,6 +2155,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         place: PlaceRef<'tcx>,
         is_local_mutation_allowed: LocalMutationIsAllowed,
     ) -> Result<RootPlace<'tcx>, PlaceRef<'tcx>> {
+        debug!("is_mutable: place={:?}, is_local...={:?}", place, is_local_mutation_allowed);
         match place.last_projection() {
             None => {
                 let local = &self.body.local_decls[place.local];
@@ -2237,9 +2237,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         if let Some(field) = upvar_field_projection {
                             let upvar = &self.upvars[field.index()];
                             debug!(
-                                "upvar.mutability={:?} local_mutation_is_allowed={:?} \
-                                 place={:?}",
-                                upvar, is_local_mutation_allowed, place
+                                "is_mutable: upvar.mutability={:?} local_mutation_is_allowed={:?} \
+                                 place={:?}, place_base={:?}",
+                                upvar, is_local_mutation_allowed, place, place_base
                             );
                             match (upvar.place.mutability, is_local_mutation_allowed) {
                                 (

From fadf03ee1bbe238ff5a46e60550a70470da492af Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Fri, 29 Jan 2021 16:01:27 -0500
Subject: [PATCH 18/24] Fix typos

---
 compiler/rustc_middle/src/ty/mod.rs        | 2 ++
 compiler/rustc_mir/src/borrow_check/mod.rs | 4 ++--
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 7681041863e9a..babab005edb2b 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -675,6 +675,8 @@ pub struct CapturedPlace<'tcx> {
 }
 
 impl CapturedPlace<'tcx> {
+    /// Returns the hir-id of the root variable for the captured place.
+    /// e.g., if `a.b.c` was captured, would return the hir-id for `a`.
     pub fn get_root_variable(&self) -> hir::HirId {
         match self.place.base {
             HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs
index c42e271f40e45..5db52db70ac68 100644
--- a/compiler/rustc_mir/src/borrow_check/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/mod.rs
@@ -1358,10 +1358,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     fn propagate_closure_used_mut_upvar(&mut self, operand: &Operand<'tcx>) {
         let propagate_closure_used_mut_place = |this: &mut Self, place: Place<'tcx>| {
-            // We have three possiblities here:
+            // We have three possibilities here:
             // a. We are modifying something through a mut-ref
             // b. We are modifying something that is local to our parent
-            // c. Current body is a nested clsoure, and we are modifying path starting from
+            // c. Current body is a nested closure, and we are modifying path starting from
             //    a Place captured by our parent closure.
 
             // Handle (c), the path being modified is exactly the path captured by our parent

From 0f4bab246b341b5d67ba1c01d81c4822f96dd878 Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Fri, 29 Jan 2021 16:25:01 -0500
Subject: [PATCH 19/24] Fixme for closure origin when reborrow is implemented

---
 compiler/rustc_typeck/src/check/upvar.rs | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index 38330d5a49a94..f039445bf7780 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -184,10 +184,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let origin = if self.tcx.features().capture_disjoint_fields {
                     origin
                 } else {
-                    // FIXME(project-rfc-2229#26): Once rust-lang#80092 is merged, we should restrict the
-                    // precision of origin as well. Otherwise, this will cause issues when project-rfc-2229#26
-                    // is fixed as we might see Index projections in the origin, which we can't print because
-                    // we don't store enough information.
+                    // FIXME(project-rfc-2229#31): Once the changes to support reborrowing are
+                    //                             made, make sure we are selecting and restricting
+                    //                             the origin correctly.
                     (origin.0, Place { projections: vec![], ..origin.1 })
                 };
 

From 5a3b85c0c9290d7b31b0c746fce1dd6bf253f16d Mon Sep 17 00:00:00 2001
From: Aman Arora <me@aman-arora.com>
Date: Sat, 30 Jan 2021 15:32:05 -0500
Subject: [PATCH 20/24] Remove changes to Cargo.lock

---
 Cargo.lock | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Cargo.lock b/Cargo.lock
index e7365e611cae7..a4ba7704426e8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5388,7 +5388,7 @@ dependencies = [
  "chrono",
  "lazy_static",
  "matchers",
- "parking_lot 0.9.0",
+ "parking_lot 0.11.0",
  "regex",
  "serde",
  "serde_json",

From 9946b54823473bae055f1d7833f9f903e9738326 Mon Sep 17 00:00:00 2001
From: b-naber <bn263@gmx.de>
Date: Thu, 28 Jan 2021 18:26:31 +0100
Subject: [PATCH 21/24] add suggestion for nested fields

---
 compiler/rustc_typeck/src/check/expr.rs       | 120 ++++++++++++++++--
 ...eld-present-in-subfield-recursion-limit.rs |  43 +++++++
 ...present-in-subfield-recursion-limit.stderr |  11 ++
 ...n-existent-field-present-in-subfield.fixed |  42 ++++++
 .../non-existent-field-present-in-subfield.rs |  42 ++++++
 ...-existent-field-present-in-subfield.stderr |  27 ++++
 6 files changed, 275 insertions(+), 10 deletions(-)
 create mode 100644 src/test/ui/suggestions/non-existent-field-present-in-subfield-recursion-limit.rs
 create mode 100644 src/test/ui/suggestions/non-existent-field-present-in-subfield-recursion-limit.stderr
 create mode 100644 src/test/ui/suggestions/non-existent-field-present-in-subfield.fixed
 create mode 100644 src/test/ui/suggestions/non-existent-field-present-in-subfield.rs
 create mode 100644 src/test/ui/suggestions/non-existent-field-present-in-subfield.stderr

diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 8aa6c6d924a53..f489d6c64ea57 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -36,6 +36,7 @@ use rustc_infer::infer;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_middle::ty;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
+use rustc_middle::ty::subst::SubstsRef;
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::TypeFoldable;
 use rustc_middle::ty::{AdtKind, Visibility};
@@ -46,8 +47,6 @@ use rustc_span::source_map::Span;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_trait_selection::traits::{self, ObligationCauseCode};
 
-use std::fmt::Display;
-
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn check_expr_eq_type(&self, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>) {
         let ty = self.check_expr_with_hint(expr, expected);
@@ -1585,11 +1584,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         base: &'tcx hir::Expr<'tcx>,
         field: Ident,
     ) -> Ty<'tcx> {
+        debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
         let expr_t = self.check_expr(base);
         let expr_t = self.structurally_resolved_type(base.span, expr_t);
         let mut private_candidate = None;
         let mut autoderef = self.autoderef(expr.span, expr_t);
         while let Some((base_t, _)) = autoderef.next() {
+            debug!("base_t: {:?}", base_t);
             match base_t.kind() {
                 ty::Adt(base_def, substs) if !base_def.is_enum() => {
                     debug!("struct named {:?}", base_t);
@@ -1706,7 +1707,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}",
             field, base, expr, expr_t
         );
-        let mut err = self.no_such_field_err(field.span, field, expr_t);
+        let mut err = self.no_such_field_err(field, expr_t);
 
         match *expr_t.peel_refs().kind() {
             ty::Array(_, len) => {
@@ -1880,21 +1881,120 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    fn no_such_field_err<T: Display>(
+    fn no_such_field_err(
         &self,
-        span: Span,
-        field: T,
-        expr_t: &ty::TyS<'_>,
+        field: Ident,
+        expr_t: &'tcx ty::TyS<'tcx>,
     ) -> DiagnosticBuilder<'_> {
-        type_error_struct!(
+        let span = field.span;
+        debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, expr_t);
+
+        let mut err = type_error_struct!(
             self.tcx().sess,
-            span,
+            field.span,
             expr_t,
             E0609,
             "no field `{}` on type `{}`",
             field,
             expr_t
-        )
+        );
+
+        // try to add a suggestion in case the field is a nested field of a field of the Adt
+        if let Some((fields, substs)) = self.get_field_candidates(span, &expr_t) {
+            for candidate_field in fields.iter() {
+                if let Some(field_path) =
+                    self.check_for_nested_field(span, field, candidate_field, substs, vec![])
+                {
+                    let field_path_str = field_path
+                        .iter()
+                        .map(|id| id.name.to_ident_string())
+                        .collect::<Vec<String>>()
+                        .join(".");
+                    debug!("field_path_str: {:?}", field_path_str);
+
+                    err.span_suggestion_verbose(
+                        field.span.shrink_to_lo(),
+                        "one of the expressions' fields has a field of the same name",
+                        format!("{}.", field_path_str),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        }
+        err
+    }
+
+    fn get_field_candidates(
+        &self,
+        span: Span,
+        base_t: Ty<'tcx>,
+    ) -> Option<(&Vec<ty::FieldDef>, SubstsRef<'tcx>)> {
+        debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_t);
+
+        let mut autoderef = self.autoderef(span, base_t);
+        while let Some((base_t, _)) = autoderef.next() {
+            match base_t.kind() {
+                ty::Adt(base_def, substs) if !base_def.is_enum() => {
+                    let fields = &base_def.non_enum_variant().fields;
+                    // For compile-time reasons put a limit on number of fields we search
+                    if fields.len() > 100 {
+                        return None;
+                    }
+                    return Some((fields, substs));
+                }
+                _ => {}
+            }
+        }
+        None
+    }
+
+    /// This method is called after we have encountered a missing field error to recursively
+    /// search for the field
+    fn check_for_nested_field(
+        &self,
+        span: Span,
+        target_field: Ident,
+        candidate_field: &ty::FieldDef,
+        subst: SubstsRef<'tcx>,
+        mut field_path: Vec<Ident>,
+    ) -> Option<Vec<Ident>> {
+        debug!(
+            "check_for_nested_field(span: {:?}, candidate_field: {:?}, field_path: {:?}",
+            span, candidate_field, field_path
+        );
+
+        if candidate_field.ident == target_field {
+            Some(field_path)
+        } else if field_path.len() > 3 {
+            // For compile-time reasons and to avoid infinite recursion we only check for fields
+            // up to a depth of three
+            None
+        } else {
+            // recursively search fields of `candidate_field` if it's a ty::Adt
+
+            field_path.push(candidate_field.ident.normalize_to_macros_2_0());
+            let field_ty = candidate_field.ty(self.tcx, subst);
+            if let Some((nested_fields, _)) = self.get_field_candidates(span, &field_ty) {
+                for field in nested_fields.iter() {
+                    let ident = field.ident.normalize_to_macros_2_0();
+                    if ident == target_field {
+                        return Some(field_path);
+                    } else {
+                        let field_path = field_path.clone();
+                        if let Some(path) = self.check_for_nested_field(
+                            span,
+                            target_field,
+                            field,
+                            subst,
+                            field_path,
+                        ) {
+                            return Some(path);
+                        }
+                    }
+                }
+            }
+            None
+        }
     }
 
     fn check_expr_index(
diff --git a/src/test/ui/suggestions/non-existent-field-present-in-subfield-recursion-limit.rs b/src/test/ui/suggestions/non-existent-field-present-in-subfield-recursion-limit.rs
new file mode 100644
index 0000000000000..98b408daa022d
--- /dev/null
+++ b/src/test/ui/suggestions/non-existent-field-present-in-subfield-recursion-limit.rs
@@ -0,0 +1,43 @@
+// In rustc_typeck::check::expr::no_such_field_err we recursively
+// look in subfields for the field. This recursive search is limited
+// in depth for compile-time reasons and to avoid infinite recursion
+// in case of cycles. This file tests that the limit in the recursion
+// depth is enforced.
+
+struct Foo {
+    first: Bar,
+    second: u32,
+    third: u32,
+}
+
+struct Bar {
+    bar: C,
+}
+
+struct C {
+    c: D,
+}
+
+struct D {
+    test: E,
+}
+
+struct E {
+    e: F,
+}
+
+struct F {
+    f: u32,
+}
+
+fn main() {
+    let f = F { f: 6 };
+    let e = E { e: f };
+    let d = D { test: e };
+    let c = C { c: d };
+    let bar = Bar { bar: c };
+    let fooer = Foo { first: bar, second: 4, third: 5 };
+
+    let test = fooer.f;
+    //~^ ERROR no field
+}
diff --git a/src/test/ui/suggestions/non-existent-field-present-in-subfield-recursion-limit.stderr b/src/test/ui/suggestions/non-existent-field-present-in-subfield-recursion-limit.stderr
new file mode 100644
index 0000000000000..b294f4da7db33
--- /dev/null
+++ b/src/test/ui/suggestions/non-existent-field-present-in-subfield-recursion-limit.stderr
@@ -0,0 +1,11 @@
+error[E0609]: no field `f` on type `Foo`
+  --> $DIR/non-existent-field-present-in-subfield-recursion-limit.rs:41:22
+   |
+LL |     let test = fooer.f;
+   |                      ^ unknown field
+   |
+   = note: available fields are: `first`, `second`, `third`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0609`.
diff --git a/src/test/ui/suggestions/non-existent-field-present-in-subfield.fixed b/src/test/ui/suggestions/non-existent-field-present-in-subfield.fixed
new file mode 100644
index 0000000000000..167548a89defa
--- /dev/null
+++ b/src/test/ui/suggestions/non-existent-field-present-in-subfield.fixed
@@ -0,0 +1,42 @@
+// run-rustfix
+
+struct Foo {
+    first: Bar,
+    _second: u32,
+    _third: u32,
+}
+
+struct Bar {
+    bar: C,
+}
+
+struct C {
+    c: D,
+}
+
+struct D {
+    test: E,
+}
+
+struct E {
+    _e: F,
+}
+
+struct F {
+    _f: u32,
+}
+
+fn main() {
+    let f = F { _f: 6 };
+    let e = E { _e: f };
+    let d = D { test: e };
+    let c = C { c: d };
+    let bar = Bar { bar: c };
+    let fooer = Foo { first: bar, _second: 4, _third: 5 };
+
+    let _test = &fooer.first.bar.c;
+    //~^ ERROR no field
+
+    let _test2 = fooer.first.bar.c.test;
+    //~^ ERROR no field
+}
diff --git a/src/test/ui/suggestions/non-existent-field-present-in-subfield.rs b/src/test/ui/suggestions/non-existent-field-present-in-subfield.rs
new file mode 100644
index 0000000000000..81cc1af4dff52
--- /dev/null
+++ b/src/test/ui/suggestions/non-existent-field-present-in-subfield.rs
@@ -0,0 +1,42 @@
+// run-rustfix
+
+struct Foo {
+    first: Bar,
+    _second: u32,
+    _third: u32,
+}
+
+struct Bar {
+    bar: C,
+}
+
+struct C {
+    c: D,
+}
+
+struct D {
+    test: E,
+}
+
+struct E {
+    _e: F,
+}
+
+struct F {
+    _f: u32,
+}
+
+fn main() {
+    let f = F { _f: 6 };
+    let e = E { _e: f };
+    let d = D { test: e };
+    let c = C { c: d };
+    let bar = Bar { bar: c };
+    let fooer = Foo { first: bar, _second: 4, _third: 5 };
+
+    let _test = &fooer.c;
+    //~^ ERROR no field
+
+    let _test2 = fooer.test;
+    //~^ ERROR no field
+}
diff --git a/src/test/ui/suggestions/non-existent-field-present-in-subfield.stderr b/src/test/ui/suggestions/non-existent-field-present-in-subfield.stderr
new file mode 100644
index 0000000000000..ddb7476ec6e34
--- /dev/null
+++ b/src/test/ui/suggestions/non-existent-field-present-in-subfield.stderr
@@ -0,0 +1,27 @@
+error[E0609]: no field `c` on type `Foo`
+  --> $DIR/non-existent-field-present-in-subfield.rs:37:24
+   |
+LL |     let _test = &fooer.c;
+   |                        ^ unknown field
+   |
+   = note: available fields are: `first`, `_second`, `_third`
+help: one of the expressions' fields has a field of the same name
+   |
+LL |     let _test = &fooer.first.bar.c;
+   |                        ^^^^^^^^^^
+
+error[E0609]: no field `test` on type `Foo`
+  --> $DIR/non-existent-field-present-in-subfield.rs:40:24
+   |
+LL |     let _test2 = fooer.test;
+   |                        ^^^^ unknown field
+   |
+   = note: available fields are: `first`, `_second`, `_third`
+help: one of the expressions' fields has a field of the same name
+   |
+LL |     let _test2 = fooer.first.bar.c.test;
+   |                        ^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0609`.

From 6946534d84161353b56157f65f8cdcdfb93df749 Mon Sep 17 00:00:00 2001
From: kadmin <julianknodt@gmail.com>
Date: Tue, 26 Jan 2021 22:49:30 +0000
Subject: [PATCH 22/24] Remove const_in_array_rep_expr

---
 compiler/rustc_feature/src/active.rs          |  3 -
 compiler/rustc_feature/src/removed.rs         |  3 +
 compiler/rustc_middle/src/traits/mod.rs       |  3 +-
 .../src/borrow_check/type_check/mod.rs        | 15 +----
 .../src/transform/check_consts/qualifs.rs     |  3 +-
 .../rustc_mir/src/transform/promote_consts.rs | 66 +------------------
 .../src/traits/error_reporting/suggestions.rs | 15 +----
 library/alloc/src/lib.rs                      |  1 -
 .../const-in-array-repeat-expressions.md      | 11 ----
 .../repeat_empty_ok.rs}                       |  0
 .../repeat_empty_ok.stderr}                   |  8 +--
 .../const-repeat.rs                           |  0
 .../fn-call-in-const.rs                       |  6 +-
 .../fn-call-in-non-const.rs                   |  2 -
 .../fn-call-in-non-const.stderr               |  2 +-
 .../migrate-fail.rs                           |  1 -
 .../migrate-fail.stderr                       |  4 +-
 .../migrate-pass.rs                           |  1 -
 .../nll-fail.rs                               |  1 -
 .../nll-fail.stderr                           |  4 +-
 .../nll-pass.rs                               |  1 -
 .../run-pass.rs                               |  1 -
 .../trait-error.rs                            |  2 -
 .../trait-error.stderr                        |  2 +-
 ...-gate-const_in_array_repeat_expressions.rs | 17 -----
 ...e-const_in_array_repeat_expressions.stderr | 25 -------
 src/test/ui/unused/unused-closure.rs          |  5 --
 src/test/ui/unused/unused-closure.stderr      | 28 +++-----
 28 files changed, 31 insertions(+), 199 deletions(-)
 delete mode 100644 src/doc/unstable-book/src/language-features/const-in-array-repeat-expressions.md
 rename src/test/ui/{issues/issue-80371.rs => array-slice-vec/repeat_empty_ok.rs} (100%)
 rename src/test/ui/{issues/issue-80371.stderr => array-slice-vec/repeat_empty_ok.stderr} (56%)
 rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/const-repeat.rs (100%)
 rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/fn-call-in-const.rs (73%)
 rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/fn-call-in-non-const.rs (85%)
 rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/fn-call-in-non-const.stderr (92%)
 rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/migrate-fail.rs (92%)
 rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/migrate-fail.stderr (93%)
 rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/migrate-pass.rs (98%)
 rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/nll-fail.rs (91%)
 rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/nll-fail.stderr (93%)
 rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/nll-pass.rs (98%)
 rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/run-pass.rs (81%)
 rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/trait-error.rs (77%)
 rename src/test/ui/consts/{rfc-2203-const-array-repeat-exprs => const-blocks}/trait-error.stderr (94%)
 delete mode 100644 src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs
 delete mode 100644 src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr

diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index cd3c8fded633f..e12b533b110d2 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -485,9 +485,6 @@ declare_features! (
     /// Allows `async || body` closures.
     (active, async_closure, "1.37.0", Some(62290), None),
 
-    /// Allows `[x; N]` where `x` is a constant (RFC 2203).
-    (active, const_in_array_repeat_expressions, "1.37.0", Some(49147), None),
-
     /// Allows `impl Trait` to be used inside type aliases (RFC 2515).
     (active, type_alias_impl_trait, "1.38.0", Some(63063), None),
 
diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs
index 07bd1602cda32..38a3a4e3d4411 100644
--- a/compiler/rustc_feature/src/removed.rs
+++ b/compiler/rustc_feature/src/removed.rs
@@ -97,6 +97,9 @@ declare_features! (
     (removed, extern_in_paths, "1.33.0", Some(55600), None,
      Some("subsumed by `::foo::bar` paths")),
     (removed, quote, "1.33.0", Some(29601), None, None),
+    /// Allows `[x; N]` where `x` is a constant (RFC 2203).
+    (removed, const_in_array_repeat_expressions,  "1.37.0", Some(49147), None,
+     Some("removed due to causing promotable bugs")),
     /// Allows using `#[unsafe_destructor_blind_to_params]` (RFC 1238).
     (removed, dropck_parametricity, "1.38.0", Some(28498), None, None),
     (removed, await_macro, "1.38.0", Some(50547), None,
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 0a663f793aa75..163b400973b7c 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -228,8 +228,7 @@ pub enum ObligationCauseCode<'tcx> {
     /// Inline asm operand type must be `Sized`.
     InlineAsmSized,
     /// `[T, ..n]` implies that `T` must be `Copy`.
-    /// If `true`, suggest `const_in_array_repeat_expressions` feature flag.
-    RepeatVec(bool),
+    RepeatVec,
 
     /// Types of fields (other than the last, except for packed structs) in a struct must be sized.
     FieldSized {
diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs
index fb9820e853f8f..8de8b32bd64af 100644
--- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs
+++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs
@@ -43,10 +43,6 @@ use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligations}
 use crate::dataflow::impls::MaybeInitializedPlaces;
 use crate::dataflow::move_paths::MoveData;
 use crate::dataflow::ResultsCursor;
-use crate::transform::{
-    check_consts::ConstCx,
-    promote_consts::should_suggest_const_in_array_repeat_expressions_attribute,
-};
 
 use crate::borrow_check::{
     borrow_set::BorrowSet,
@@ -1997,22 +1993,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                             let span = body.source_info(location).span;
                             let ty = operand.ty(body, tcx);
                             if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) {
-                                let ccx = ConstCx::new_with_param_env(tcx, body, self.param_env);
-                                // To determine if `const_in_array_repeat_expressions` feature gate should
-                                // be mentioned, need to check if the rvalue is promotable.
-                                let should_suggest =
-                                    should_suggest_const_in_array_repeat_expressions_attribute(
-                                        &ccx, operand,
-                                    );
-                                debug!("check_rvalue: should_suggest={:?}", should_suggest);
-
                                 let def_id = body.source.def_id().expect_local();
                                 self.infcx.report_selection_error(
                                     &traits::Obligation::new(
                                         ObligationCause::new(
                                             span,
                                             self.tcx().hir().local_def_id_to_hir_id(def_id),
-                                            traits::ObligationCauseCode::RepeatVec(should_suggest),
+                                            traits::ObligationCauseCode::RepeatVec,
                                         ),
                                         self.param_env,
                                         ty::Binder::bind(ty::TraitRef::new(
diff --git a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs
index 4d159ed340380..0ce1980f10a11 100644
--- a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs
@@ -246,7 +246,8 @@ where
     };
 
     // Check the qualifs of the value of `const` items.
-    if let ty::ConstKind::Unevaluated(def, _, None) = constant.literal.val {
+    if let ty::ConstKind::Unevaluated(def, _, promoted) = constant.literal.val {
+        assert!(promoted.is_none());
         // Don't peek inside trait associated constants.
         if cx.tcx.trait_of_item(def.did).is_none() {
             let qualifs = if let Some((did, param_did)) = def.as_const_arg() {
diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs
index d8758e045443c..b4504a0e223f6 100644
--- a/compiler/rustc_mir/src/transform/promote_consts.rs
+++ b/compiler/rustc_mir/src/transform/promote_consts.rs
@@ -102,9 +102,6 @@ pub enum Candidate {
     /// Borrow of a constant temporary, candidate for lifetime extension.
     Ref(Location),
 
-    /// Promotion of the `x` in `[x; 32]`.
-    Repeat(Location),
-
     /// Currently applied to function calls where the callee has the unstable
     /// `#[rustc_args_required_const]` attribute as well as the SIMD shuffle
     /// intrinsic. The intrinsic requires the arguments are indeed constant and
@@ -120,14 +117,14 @@ impl Candidate {
     /// Returns `true` if we should use the "explicit" rules for promotability for this `Candidate`.
     fn forces_explicit_promotion(&self) -> bool {
         match self {
-            Candidate::Ref(_) | Candidate::Repeat(_) => false,
+            Candidate::Ref(_) => false,
             Candidate::Argument { .. } | Candidate::InlineAsm { .. } => true,
         }
     }
 
     fn source_info(&self, body: &Body<'_>) -> SourceInfo {
         match self {
-            Candidate::Ref(location) | Candidate::Repeat(location) => *body.source_info(*location),
+            Candidate::Ref(location) => *body.source_info(*location),
             Candidate::Argument { bb, .. } | Candidate::InlineAsm { bb, .. } => {
                 *body.source_info(body.terminator_loc(*bb))
             }
@@ -213,11 +210,6 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
             Rvalue::Ref(..) => {
                 self.candidates.push(Candidate::Ref(location));
             }
-            Rvalue::Repeat(..) if self.ccx.tcx.features().const_in_array_repeat_expressions => {
-                // FIXME(#49147) only promote the element when it isn't `Copy`
-                // (so that code that can copy it at runtime is unaffected).
-                self.candidates.push(Candidate::Repeat(location));
-            }
             _ => {}
         }
     }
@@ -334,21 +326,6 @@ impl<'tcx> Validator<'_, 'tcx> {
                     _ => bug!(),
                 }
             }
-            Candidate::Repeat(loc) => {
-                assert!(!self.explicit);
-
-                let statement = &self.body[loc.block].statements[loc.statement_index];
-                match &statement.kind {
-                    StatementKind::Assign(box (_, Rvalue::Repeat(ref operand, _))) => {
-                        if !self.tcx.features().const_in_array_repeat_expressions {
-                            return Err(Unpromotable);
-                        }
-
-                        self.validate_operand(operand)
-                    }
-                    _ => bug!(),
-                }
-            }
             Candidate::Argument { bb, index } => {
                 assert!(self.explicit);
 
@@ -1090,18 +1067,6 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                         _ => bug!(),
                     }
                 }
-                Candidate::Repeat(loc) => {
-                    let statement = &mut blocks[loc.block].statements[loc.statement_index];
-                    match statement.kind {
-                        StatementKind::Assign(box (_, Rvalue::Repeat(ref mut operand, _))) => {
-                            let ty = operand.ty(local_decls, self.tcx);
-                            let span = statement.source_info.span;
-
-                            Rvalue::Use(mem::replace(operand, promoted_operand(ty, span)))
-                        }
-                        _ => bug!(),
-                    }
-                }
                 Candidate::Argument { bb, index } => {
                     let terminator = blocks[bb].terminator_mut();
                     match terminator.kind {
@@ -1182,8 +1147,7 @@ pub fn promote_candidates<'tcx>(
     let mut extra_statements = vec![];
     for candidate in candidates.into_iter().rev() {
         match candidate {
-            Candidate::Repeat(Location { block, statement_index })
-            | Candidate::Ref(Location { block, statement_index }) => {
+            Candidate::Ref(Location { block, statement_index }) => {
                 if let StatementKind::Assign(box (place, _)) =
                     &body[block].statements[statement_index].kind
                 {
@@ -1267,27 +1231,3 @@ pub fn promote_candidates<'tcx>(
 
     promotions
 }
-
-/// This function returns `true` if the `const_in_array_repeat_expressions` feature attribute should
-/// be suggested. This function is probably quite expensive, it shouldn't be run in the happy path.
-/// Feature attribute should be suggested if `operand` can be promoted and the feature is not
-/// enabled.
-crate fn should_suggest_const_in_array_repeat_expressions_attribute<'tcx>(
-    ccx: &ConstCx<'_, 'tcx>,
-    operand: &Operand<'tcx>,
-) -> bool {
-    let mut rpo = traversal::reverse_postorder(&ccx.body);
-    let (temps, _) = collect_temps_and_candidates(&ccx, &mut rpo);
-    let validator = Validator { ccx, temps: &temps, explicit: false };
-
-    let should_promote = validator.validate_operand(operand).is_ok();
-    let feature_flag = validator.ccx.tcx.features().const_in_array_repeat_expressions;
-    debug!(
-        "should_suggest_const_in_array_repeat_expressions_flag: def_id={:?} \
-            should_promote={:?} feature_flag={:?}",
-        validator.ccx.def_id(),
-        should_promote,
-        feature_flag
-    );
-    should_promote && !feature_flag
-}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 0724a9290e91c..690591930deb9 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -1881,23 +1881,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             ObligationCauseCode::Coercion { source: _, target } => {
                 err.note(&format!("required by cast to type `{}`", self.ty_to_string(target)));
             }
-            ObligationCauseCode::RepeatVec(suggest_const_in_array_repeat_expressions) => {
+            ObligationCauseCode::RepeatVec => {
                 err.note(
                     "the `Copy` trait is required because the repeated element will be copied",
                 );
-                if suggest_const_in_array_repeat_expressions {
-                    err.note(
-                        "this array initializer can be evaluated at compile-time, see issue \
-                         #49147 <https://github.com/rust-lang/rust/issues/49147> \
-                         for more information",
-                    );
-                    if tcx.sess.opts.unstable_features.is_nightly_build() {
-                        err.help(
-                            "add `#![feature(const_in_array_repeat_expressions)]` to the \
-                             crate attributes to enable",
-                        );
-                    }
-                }
             }
             ObligationCauseCode::VariableType(hir_id) => {
                 let parent_node = self.tcx.hir().get_parent_node(hir_id);
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index d7ae353282e79..d8f774f723c85 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -89,7 +89,6 @@
 #![feature(coerce_unsized)]
 #![feature(const_btree_new)]
 #![feature(const_fn)]
-#![feature(const_in_array_repeat_expressions)]
 #![feature(cow_is_borrowed)]
 #![feature(const_cow_is_borrowed)]
 #![feature(dispatch_from_dyn)]
diff --git a/src/doc/unstable-book/src/language-features/const-in-array-repeat-expressions.md b/src/doc/unstable-book/src/language-features/const-in-array-repeat-expressions.md
deleted file mode 100644
index 940916944bdaa..0000000000000
--- a/src/doc/unstable-book/src/language-features/const-in-array-repeat-expressions.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# `const_in_array_repeat_expressions`
-
-The tracking issue for this feature is: [#49147]
-
-[#49147]: https://github.com/rust-lang/rust/issues/49147
-
-------------------------
-
-Relaxes the rules for repeat expressions, `[x; N]` such that `x` may also be `const` (strictly
-speaking rvalue promotable), in addition to `typeof(x): Copy`. The result of `[x; N]` where `x` is
-`const` is itself also `const`.
diff --git a/src/test/ui/issues/issue-80371.rs b/src/test/ui/array-slice-vec/repeat_empty_ok.rs
similarity index 100%
rename from src/test/ui/issues/issue-80371.rs
rename to src/test/ui/array-slice-vec/repeat_empty_ok.rs
diff --git a/src/test/ui/issues/issue-80371.stderr b/src/test/ui/array-slice-vec/repeat_empty_ok.stderr
similarity index 56%
rename from src/test/ui/issues/issue-80371.stderr
rename to src/test/ui/array-slice-vec/repeat_empty_ok.stderr
index 5e2052abba7c6..85baa1268bf04 100644
--- a/src/test/ui/issues/issue-80371.stderr
+++ b/src/test/ui/array-slice-vec/repeat_empty_ok.stderr
@@ -1,22 +1,18 @@
 error[E0277]: the trait bound `Header<'_>: Copy` is not satisfied
-  --> $DIR/issue-80371.rs:8:19
+  --> $DIR/repeat_empty_ok.rs:8:19
    |
 LL |     let headers = [Header{value: &[]}; 128];
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>`
    |
    = note: the `Copy` trait is required because the repeated element will be copied
-   = note: this array initializer can be evaluated at compile-time, see issue #49147 <https://github.com/rust-lang/rust/issues/49147> for more information
-   = help: add `#![feature(const_in_array_repeat_expressions)]` to the crate attributes to enable
 
 error[E0277]: the trait bound `Header<'_>: Copy` is not satisfied
-  --> $DIR/issue-80371.rs:13:19
+  --> $DIR/repeat_empty_ok.rs:13:19
    |
 LL |     let headers = [Header{value: &[0]}; 128];
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>`
    |
    = note: the `Copy` trait is required because the repeated element will be copied
-   = note: this array initializer can be evaluated at compile-time, see issue #49147 <https://github.com/rust-lang/rust/issues/49147> for more information
-   = help: add `#![feature(const_in_array_repeat_expressions)]` to the crate attributes to enable
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/const-repeat.rs b/src/test/ui/consts/const-blocks/const-repeat.rs
similarity index 100%
rename from src/test/ui/consts/rfc-2203-const-array-repeat-exprs/const-repeat.rs
rename to src/test/ui/consts/const-blocks/const-repeat.rs
diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-const.rs b/src/test/ui/consts/const-blocks/fn-call-in-const.rs
similarity index 73%
rename from src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-const.rs
rename to src/test/ui/consts/const-blocks/fn-call-in-const.rs
index da1bae1be8d4e..7936af75d84ac 100644
--- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-const.rs
+++ b/src/test/ui/consts/const-blocks/fn-call-in-const.rs
@@ -1,7 +1,7 @@
 // run-pass
 
-#![allow(unused)]
-#![feature(const_in_array_repeat_expressions)]
+#![feature(inline_const)]
+#![allow(unused, incomplete_features)]
 
 // Some type that is not copyable.
 struct Bar;
@@ -18,6 +18,6 @@ const _: [u32; 2] = [type_copy(); 2];
 
 // This is allowed because all promotion contexts use the explicit rules for promotability when
 // inside an explicit const context.
-const _: [Option<Bar>; 2] = [type_no_copy(); 2];
+const _: [Option<Bar>; 2] = [const { type_no_copy() }; 2];
 
 fn main() {}
diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-non-const.rs b/src/test/ui/consts/const-blocks/fn-call-in-non-const.rs
similarity index 85%
rename from src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-non-const.rs
rename to src/test/ui/consts/const-blocks/fn-call-in-non-const.rs
index d40facf232a67..19217843759c1 100644
--- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-non-const.rs
+++ b/src/test/ui/consts/const-blocks/fn-call-in-non-const.rs
@@ -1,5 +1,3 @@
-#![feature(const_in_array_repeat_expressions)]
-
 // Some type that is not copyable.
 struct Bar;
 
diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-non-const.stderr b/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr
similarity index 92%
rename from src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-non-const.stderr
rename to src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr
index 48092432bb1d6..b75452cd21702 100644
--- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/fn-call-in-non-const.stderr
+++ b/src/test/ui/consts/const-blocks/fn-call-in-non-const.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
-  --> $DIR/fn-call-in-non-const.rs:16:31
+  --> $DIR/fn-call-in-non-const.rs:14:31
    |
 LL |     let _: [Option<Bar>; 2] = [no_copy(); 2];
    |                               ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-fail.rs b/src/test/ui/consts/const-blocks/migrate-fail.rs
similarity index 92%
rename from src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-fail.rs
rename to src/test/ui/consts/const-blocks/migrate-fail.rs
index d04b0b7e168f5..bb12139a7bae9 100644
--- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-fail.rs
+++ b/src/test/ui/consts/const-blocks/migrate-fail.rs
@@ -1,6 +1,5 @@
 // ignore-compare-mode-nll
 // compile-flags: -Z borrowck=migrate
-#![feature(const_in_array_repeat_expressions)]
 #![allow(warnings)]
 
 // Some type that is not copyable.
diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-fail.stderr b/src/test/ui/consts/const-blocks/migrate-fail.stderr
similarity index 93%
rename from src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-fail.stderr
rename to src/test/ui/consts/const-blocks/migrate-fail.stderr
index 476d48fd4969d..0fdbbc36288e9 100644
--- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-fail.stderr
+++ b/src/test/ui/consts/const-blocks/migrate-fail.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
-  --> $DIR/migrate-fail.rs:14:37
+  --> $DIR/migrate-fail.rs:13:37
    |
 LL |         let arr: [Option<Bar>; 2] = [x; 2];
    |                                     ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
@@ -9,7 +9,7 @@ LL |         let arr: [Option<Bar>; 2] = [x; 2];
    = note: the `Copy` trait is required because the repeated element will be copied
 
 error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
-  --> $DIR/migrate-fail.rs:20:37
+  --> $DIR/migrate-fail.rs:19:37
    |
 LL |         let arr: [Option<Bar>; 2] = [x; 2];
    |                                     ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-pass.rs b/src/test/ui/consts/const-blocks/migrate-pass.rs
similarity index 98%
rename from src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-pass.rs
rename to src/test/ui/consts/const-blocks/migrate-pass.rs
index bfa8ebcfdd32a..3195717fa38ba 100644
--- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/migrate-pass.rs
+++ b/src/test/ui/consts/const-blocks/migrate-pass.rs
@@ -1,7 +1,6 @@
 // check-pass
 // compile-flags: -Z borrowck=migrate
 // ignore-compare-mode-nll
-#![feature(const_in_array_repeat_expressions)]
 #![allow(warnings)]
 
 // Some type that is not copyable.
diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-fail.rs b/src/test/ui/consts/const-blocks/nll-fail.rs
similarity index 91%
rename from src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-fail.rs
rename to src/test/ui/consts/const-blocks/nll-fail.rs
index 2d5c59d112e1b..871387c1fd0cf 100644
--- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-fail.rs
+++ b/src/test/ui/consts/const-blocks/nll-fail.rs
@@ -1,5 +1,4 @@
 // ignore-compare-mode-nll
-#![feature(const_in_array_repeat_expressions, nll)]
 #![allow(warnings)]
 
 // Some type that is not copyable.
diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-fail.stderr b/src/test/ui/consts/const-blocks/nll-fail.stderr
similarity index 93%
rename from src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-fail.stderr
rename to src/test/ui/consts/const-blocks/nll-fail.stderr
index 3aa69996ff743..8122085635977 100644
--- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-fail.stderr
+++ b/src/test/ui/consts/const-blocks/nll-fail.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
-  --> $DIR/nll-fail.rs:13:37
+  --> $DIR/nll-fail.rs:12:37
    |
 LL |         let arr: [Option<Bar>; 2] = [x; 2];
    |                                     ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
@@ -9,7 +9,7 @@ LL |         let arr: [Option<Bar>; 2] = [x; 2];
    = note: the `Copy` trait is required because the repeated element will be copied
 
 error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
-  --> $DIR/nll-fail.rs:19:37
+  --> $DIR/nll-fail.rs:18:37
    |
 LL |         let arr: [Option<Bar>; 2] = [x; 2];
    |                                     ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-pass.rs b/src/test/ui/consts/const-blocks/nll-pass.rs
similarity index 98%
rename from src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-pass.rs
rename to src/test/ui/consts/const-blocks/nll-pass.rs
index a304f877ab7ad..d8defa19483e1 100644
--- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/nll-pass.rs
+++ b/src/test/ui/consts/const-blocks/nll-pass.rs
@@ -1,7 +1,6 @@
 // check-pass
 // ignore-compare-mode-nll
 #![allow(warnings)]
-#![feature(const_in_array_repeat_expressions, nll)]
 
 // Some type that is not copyable.
 struct Bar;
diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/run-pass.rs b/src/test/ui/consts/const-blocks/run-pass.rs
similarity index 81%
rename from src/test/ui/consts/rfc-2203-const-array-repeat-exprs/run-pass.rs
rename to src/test/ui/consts/const-blocks/run-pass.rs
index 27bf5dabf566b..e11f69babf798 100644
--- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/run-pass.rs
+++ b/src/test/ui/consts/const-blocks/run-pass.rs
@@ -1,5 +1,4 @@
 // run-pass
-#![feature(const_in_array_repeat_expressions)]
 
 #[derive(Debug, Eq, PartialEq)]
 struct Bar;
diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/trait-error.rs b/src/test/ui/consts/const-blocks/trait-error.rs
similarity index 77%
rename from src/test/ui/consts/rfc-2203-const-array-repeat-exprs/trait-error.rs
rename to src/test/ui/consts/const-blocks/trait-error.rs
index f8df7aafa60df..5a614cbdd1561 100644
--- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/trait-error.rs
+++ b/src/test/ui/consts/const-blocks/trait-error.rs
@@ -1,5 +1,3 @@
-#![feature(const_in_array_repeat_expressions)]
-
 #[derive(Copy, Clone)]
 struct Foo<T>(T);
 
diff --git a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/trait-error.stderr b/src/test/ui/consts/const-blocks/trait-error.stderr
similarity index 94%
rename from src/test/ui/consts/rfc-2203-const-array-repeat-exprs/trait-error.stderr
rename to src/test/ui/consts/const-blocks/trait-error.stderr
index 26de67e50fa67..26e2848e7f7a1 100644
--- a/src/test/ui/consts/rfc-2203-const-array-repeat-exprs/trait-error.stderr
+++ b/src/test/ui/consts/const-blocks/trait-error.stderr
@@ -1,5 +1,5 @@
 error[E0277]: the trait bound `Foo<String>: Copy` is not satisfied
-  --> $DIR/trait-error.rs:7:5
+  --> $DIR/trait-error.rs:5:5
    |
 LL |     [Foo(String::new()); 4];
    |     ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Foo<String>`
diff --git a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs
deleted file mode 100644
index 5ed302bbff3aa..0000000000000
--- a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-#![allow(warnings)]
-
-struct Bar;
-
-// This function would compile with the feature gate, and tests that it is suggested.
-fn foo() {
-    let arr: [Option<String>; 2] = [None::<String>; 2];
-    //~^ ERROR the trait bound `Option<String>: Copy` is not satisfied [E0277]
-}
-
-// This function would not compile with the feature gate, and tests that it is not suggested.
-fn bar() {
-    let arr: [Option<String>; 2] = [Some("foo".to_string()); 2];
-    //~^ ERROR the trait bound `Option<String>: Copy` is not satisfied [E0277]
-}
-
-fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr b/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr
deleted file mode 100644
index ca1706169afc6..0000000000000
--- a/src/test/ui/feature-gates/feature-gate-const_in_array_repeat_expressions.stderr
+++ /dev/null
@@ -1,25 +0,0 @@
-error[E0277]: the trait bound `Option<String>: Copy` is not satisfied
-  --> $DIR/feature-gate-const_in_array_repeat_expressions.rs:7:36
-   |
-LL |     let arr: [Option<String>; 2] = [None::<String>; 2];
-   |                                    ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Option<String>`
-   |
-   = help: the following implementations were found:
-             <Option<T> as Copy>
-   = note: the `Copy` trait is required because the repeated element will be copied
-   = note: this array initializer can be evaluated at compile-time, see issue #49147 <https://github.com/rust-lang/rust/issues/49147> for more information
-   = help: add `#![feature(const_in_array_repeat_expressions)]` to the crate attributes to enable
-
-error[E0277]: the trait bound `Option<String>: Copy` is not satisfied
-  --> $DIR/feature-gate-const_in_array_repeat_expressions.rs:13:36
-   |
-LL |     let arr: [Option<String>; 2] = [Some("foo".to_string()); 2];
-   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Option<String>`
-   |
-   = help: the following implementations were found:
-             <Option<T> as Copy>
-   = note: the `Copy` trait is required because the repeated element will be copied
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/unused/unused-closure.rs b/src/test/ui/unused/unused-closure.rs
index 5100636842be5..c96c907318ce9 100644
--- a/src/test/ui/unused/unused-closure.rs
+++ b/src/test/ui/unused/unused-closure.rs
@@ -2,7 +2,6 @@
 // edition:2018
 
 #![feature(async_closure)]
-#![feature(const_in_array_repeat_expressions)]
 #![feature(generators)]
 #![deny(unused_must_use)]
 
@@ -18,10 +17,6 @@ fn unused() {
 
     [Box::new([|| {}; 10]); 1]; //~ ERROR unused array of boxed arrays of closures that must be used
 
-    [|| { //~ ERROR unused array of generators that must be used
-        yield 42u32;
-    }; 42];
-
     vec![|| "a"].pop().unwrap(); //~ ERROR unused closure that must be used
 
     let b = false;
diff --git a/src/test/ui/unused/unused-closure.stderr b/src/test/ui/unused/unused-closure.stderr
index f8b4cbb02c4bd..265d3e8e075fd 100644
--- a/src/test/ui/unused/unused-closure.stderr
+++ b/src/test/ui/unused/unused-closure.stderr
@@ -1,5 +1,5 @@
 error: unused closure that must be used
-  --> $DIR/unused-closure.rs:10:5
+  --> $DIR/unused-closure.rs:9:5
    |
 LL | /     || {
 LL | |         println!("Hello!");
@@ -7,14 +7,14 @@ LL | |     };
    | |______^
    |
 note: the lint level is defined here
-  --> $DIR/unused-closure.rs:7:9
+  --> $DIR/unused-closure.rs:6:9
    |
 LL | #![deny(unused_must_use)]
    |         ^^^^^^^^^^^^^^^
    = note: closures are lazy and do nothing unless called
 
 error: unused implementer of `Future` that must be used
-  --> $DIR/unused-closure.rs:14:5
+  --> $DIR/unused-closure.rs:13:5
    |
 LL |     async {};
    |     ^^^^^^^^^
@@ -22,7 +22,7 @@ LL |     async {};
    = note: futures do nothing unless you `.await` or poll them
 
 error: unused closure that must be used
-  --> $DIR/unused-closure.rs:15:5
+  --> $DIR/unused-closure.rs:14:5
    |
 LL |     || async {};
    |     ^^^^^^^^^^^^
@@ -30,7 +30,7 @@ LL |     || async {};
    = note: closures are lazy and do nothing unless called
 
 error: unused closure that must be used
-  --> $DIR/unused-closure.rs:16:5
+  --> $DIR/unused-closure.rs:15:5
    |
 LL |     async || {};
    |     ^^^^^^^^^^^^
@@ -38,25 +38,15 @@ LL |     async || {};
    = note: closures are lazy and do nothing unless called
 
 error: unused array of boxed arrays of closures that must be used
-  --> $DIR/unused-closure.rs:19:5
+  --> $DIR/unused-closure.rs:18:5
    |
 LL |     [Box::new([|| {}; 10]); 1];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: closures are lazy and do nothing unless called
 
-error: unused array of generators that must be used
-  --> $DIR/unused-closure.rs:21:5
-   |
-LL | /     [|| {
-LL | |         yield 42u32;
-LL | |     }; 42];
-   | |___________^
-   |
-   = note: generators are lazy and do nothing unless resumed
-
 error: unused closure that must be used
-  --> $DIR/unused-closure.rs:25:5
+  --> $DIR/unused-closure.rs:20:5
    |
 LL |     vec![|| "a"].pop().unwrap();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -64,12 +54,12 @@ LL |     vec![|| "a"].pop().unwrap();
    = note: closures are lazy and do nothing unless called
 
 error: unused closure that must be used
-  --> $DIR/unused-closure.rs:28:9
+  --> $DIR/unused-closure.rs:23:9
    |
 LL |         || true;
    |         ^^^^^^^^
    |
    = note: closures are lazy and do nothing unless called
 
-error: aborting due to 8 previous errors
+error: aborting due to 7 previous errors
 

From 6695690d495ff7b2ffb6dd78db4ae9b9639dee67 Mon Sep 17 00:00:00 2001
From: Sebastian Widua <seppel3210@gmail.com>
Date: Sun, 31 Jan 2021 12:19:09 +0100
Subject: [PATCH 23/24] Fix small typo

---
 library/alloc/src/string.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs
index 9b0b480a7e925..3218b3535c970 100644
--- a/library/alloc/src/string.rs
+++ b/library/alloc/src/string.rs
@@ -972,7 +972,7 @@ impl String {
         self.vec.try_reserve(additional)
     }
 
-    /// Tries to reserves the minimum capacity for exactly `additional` more elements to
+    /// Tries to reserve the minimum capacity for exactly `additional` more elements to
     /// be inserted in the given `String`. After calling `reserve_exact`,
     /// capacity will be greater than or equal to `self.len() + additional`.
     /// Does nothing if the capacity is already sufficient.

From 8940a2652e100554acb3e3bc86ee4f37a3e958ee Mon Sep 17 00:00:00 2001
From: Ashley Mannix <kodraus@hey.com>
Date: Sun, 31 Jan 2021 21:50:47 +1000
Subject: [PATCH 24/24] stabilize int_bits_const

---
 compiler/rustc_data_structures/src/lib.rs | 1 -
 compiler/rustc_serialize/src/lib.rs       | 1 -
 compiler/rustc_serialize/tests/leb128.rs  | 1 -
 library/alloc/src/lib.rs                  | 1 -
 library/alloc/tests/lib.rs                | 1 -
 library/core/src/num/int_macros.rs        | 3 +--
 library/core/src/num/uint_macros.rs       | 3 +--
 library/core/tests/lib.rs                 | 1 -
 library/panic_unwind/src/lib.rs           | 1 -
 9 files changed, 2 insertions(+), 11 deletions(-)

diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index 5880bbd3de44e..36d261fb737ce 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -13,7 +13,6 @@
 #![feature(unboxed_closures)]
 #![feature(generator_trait)]
 #![feature(fn_traits)]
-#![feature(int_bits_const)]
 #![feature(min_specialization)]
 #![feature(auto_traits)]
 #![feature(nll)]
diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs
index 53c3adcc20c02..ea04e7bb44b3b 100644
--- a/compiler/rustc_serialize/src/lib.rs
+++ b/compiler/rustc_serialize/src/lib.rs
@@ -17,7 +17,6 @@ Core encoding and decoding interfaces.
 #![feature(min_specialization)]
 #![feature(vec_spare_capacity)]
 #![feature(core_intrinsics)]
-#![feature(int_bits_const)]
 #![feature(maybe_uninit_slice)]
 #![feature(new_uninit)]
 #![cfg_attr(test, feature(test))]
diff --git a/compiler/rustc_serialize/tests/leb128.rs b/compiler/rustc_serialize/tests/leb128.rs
index a2bcf2c251d7a..3e2aab5125ab7 100644
--- a/compiler/rustc_serialize/tests/leb128.rs
+++ b/compiler/rustc_serialize/tests/leb128.rs
@@ -1,4 +1,3 @@
-#![feature(int_bits_const)]
 #![feature(maybe_uninit_slice)]
 #![feature(maybe_uninit_uninit_array)]
 
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index 13f4d902d3b2a..08e933bbb396c 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -102,7 +102,6 @@
 #![feature(fn_traits)]
 #![feature(fundamental)]
 #![feature(inplace_iteration)]
-#![feature(int_bits_const)]
 #![feature(lang_items)]
 #![feature(layout_for_ptr)]
 #![feature(maybe_uninit_ref)]
diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs
index 0b7eeab4e9679..7b00354629875 100644
--- a/library/alloc/tests/lib.rs
+++ b/library/alloc/tests/lib.rs
@@ -17,7 +17,6 @@
 #![feature(binary_heap_retain)]
 #![feature(inplace_iteration)]
 #![feature(iter_map_while)]
-#![feature(int_bits_const)]
 #![feature(vecdeque_binary_search)]
 #![feature(slice_group_by)]
 
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index 8fdd7c9e5d7fb..96affd17cc520 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -32,10 +32,9 @@ macro_rules! int_impl {
         /// # Examples
         ///
         /// ```
-        /// #![feature(int_bits_const)]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::BITS, ", stringify!($BITS), ");")]
         /// ```
-        #[unstable(feature = "int_bits_const", issue = "76904")]
+        #[stable(feature = "int_bits_const", since = "1.51.0")]
         pub const BITS: u32 = $BITS;
 
         /// Converts a string slice in a given base to an integer.
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index 9fccf3f72ce1a..aee424b9b1392 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -32,10 +32,9 @@ macro_rules! uint_impl {
         /// # Examples
         ///
         /// ```
-        /// #![feature(int_bits_const)]
         #[doc = concat!("assert_eq!(", stringify!($SelfT), "::BITS, ", stringify!($BITS), ");")]
         /// ```
-        #[unstable(feature = "int_bits_const", issue = "76904")]
+        #[stable(feature = "int_bits_const", since = "1.51.0")]
         pub const BITS: u32 = $BITS;
 
         /// Converts a string slice in a given base to an integer.
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index bc0e3e059c917..26dbcb8569b43 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -70,7 +70,6 @@
 #![feature(partition_point)]
 #![feature(once_cell)]
 #![feature(unsafe_block_in_unsafe_fn)]
-#![feature(int_bits_const)]
 #![feature(nonzero_leading_trailing_zeros)]
 #![feature(const_option)]
 #![feature(integer_atomics)]
diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs
index 9ce9c477ec0f0..99a0c67fc11b9 100644
--- a/library/panic_unwind/src/lib.rs
+++ b/library/panic_unwind/src/lib.rs
@@ -18,7 +18,6 @@
     issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/"
 )]
 #![feature(core_intrinsics)]
-#![feature(int_bits_const)]
 #![feature(lang_items)]
 #![feature(nll)]
 #![feature(panic_unwind)]