diff --git a/.gitattributes b/.gitattributes
index a7de7ce8559..2c5c37007d5 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -3,7 +3,7 @@
 * text=auto eol=lf
 *.cpp rust
 *.h rust
-*.rs rust
+*.rs rust diff=rust
 *.fixed linguist-language=Rust
 src/etc/installer/gfx/* binary
 *.woff binary
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c45832e8153..0d4e0e5b06b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -154,6 +154,11 @@ jobs:
     strategy:
       matrix:
         include:
+          - name: aarch64-gnu
+            os:
+              - self-hosted
+              - ARM64
+              - linux
           - name: arm-android
             os: ubuntu-latest-xl
             env: {}
@@ -497,116 +502,6 @@ jobs:
           AWS_ACCESS_KEY_ID: "${{ env.ARTIFACTS_AWS_ACCESS_KEY_ID }}"
           AWS_SECRET_ACCESS_KEY: "${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }}"
         if: "success() && !env.SKIP_JOB && (github.event_name == 'push' || env.DEPLOY == '1' || env.DEPLOY_ALT == '1')"
-  auto-fallible:
-    name: auto-fallible
-    env:
-      CI_JOB_NAME: "${{ matrix.name }}"
-      SCCACHE_BUCKET: rust-lang-gha-caches
-      DEPLOY_BUCKET: rust-lang-gha
-      TOOLSTATE_REPO: "https://github.com/pietroalbini/rust-toolstate"
-      TOOLSTATE_ISSUES_API_URL: "https://api.github.com/repos/pietroalbini/rust-toolstate/issues"
-      TOOLSTATE_PUBLISH: 1
-      CACHES_AWS_ACCESS_KEY_ID: AKIA46X5W6CZOMUQATD5
-      ARTIFACTS_AWS_ACCESS_KEY_ID: AKIA46X5W6CZH5AYXDVF
-      CACHE_DOMAIN: ci-caches-gha.rust-lang.org
-    if: "github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust'"
-    strategy:
-      fail-fast: false
-      matrix:
-        include:
-          - name: aarch64-gnu
-            os:
-              - self-hosted
-              - ARM64
-              - linux
-    timeout-minutes: 600
-    runs-on: "${{ matrix.os }}"
-    steps:
-      - name: disable git crlf conversion
-        run: git config --global core.autocrlf false
-      - name: checkout the source code
-        uses: actions/checkout@v2
-        with:
-          fetch-depth: 2
-      - name: configure the PR in which the error message will be posted
-        run: "echo \"[CI_PR_NUMBER=$num]\""
-        env:
-          num: "${{ github.event.number }}"
-        if: "success() && !env.SKIP_JOBS && github.event_name == 'pull_request'"
-      - name: add extra environment variables
-        run: src/ci/scripts/setup-environment.sh
-        env:
-          EXTRA_VARIABLES: "${{ toJson(matrix.env) }}"
-        if: success() && !env.SKIP_JOB
-      - name: decide whether to skip this job
-        run: src/ci/scripts/should-skip-this.sh
-        if: success() && !env.SKIP_JOB
-      - name: configure GitHub Actions to kill the build when outdated
-        uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
-        with:
-          github_token: "${{ secrets.github_token }}"
-        if: "success() && !env.SKIP_JOB && github.ref != 'refs/heads/try'"
-      - name: collect CPU statistics
-        run: src/ci/scripts/collect-cpu-stats.sh
-        if: success() && !env.SKIP_JOB
-      - name: show the current environment
-        run: src/ci/scripts/dump-environment.sh
-        if: success() && !env.SKIP_JOB
-      - name: install awscli
-        run: src/ci/scripts/install-awscli.sh
-        if: success() && !env.SKIP_JOB
-      - name: install sccache
-        run: src/ci/scripts/install-sccache.sh
-        if: success() && !env.SKIP_JOB
-      - name: select Xcode
-        run: src/ci/scripts/select-xcode.sh
-        if: success() && !env.SKIP_JOB
-      - name: install clang
-        run: src/ci/scripts/install-clang.sh
-        if: success() && !env.SKIP_JOB
-      - name: install WIX
-        run: src/ci/scripts/install-wix.sh
-        if: success() && !env.SKIP_JOB
-      - name: ensure the build happens on a partition with enough space
-        run: src/ci/scripts/symlink-build-dir.sh
-        if: success() && !env.SKIP_JOB
-      - name: disable git crlf conversion
-        run: src/ci/scripts/disable-git-crlf-conversion.sh
-        if: success() && !env.SKIP_JOB
-      - name: install MSYS2
-        run: src/ci/scripts/install-msys2.sh
-        if: success() && !env.SKIP_JOB
-      - name: install MinGW
-        run: src/ci/scripts/install-mingw.sh
-        if: success() && !env.SKIP_JOB
-      - name: install ninja
-        run: src/ci/scripts/install-ninja.sh
-        if: success() && !env.SKIP_JOB
-      - name: enable ipv6 on Docker
-        run: src/ci/scripts/enable-docker-ipv6.sh
-        if: success() && !env.SKIP_JOB
-      - name: disable git crlf conversion
-        run: src/ci/scripts/disable-git-crlf-conversion.sh
-        if: success() && !env.SKIP_JOB
-      - name: checkout submodules
-        run: src/ci/scripts/checkout-submodules.sh
-        if: success() && !env.SKIP_JOB
-      - name: ensure line endings are correct
-        run: src/ci/scripts/verify-line-endings.sh
-        if: success() && !env.SKIP_JOB
-      - name: run the build
-        run: src/ci/scripts/run-build-from-ci.sh
-        env:
-          AWS_ACCESS_KEY_ID: "${{ env.CACHES_AWS_ACCESS_KEY_ID }}"
-          AWS_SECRET_ACCESS_KEY: "${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.CACHES_AWS_ACCESS_KEY_ID)] }}"
-          TOOLSTATE_REPO_ACCESS_TOKEN: "${{ secrets.TOOLSTATE_REPO_ACCESS_TOKEN }}"
-        if: success() && !env.SKIP_JOB
-      - name: upload artifacts to S3
-        run: src/ci/scripts/upload-artifacts.sh
-        env:
-          AWS_ACCESS_KEY_ID: "${{ env.ARTIFACTS_AWS_ACCESS_KEY_ID }}"
-          AWS_SECRET_ACCESS_KEY: "${{ secrets[format('AWS_SECRET_ACCESS_KEY_{0}', env.ARTIFACTS_AWS_ACCESS_KEY_ID)] }}"
-        if: "success() && !env.SKIP_JOB && (github.event_name == 'push' || env.DEPLOY == '1' || env.DEPLOY_ALT == '1')"
   try:
     name: try
     env:
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index a6ac056b93b..1f2aba2b27e 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -9,6 +9,7 @@ use rustc_data_structures::thin_vec::ThinVec;
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
+use rustc_session::parse::feature_err;
 use rustc_span::hygiene::ForLoopLoc;
 use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
 use rustc_span::symbol::{sym, Ident, Symbol};
@@ -146,7 +147,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label)
                 }
                 ExprKind::Assign(ref el, ref er, span) => {
-                    hir::ExprKind::Assign(self.lower_expr(el), self.lower_expr(er), span)
+                    self.lower_expr_assign(el, er, span, e.span)
                 }
                 ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp(
                     self.lower_binop(op),
@@ -840,6 +841,134 @@ impl<'hir> LoweringContext<'_, 'hir> {
         })
     }
 
+    /// Destructure the LHS of complex assignments.
+    /// For instance, lower `(a, b) = t` to `{ let (lhs1, lhs2) = t; a = lhs1; b = lhs2; }`.
+    fn lower_expr_assign(
+        &mut self,
+        lhs: &Expr,
+        rhs: &Expr,
+        eq_sign_span: Span,
+        whole_span: Span,
+    ) -> hir::ExprKind<'hir> {
+        // Return early in case of an ordinary assignment.
+        fn is_ordinary(lhs: &Expr) -> bool {
+            match &lhs.kind {
+                ExprKind::Tup(..) => false,
+                ExprKind::Paren(e) => {
+                    match e.kind {
+                        // We special-case `(..)` for consistency with patterns.
+                        ExprKind::Range(None, None, RangeLimits::HalfOpen) => false,
+                        _ => is_ordinary(e),
+                    }
+                }
+                _ => true,
+            }
+        }
+        if is_ordinary(lhs) {
+            return hir::ExprKind::Assign(self.lower_expr(lhs), self.lower_expr(rhs), eq_sign_span);
+        }
+        if !self.sess.features_untracked().destructuring_assignment {
+            feature_err(
+                &self.sess.parse_sess,
+                sym::destructuring_assignment,
+                eq_sign_span,
+                "destructuring assignments are unstable",
+            )
+            .span_label(lhs.span, "cannot assign to this expression")
+            .emit();
+        }
+
+        let mut assignments = vec![];
+
+        // The LHS becomes a pattern: `(lhs1, lhs2)`.
+        let pat = self.destructure_assign(lhs, eq_sign_span, &mut assignments);
+        let rhs = self.lower_expr(rhs);
+
+        // Introduce a `let` for destructuring: `let (lhs1, lhs2) = t`.
+        let destructure_let = self.stmt_let_pat(
+            ThinVec::new(),
+            whole_span,
+            Some(rhs),
+            pat,
+            hir::LocalSource::AssignDesugar(eq_sign_span),
+        );
+
+        // `a = lhs1; b = lhs2;`.
+        let stmts = self
+            .arena
+            .alloc_from_iter(std::iter::once(destructure_let).chain(assignments.into_iter()));
+
+        // Wrap everything in a block.
+        hir::ExprKind::Block(&self.block_all(whole_span, stmts, None), None)
+    }
+
+    /// Convert the LHS of a destructuring assignment to a pattern.
+    /// Each sub-assignment is recorded in `assignments`.
+    fn destructure_assign(
+        &mut self,
+        lhs: &Expr,
+        eq_sign_span: Span,
+        assignments: &mut Vec<hir::Stmt<'hir>>,
+    ) -> &'hir hir::Pat<'hir> {
+        match &lhs.kind {
+            // Tuples.
+            ExprKind::Tup(elements) => {
+                let (pats, rest) =
+                    self.destructure_sequence(elements, "tuple", eq_sign_span, assignments);
+                let tuple_pat = hir::PatKind::Tuple(pats, rest.map(|r| r.0));
+                return self.pat_without_dbm(lhs.span, tuple_pat);
+            }
+            ExprKind::Paren(e) => {
+                // We special-case `(..)` for consistency with patterns.
+                if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind {
+                    let tuple_pat = hir::PatKind::Tuple(&[], Some(0));
+                    return self.pat_without_dbm(lhs.span, tuple_pat);
+                } else {
+                    return self.destructure_assign(e, eq_sign_span, assignments);
+                }
+            }
+            _ => {}
+        }
+        // Treat all other cases as normal lvalue.
+        let ident = Ident::new(sym::lhs, lhs.span);
+        let (pat, binding) = self.pat_ident(lhs.span, ident);
+        let ident = self.expr_ident(lhs.span, ident, binding);
+        let assign = hir::ExprKind::Assign(self.lower_expr(lhs), ident, eq_sign_span);
+        let expr = self.expr(lhs.span, assign, ThinVec::new());
+        assignments.push(self.stmt_expr(lhs.span, expr));
+        pat
+    }
+
+    /// Destructure a sequence of expressions occurring on the LHS of an assignment.
+    /// Such a sequence occurs in a tuple (struct)/slice.
+    /// Return a sequence of corresponding patterns, and the index and the span of `..` if it
+    /// exists.
+    /// Each sub-assignment is recorded in `assignments`.
+    fn destructure_sequence(
+        &mut self,
+        elements: &[AstP<Expr>],
+        ctx: &str,
+        eq_sign_span: Span,
+        assignments: &mut Vec<hir::Stmt<'hir>>,
+    ) -> (&'hir [&'hir hir::Pat<'hir>], Option<(usize, Span)>) {
+        let mut rest = None;
+        let elements =
+            self.arena.alloc_from_iter(elements.iter().enumerate().filter_map(|(i, e)| {
+                // Check for `..` pattern.
+                if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind {
+                    if let Some((_, prev_span)) = rest {
+                        self.ban_extra_rest_pat(e.span, prev_span, ctx);
+                    } else {
+                        rest = Some((i, e.span));
+                    }
+                    None
+                } else {
+                    Some(self.destructure_assign(e, eq_sign_span, assignments))
+                }
+            }));
+        (elements, rest)
+    }
+
     /// Desugar `<start>..=<end>` into `std::ops::RangeInclusive::new(<start>, <end>)`.
     fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> {
         let e1 = self.lower_expr_mut(e1);
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 599599f415f..af2f96d5e62 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -2531,6 +2531,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 hir_id,
                 kind: hir::PatKind::Binding(bm, hir_id, ident.with_span_pos(span), None),
                 span,
+                default_binding_modes: true,
             }),
             hir_id,
         )
@@ -2541,7 +2542,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     }
 
     fn pat(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
-        self.arena.alloc(hir::Pat { hir_id: self.next_id(), kind, span })
+        self.arena.alloc(hir::Pat {
+            hir_id: self.next_id(),
+            kind,
+            span,
+            default_binding_modes: true,
+        })
+    }
+
+    fn pat_without_dbm(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
+        self.arena.alloc(hir::Pat {
+            hir_id: self.next_id(),
+            kind,
+            span,
+            default_binding_modes: false,
+        })
     }
 
     fn ty_path(
diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs
index a1cbcde1f42..e4e7b24d29e 100644
--- a/compiler/rustc_ast_lowering/src/pat.rs
+++ b/compiler/rustc_ast_lowering/src/pat.rs
@@ -273,11 +273,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
     /// Construct a `Pat` with the `HirId` of `p.id` lowered.
     fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
-        self.arena.alloc(hir::Pat { hir_id: self.lower_node_id(p.id), kind, span: p.span })
+        self.arena.alloc(hir::Pat {
+            hir_id: self.lower_node_id(p.id),
+            kind,
+            span: p.span,
+            default_binding_modes: true,
+        })
     }
 
     /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
-    fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
+    crate fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
         self.diagnostic()
             .struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
             .span_label(sp, &format!("can only be used once per {} pattern", ctx))
diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs
index eda77bf19d3..466758f2f86 100644
--- a/compiler/rustc_codegen_cranelift/src/common.rs
+++ b/compiler/rustc_codegen_cranelift/src/common.rs
@@ -361,13 +361,11 @@ impl<'tcx, M: Module> FunctionCx<'_, 'tcx, M> {
     where
         T: TypeFoldable<'tcx> + Copy,
     {
-        if let Some(substs) = self.instance.substs_for_mir_body() {
-            self.tcx
-                .subst_and_normalize_erasing_regions(substs, ty::ParamEnv::reveal_all(), value)
-        } else {
-            self.tcx
-                .normalize_erasing_regions(ty::ParamEnv::reveal_all(), *value)
-        }
+        self.instance.subst_mir_and_normalize_erasing_regions(
+            self.tcx,
+            ty::ParamEnv::reveal_all(),
+            value
+        )
     }
 
     pub(crate) fn clif_type(&self, ty: Ty<'tcx>) -> Option<Type> {
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index 84e82e88e8e..01fd1681593 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -92,15 +92,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         T: Copy + TypeFoldable<'tcx>,
     {
         debug!("monomorphize: self.instance={:?}", self.instance);
-        if let Some(substs) = self.instance.substs_for_mir_body() {
-            self.cx.tcx().subst_and_normalize_erasing_regions(
-                substs,
-                ty::ParamEnv::reveal_all(),
-                &value,
-            )
-        } else {
-            self.cx.tcx().normalize_erasing_regions(ty::ParamEnv::reveal_all(), *value)
-        }
+        self.instance.subst_mir_and_normalize_erasing_regions(
+            self.cx.tcx(),
+            ty::ParamEnv::reveal_all(),
+            value,
+        )
     }
 }
 
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index ad926a810e6..84114fc7735 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -610,6 +610,9 @@ declare_features! (
     /// Allows unsized fn parameters.
     (active, unsized_fn_params, "1.49.0", Some(48055), None),
 
+    /// Allows the use of destructuring assignments.
+    (active, destructuring_assignment, "1.49.0", Some(71126), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index b9ec18688c5..3c28b48795f 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -732,6 +732,9 @@ pub struct Pat<'hir> {
     pub hir_id: HirId,
     pub kind: PatKind<'hir>,
     pub span: Span,
+    // Whether to use default binding modes.
+    // At present, this is false only for destructuring assignment.
+    pub default_binding_modes: bool,
 }
 
 impl Pat<'_> {
@@ -1680,6 +1683,9 @@ pub enum LocalSource {
     AsyncFn,
     /// A desugared `<expr>.await`.
     AwaitDesugar,
+    /// A desugared `expr = expr`, where the LHS is a tuple, struct or array.
+    /// The span is that of the `=` sign.
+    AssignDesugar(Span),
 }
 
 /// Hints at the original code for a `match _ { .. }`.
@@ -2677,6 +2683,9 @@ impl<'hir> Node<'hir> {
             Node::TraitItem(TraitItem { ident, .. })
             | Node::ImplItem(ImplItem { ident, .. })
             | Node::ForeignItem(ForeignItem { ident, .. })
+            | Node::Field(StructField { ident, .. })
+            | Node::Variant(Variant { ident, .. })
+            | Node::MacroDef(MacroDef { ident, .. })
             | Node::Item(Item { ident, .. }) => Some(*ident),
             _ => None,
         }
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index ff7a145c266..1d0d6980b7a 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -2801,6 +2801,7 @@ declare_lint_pass! {
         UNSTABLE_NAME_COLLISIONS,
         IRREFUTABLE_LET_PATTERNS,
         BROKEN_INTRA_DOC_LINKS,
+        PRIVATE_INTRA_DOC_LINKS,
         INVALID_CODEBLOCK_ATTRIBUTES,
         MISSING_CRATE_LEVEL_DOCS,
         MISSING_DOC_CODE_EXAMPLES,
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 106fa8c78fa..d86e8987195 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -478,7 +478,7 @@ impl<'hir> Map<'hir> {
     }
 
     pub fn get_if_local(&self, id: DefId) -> Option<Node<'hir>> {
-        id.as_local().map(|id| self.get(self.local_def_id_to_hir_id(id)))
+        id.as_local().and_then(|id| self.find(self.local_def_id_to_hir_id(id)))
     }
 
     pub fn get_generics(&self, id: DefId) -> Option<&'hir Generics<'hir>> {
diff --git a/compiler/rustc_middle/src/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs
index def9e5ebb52..41342764ba7 100644
--- a/compiler/rustc_middle/src/middle/limits.rs
+++ b/compiler/rustc_middle/src/middle/limits.rs
@@ -48,10 +48,12 @@ fn update_limit(
                         .unwrap_or(attr.span);
 
                     let error_str = match e.kind() {
-                        IntErrorKind::Overflow => "`limit` is too large",
+                        IntErrorKind::PosOverflow => "`limit` is too large",
                         IntErrorKind::Empty => "`limit` must be a non-negative integer",
                         IntErrorKind::InvalidDigit => "not a valid integer",
-                        IntErrorKind::Underflow => bug!("`limit` should never underflow"),
+                        IntErrorKind::NegOverflow => {
+                            bug!("`limit` should never negatively overflow")
+                        }
                         IntErrorKind::Zero => bug!("zero is a valid `limit`"),
                         kind => bug!("unimplemented IntErrorKind variant: {:?}", kind),
                     };
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 8b3fb875070..306cebd9cb7 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -1,6 +1,6 @@
 use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use crate::ty::print::{FmtPrinter, Printer};
-use crate::ty::subst::InternalSubsts;
+use crate::ty::subst::{InternalSubsts, Subst};
 use crate::ty::{self, SubstsRef, Ty, TyCtxt, TypeFoldable};
 use rustc_errors::ErrorReported;
 use rustc_hir::def::Namespace;
@@ -470,10 +470,33 @@ impl<'tcx> Instance<'tcx> {
     /// This function returns `Some(substs)` in the former case and `None` otherwise -- i.e., if
     /// this function returns `None`, then the MIR body does not require substitution during
     /// codegen.
-    pub fn substs_for_mir_body(&self) -> Option<SubstsRef<'tcx>> {
+    fn substs_for_mir_body(&self) -> Option<SubstsRef<'tcx>> {
         if self.def.has_polymorphic_mir_body() { Some(self.substs) } else { None }
     }
 
+    pub fn subst_mir<T>(&self, tcx: TyCtxt<'tcx>, v: &T) -> T
+    where
+        T: TypeFoldable<'tcx> + Copy,
+    {
+        if let Some(substs) = self.substs_for_mir_body() { v.subst(tcx, substs) } else { *v }
+    }
+
+    pub fn subst_mir_and_normalize_erasing_regions<T>(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        v: &T,
+    ) -> T
+    where
+        T: TypeFoldable<'tcx> + Clone,
+    {
+        if let Some(substs) = self.substs_for_mir_body() {
+            tcx.subst_and_normalize_erasing_regions(substs, param_env, v)
+        } else {
+            tcx.normalize_erasing_regions(param_env, v.clone())
+        }
+    }
+
     /// Returns a new `Instance` where generic parameters in `instance.substs` are replaced by
     /// identify parameters if they are determined to be unused in `instance.def`.
     pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index aa5a696b09c..0042b4a3a42 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -2795,10 +2795,50 @@ impl<'tcx> TyCtxt<'tcx> {
             .filter(|item| item.kind == AssocKind::Fn && item.defaultness.has_value())
     }
 
+    fn item_name_from_hir(self, def_id: DefId) -> Option<Ident> {
+        self.hir().get_if_local(def_id).and_then(|node| node.ident())
+    }
+
+    fn item_name_from_def_id(self, def_id: DefId) -> Option<Symbol> {
+        if def_id.index == CRATE_DEF_INDEX {
+            Some(self.original_crate_name(def_id.krate))
+        } else {
+            let def_key = self.def_key(def_id);
+            match def_key.disambiguated_data.data {
+                // The name of a constructor is that of its parent.
+                rustc_hir::definitions::DefPathData::Ctor => self.item_name_from_def_id(DefId {
+                    krate: def_id.krate,
+                    index: def_key.parent.unwrap(),
+                }),
+                _ => def_key.disambiguated_data.data.get_opt_name(),
+            }
+        }
+    }
+
+    /// Look up the name of an item across crates. This does not look at HIR.
+    ///
+    /// When possible, this function should be used for cross-crate lookups over
+    /// [`opt_item_name`] to avoid invalidating the incremental cache. If you
+    /// need to handle items without a name, or HIR items that will not be
+    /// serialized cross-crate, or if you need the span of the item, use
+    /// [`opt_item_name`] instead.
+    ///
+    /// [`opt_item_name`]: Self::opt_item_name
+    pub fn item_name(self, id: DefId) -> Symbol {
+        // Look at cross-crate items first to avoid invalidating the incremental cache
+        // unless we have to.
+        self.item_name_from_def_id(id).unwrap_or_else(|| {
+            bug!("item_name: no name for {:?}", self.def_path(id));
+        })
+    }
+
+    /// Look up the name and span of an item or [`Node`].
+    ///
+    /// See [`item_name`][Self::item_name] for more information.
     pub fn opt_item_name(self, def_id: DefId) -> Option<Ident> {
-        def_id
-            .as_local()
-            .and_then(|def_id| self.hir().get(self.hir().local_def_id_to_hir_id(def_id)).ident())
+        // Look at the HIR first so the span will be correct if this is a local item.
+        self.item_name_from_hir(def_id)
+            .or_else(|| self.item_name_from_def_id(def_id).map(Ident::with_dummy_span))
     }
 
     pub fn opt_associated_item(self, def_id: DefId) -> Option<&'tcx AssocItem> {
@@ -2921,23 +2961,6 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
-    pub fn item_name(self, id: DefId) -> Symbol {
-        if id.index == CRATE_DEF_INDEX {
-            self.original_crate_name(id.krate)
-        } else {
-            let def_key = self.def_key(id);
-            match def_key.disambiguated_data.data {
-                // The name of a constructor is that of its parent.
-                rustc_hir::definitions::DefPathData::Ctor => {
-                    self.item_name(DefId { krate: id.krate, index: def_key.parent.unwrap() })
-                }
-                _ => def_key.disambiguated_data.data.get_opt_name().unwrap_or_else(|| {
-                    bug!("item_name: no name for {:?}", self.def_path(id));
-                }),
-            }
-        }
-    }
-
     /// Returns the possibly-auto-generated MIR of a `(DefId, Subst)` pair.
     pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> {
         match instance {
diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs
index 8d0c8c18537..0f86a181a55 100644
--- a/compiler/rustc_mir/src/interpret/eval_context.rs
+++ b/compiler/rustc_mir/src/interpret/eval_context.rs
@@ -505,11 +505,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>,
         value: T,
     ) -> T {
-        if let Some(substs) = frame.instance.substs_for_mir_body() {
-            self.tcx.subst_and_normalize_erasing_regions(substs, self.param_env, &value)
-        } else {
-            self.tcx.normalize_erasing_regions(self.param_env, value)
-        }
+        frame.instance.subst_mir_and_normalize_erasing_regions(*self.tcx, self.param_env, &value)
     }
 
     /// The `substs` are assumed to already be in our interpreter "universe" (param_env).
diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs
index 417176564b9..938181abff2 100644
--- a/compiler/rustc_mir/src/monomorphize/collector.rs
+++ b/compiler/rustc_mir/src/monomorphize/collector.rs
@@ -543,11 +543,11 @@ impl<'a, 'tcx> MirNeighborCollector<'a, 'tcx> {
         T: TypeFoldable<'tcx>,
     {
         debug!("monomorphize: self.instance={:?}", self.instance);
-        if let Some(substs) = self.instance.substs_for_mir_body() {
-            self.tcx.subst_and_normalize_erasing_regions(substs, ty::ParamEnv::reveal_all(), &value)
-        } else {
-            self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), value)
-        }
+        self.instance.subst_mir_and_normalize_erasing_regions(
+            self.tcx,
+            ty::ParamEnv::reveal_all(),
+            &value,
+        )
     }
 }
 
diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs
index f3d2ab9590e..a41304236b2 100644
--- a/compiler/rustc_mir/src/transform/inline.rs
+++ b/compiler/rustc_mir/src/transform/inline.rs
@@ -6,7 +6,6 @@ use rustc_index::vec::Idx;
 use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
 use rustc_middle::mir::visit::*;
 use rustc_middle::mir::*;
-use rustc_middle::ty::subst::Subst;
 use rustc_middle::ty::{self, ConstKind, Instance, InstanceDef, ParamEnv, Ty, TyCtxt};
 use rustc_span::{hygiene::ExpnKind, ExpnData, Span};
 use rustc_target::spec::abi::Abi;
@@ -128,17 +127,15 @@ impl Inliner<'tcx> {
                 self.tcx.instance_mir(callsite.callee.def)
             };
 
-            let callee_body: &Body<'tcx> = &*callee_body;
-
-            let callee_body = if self.consider_optimizing(callsite, callee_body) {
-                self.tcx.subst_and_normalize_erasing_regions(
-                    &callsite.callee.substs,
-                    self.param_env,
-                    callee_body,
-                )
-            } else {
+            if !self.consider_optimizing(callsite, &callee_body) {
                 continue;
-            };
+            }
+
+            let callee_body = callsite.callee.subst_mir_and_normalize_erasing_regions(
+                self.tcx,
+                self.param_env,
+                callee_body,
+            );
 
             let start = caller_body.basic_blocks().len();
             debug!("attempting to inline callsite {:?} - body={:?}", callsite, callee_body);
@@ -309,7 +306,7 @@ impl Inliner<'tcx> {
                     work_list.push(target);
                     // If the place doesn't actually need dropping, treat it like
                     // a regular goto.
-                    let ty = place.ty(callee_body, tcx).subst(tcx, callsite.callee.substs).ty;
+                    let ty = callsite.callee.subst_mir(self.tcx, &place.ty(callee_body, tcx).ty);
                     if ty.needs_drop(tcx, self.param_env) {
                         cost += CALL_PENALTY;
                         if let Some(unwind) = unwind {
@@ -371,8 +368,7 @@ impl Inliner<'tcx> {
         let ptr_size = tcx.data_layout.pointer_size.bytes();
 
         for v in callee_body.vars_and_temps_iter() {
-            let v = &callee_body.local_decls[v];
-            let ty = v.ty.subst(tcx, callsite.callee.substs);
+            let ty = callsite.callee.subst_mir(self.tcx, &callee_body.local_decls[v].ty);
             // Cost of the var is the size in machine-words, if we know
             // it.
             if let Some(size) = type_size_of(tcx, self.param_env, ty) {
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 205ad850c0c..14ed93f1127 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -69,6 +69,7 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
             hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None),
             hir::LocalSource::AsyncFn => ("async fn binding", None),
             hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
+            hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None),
         };
         self.check_irrefutable(&loc.pat, msg, sp);
         self.check_patterns(&loc.pat);
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 1a6c45b6c80..2324dba80f5 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -434,6 +434,7 @@ symbols! {
         deref_mut,
         deref_target,
         derive,
+        destructuring_assignment,
         diagnostic,
         direct,
         discriminant_kind,
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 324aa1a66a6..af19ad08c1d 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -718,39 +718,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
     }
 
-    fn is_destructuring_place_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> bool {
-        match &expr.kind {
-            ExprKind::Array(comps) | ExprKind::Tup(comps) => {
-                comps.iter().all(|e| self.is_destructuring_place_expr(e))
-            }
-            ExprKind::Struct(_path, fields, rest) => {
-                rest.as_ref().map(|e| self.is_destructuring_place_expr(e)).unwrap_or(true)
-                    && fields.iter().all(|f| self.is_destructuring_place_expr(&f.expr))
-            }
-            _ => expr.is_syntactic_place_expr(),
-        }
-    }
-
     pub(crate) fn check_lhs_assignable(
         &self,
         lhs: &'tcx hir::Expr<'tcx>,
         err_code: &'static str,
         expr_span: &Span,
     ) {
-        if !lhs.is_syntactic_place_expr() {
-            // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set.
-            let mut err = self.tcx.sess.struct_span_err_with_code(
-                *expr_span,
-                "invalid left-hand side of assignment",
-                DiagnosticId::Error(err_code.into()),
-            );
-            err.span_label(lhs.span, "cannot assign to this expression");
-            if self.is_destructuring_place_expr(lhs) {
-                err.note("destructuring assignments are not currently supported");
-                err.note("for more information, see https://github.com/rust-lang/rfcs/issues/372");
-            }
-            err.emit();
+        if lhs.is_syntactic_place_expr() {
+            return;
         }
+
+        // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set.
+        let mut err = self.tcx.sess.struct_span_err_with_code(
+            *expr_span,
+            "invalid left-hand side of assignment",
+            DiagnosticId::Error(err_code.into()),
+        );
+        err.span_label(lhs.span, "cannot assign to this expression");
+        err.emit();
     }
 
     /// Type check assignment expression `expr` of form `lhs = rhs`.
diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs
index f76f42dea1e..6489b7838d6 100644
--- a/compiler/rustc_typeck/src/check/pat.rs
+++ b/compiler/rustc_typeck/src/check/pat.rs
@@ -270,6 +270,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ///
     /// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`.
     fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option<Res>) -> AdjustMode {
+        // When we perform destructuring assignment, we disable default match bindings, which are
+        // unintuitive in this context.
+        if !pat.default_binding_modes {
+            return AdjustMode::Reset;
+        }
         match &pat.kind {
             // Type checking these product-like types successfully always require
             // that the expected type be of those types and not reference types.
diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs
index ba0f22513a1..7b31b9f3915 100644
--- a/compiler/rustc_typeck/src/check/regionck.rs
+++ b/compiler/rustc_typeck/src/check/regionck.rs
@@ -577,7 +577,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
     fn link_pattern(&self, discr_cmt: PlaceWithHirId<'tcx>, root_pat: &hir::Pat<'_>) {
         debug!("link_pattern(discr_cmt={:?}, root_pat={:?})", discr_cmt, root_pat);
         ignore_err!(self.with_mc(|mc| {
-            mc.cat_pattern(discr_cmt, root_pat, |sub_cmt, hir::Pat { kind, span, hir_id }| {
+            mc.cat_pattern(discr_cmt, root_pat, |sub_cmt, hir::Pat { kind, span, hir_id, .. }| {
                 // `ref x` pattern
                 if let PatKind::Binding(..) = kind {
                     if let Some(ty::BindByReference(mutbl)) =
diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs
index d5b1c600d93..4fea6adf541 100644
--- a/library/alloc/src/collections/btree/map/tests.rs
+++ b/library/alloc/src/collections/btree/map/tests.rs
@@ -42,7 +42,7 @@ fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator<Item = &'a mut T>
     }
 }
 
-impl<'a, K: 'a, V: 'a> BTreeMap<K, V> {
+impl<K, V> BTreeMap<K, V> {
     /// Panics if the map (or the code navigating it) is corrupted.
     fn check(&self)
     where
@@ -54,14 +54,14 @@ impl<'a, K: 'a, V: 'a> BTreeMap<K, V> {
             assert!(root_node.ascend().is_err());
             root_node.assert_back_pointers();
 
-            let counted = root_node.assert_ascending();
-            assert_eq!(self.length, counted);
             assert_eq!(self.length, root_node.calc_length());
 
             root_node.assert_min_len(if root_node.height() > 0 { 1 } else { 0 });
         } else {
             assert_eq!(self.length, 0);
         }
+
+        self.assert_ascending();
     }
 
     /// Returns the height of the root, if any.
@@ -79,10 +79,28 @@ impl<'a, K: 'a, V: 'a> BTreeMap<K, V> {
             String::from("not yet allocated")
         }
     }
+
+    /// Asserts that the keys are in strictly ascending order.
+    fn assert_ascending(&self)
+    where
+        K: Copy + Debug + Ord,
+    {
+        let mut num_seen = 0;
+        let mut keys = self.keys();
+        if let Some(mut previous) = keys.next() {
+            num_seen = 1;
+            for next in keys {
+                assert!(previous < next, "{:?} >= {:?}", previous, next);
+                previous = next;
+                num_seen += 1;
+            }
+        }
+        assert_eq!(num_seen, self.len());
+    }
 }
 
 impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal> {
-    pub fn assert_min_len(self, min_len: usize) {
+    fn assert_min_len(self, min_len: usize) {
         assert!(self.len() >= min_len, "{} < {}", self.len(), min_len);
         if let node::ForceResult::Internal(node) = self.force() {
             for idx in 0..=node.len() {
diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs
index c8d3de9e5cd..433074027e7 100644
--- a/library/alloc/src/collections/btree/node.rs
+++ b/library/alloc/src/collections/btree/node.rs
@@ -1608,15 +1608,19 @@ pub mod marker {
 
 unsafe fn slice_insert<T>(slice: &mut [T], idx: usize, val: T) {
     unsafe {
-        ptr::copy(slice.as_ptr().add(idx), slice.as_mut_ptr().add(idx + 1), slice.len() - idx);
-        ptr::write(slice.get_unchecked_mut(idx), val);
+        let len = slice.len();
+        let slice_ptr = slice.as_mut_ptr();
+        ptr::copy(slice_ptr.add(idx), slice_ptr.add(idx + 1), len - idx);
+        ptr::write(slice_ptr.add(idx), val);
     }
 }
 
 unsafe fn slice_remove<T>(slice: &mut [T], idx: usize) -> T {
     unsafe {
-        let ret = ptr::read(slice.get_unchecked(idx));
-        ptr::copy(slice.as_ptr().add(idx + 1), slice.as_mut_ptr().add(idx), slice.len() - idx - 1);
+        let len = slice.len();
+        let slice_ptr = slice.as_mut_ptr();
+        let ret = ptr::read(slice_ptr.add(idx));
+        ptr::copy(slice_ptr.add(idx + 1), slice_ptr.add(idx), len - idx - 1);
         ret
     }
 }
diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs
index d6527057c5d..38c75de34ee 100644
--- a/library/alloc/src/collections/btree/node/tests.rs
+++ b/library/alloc/src/collections/btree/node/tests.rs
@@ -17,43 +17,6 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal>
         }
     }
 
-    /// Asserts that the keys are in strictly ascending order.
-    /// Returns how many keys it encountered.
-    pub fn assert_ascending(self) -> usize
-    where
-        K: Copy + Debug + Ord,
-    {
-        struct SeriesChecker<T> {
-            num_seen: usize,
-            previous: Option<T>,
-        }
-        impl<T: Copy + Debug + Ord> SeriesChecker<T> {
-            fn is_ascending(&mut self, next: T) {
-                if let Some(previous) = self.previous {
-                    assert!(previous < next, "{:?} >= {:?}", previous, next);
-                }
-                self.previous = Some(next);
-                self.num_seen += 1;
-            }
-        }
-
-        let mut checker = SeriesChecker { num_seen: 0, previous: None };
-        self.visit_nodes_in_order(|pos| match pos {
-            navigate::Position::Leaf(node) => {
-                for idx in 0..node.len() {
-                    let key = *unsafe { node.key_at(idx) };
-                    checker.is_ascending(key);
-                }
-            }
-            navigate::Position::InternalKV(kv) => {
-                let key = *kv.into_kv().0;
-                checker.is_ascending(key);
-            }
-            navigate::Position::Internal(_) => {}
-        });
-        checker.num_seen
-    }
-
     pub fn dump_keys(self) -> String
     where
         K: Debug,
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index ea560288c28..41202546566 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -159,6 +159,7 @@
 #![feature(slice_ptr_get)]
 #![feature(no_niche)] // rust-lang/rust#68303
 #![feature(unsafe_block_in_unsafe_fn)]
+#![feature(int_error_matching)]
 #![deny(unsafe_op_in_unsafe_fn)]
 
 #[prelude_import]
diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs
index aab17155186..9d8c8c86291 100644
--- a/library/core/src/num/error.rs
+++ b/library/core/src/num/error.rs
@@ -98,15 +98,18 @@ pub enum IntErrorKind {
     ///
     /// Among other causes, this variant will be constructed when parsing an empty string.
     Empty,
-    /// Contains an invalid digit.
+    /// Contains an invalid digit in its context.
     ///
     /// Among other causes, this variant will be constructed when parsing a string that
-    /// contains a letter.
+    /// contains a non-ASCII char.
+    ///
+    /// This variant is also constructed when a `+` or `-` is misplaced within a string
+    /// either on its own or in the middle of a number.
     InvalidDigit,
     /// Integer is too large to store in target integer type.
-    Overflow,
+    PosOverflow,
     /// Integer is too small to store in target integer type.
-    Underflow,
+    NegOverflow,
     /// Value was Zero
     ///
     /// This variant will be emitted when the parsing string has a value of zero, which
@@ -119,7 +122,7 @@ impl ParseIntError {
     #[unstable(
         feature = "int_error_matching",
         reason = "it can be useful to match errors when making error messages \
-                  for integer parsing",
+              for integer parsing",
         issue = "22639"
     )]
     pub fn kind(&self) -> &IntErrorKind {
@@ -136,8 +139,8 @@ impl ParseIntError {
         match self.kind {
             IntErrorKind::Empty => "cannot parse integer from empty string",
             IntErrorKind::InvalidDigit => "invalid digit found in string",
-            IntErrorKind::Overflow => "number too large to fit in target type",
-            IntErrorKind::Underflow => "number too small to fit in target type",
+            IntErrorKind::PosOverflow => "number too large to fit in target type",
+            IntErrorKind::NegOverflow => "number too small to fit in target type",
             IntErrorKind::Zero => "number would be zero for non-zero type",
         }
     }
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index c90887b89d0..9f5ae57b74a 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -63,7 +63,12 @@ pub use nonzero::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, No
 #[stable(feature = "try_from", since = "1.34.0")]
 pub use error::TryFromIntError;
 
-#[unstable(feature = "int_error_matching", issue = "22639")]
+#[unstable(
+    feature = "int_error_matching",
+    reason = "it can be useful to match errors when making error messages \
+              for integer parsing",
+    issue = "22639"
+)]
 pub use error::IntErrorKind;
 
 macro_rules! usize_isize_to_xe_bytes_doc {
@@ -830,15 +835,14 @@ fn from_str_radix<T: FromStrRadixHelper>(src: &str, radix: u32) -> Result<T, Par
     let src = src.as_bytes();
 
     let (is_positive, digits) = match src[0] {
+        b'+' | b'-' if src[1..].is_empty() => {
+            return Err(PIE { kind: InvalidDigit });
+        }
         b'+' => (true, &src[1..]),
         b'-' if is_signed_ty => (false, &src[1..]),
         _ => (true, src),
     };
 
-    if digits.is_empty() {
-        return Err(PIE { kind: Empty });
-    }
-
     let mut result = T::from_u32(0);
     if is_positive {
         // The number is positive
@@ -849,11 +853,11 @@ fn from_str_radix<T: FromStrRadixHelper>(src: &str, radix: u32) -> Result<T, Par
             };
             result = match result.checked_mul(radix) {
                 Some(result) => result,
-                None => return Err(PIE { kind: Overflow }),
+                None => return Err(PIE { kind: PosOverflow }),
             };
             result = match result.checked_add(x) {
                 Some(result) => result,
-                None => return Err(PIE { kind: Overflow }),
+                None => return Err(PIE { kind: PosOverflow }),
             };
         }
     } else {
@@ -865,11 +869,11 @@ fn from_str_radix<T: FromStrRadixHelper>(src: &str, radix: u32) -> Result<T, Par
             };
             result = match result.checked_mul(radix) {
                 Some(result) => result,
-                None => return Err(PIE { kind: Underflow }),
+                None => return Err(PIE { kind: NegOverflow }),
             };
             result = match result.checked_sub(x) {
                 Some(result) => result,
-                None => return Err(PIE { kind: Underflow }),
+                None => return Err(PIE { kind: NegOverflow }),
             };
         }
     }
diff --git a/library/core/tests/nonzero.rs b/library/core/tests/nonzero.rs
index 825e5e63b59..fb1293c99bb 100644
--- a/library/core/tests/nonzero.rs
+++ b/library/core/tests/nonzero.rs
@@ -135,11 +135,11 @@ fn test_from_str() {
     );
     assert_eq!(
         "-129".parse::<NonZeroI8>().err().map(|e| e.kind().clone()),
-        Some(IntErrorKind::Underflow)
+        Some(IntErrorKind::NegOverflow)
     );
     assert_eq!(
         "257".parse::<NonZeroU8>().err().map(|e| e.kind().clone()),
-        Some(IntErrorKind::Overflow)
+        Some(IntErrorKind::PosOverflow)
     );
 }
 
diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs
index 190528fd445..49e5cc0eaa5 100644
--- a/library/core/tests/num/mod.rs
+++ b/library/core/tests/num/mod.rs
@@ -2,10 +2,11 @@ use core::cmp::PartialEq;
 use core::convert::{TryFrom, TryInto};
 use core::fmt::Debug;
 use core::marker::Copy;
-use core::num::TryFromIntError;
+use core::num::{IntErrorKind, ParseIntError, TryFromIntError};
 use core::ops::{Add, Div, Mul, Rem, Sub};
 use core::option::Option;
-use core::option::Option::{None, Some};
+use core::option::Option::None;
+use core::str::FromStr;
 
 #[macro_use]
 mod int_macros;
@@ -67,6 +68,15 @@ where
     assert_eq!(ten.rem(two), ten % two);
 }
 
+/// Helper function for asserting number parsing returns a specific error
+fn test_parse<T>(num_str: &str, expected: Result<T, IntErrorKind>)
+where
+    T: FromStr<Err = ParseIntError>,
+    Result<T, IntErrorKind>: PartialEq + Debug,
+{
+    assert_eq!(num_str.parse::<T>().map_err(|e| e.kind().clone()), expected)
+}
+
 #[test]
 fn from_str_issue7588() {
     let u: Option<u8> = u8::from_str_radix("1000", 10).ok();
@@ -77,49 +87,52 @@ fn from_str_issue7588() {
 
 #[test]
 fn test_int_from_str_overflow() {
-    assert_eq!("127".parse::<i8>().ok(), Some(127i8));
-    assert_eq!("128".parse::<i8>().ok(), None);
+    test_parse::<i8>("127", Ok(127));
+    test_parse::<i8>("128", Err(IntErrorKind::PosOverflow));
 
-    assert_eq!("-128".parse::<i8>().ok(), Some(-128i8));
-    assert_eq!("-129".parse::<i8>().ok(), None);
+    test_parse::<i8>("-128", Ok(-128));
+    test_parse::<i8>("-129", Err(IntErrorKind::NegOverflow));
 
-    assert_eq!("32767".parse::<i16>().ok(), Some(32_767i16));
-    assert_eq!("32768".parse::<i16>().ok(), None);
+    test_parse::<i16>("32767", Ok(32_767));
+    test_parse::<i16>("32768", Err(IntErrorKind::PosOverflow));
 
-    assert_eq!("-32768".parse::<i16>().ok(), Some(-32_768i16));
-    assert_eq!("-32769".parse::<i16>().ok(), None);
+    test_parse::<i16>("-32768", Ok(-32_768));
+    test_parse::<i16>("-32769", Err(IntErrorKind::NegOverflow));
 
-    assert_eq!("2147483647".parse::<i32>().ok(), Some(2_147_483_647i32));
-    assert_eq!("2147483648".parse::<i32>().ok(), None);
+    test_parse::<i32>("2147483647", Ok(2_147_483_647));
+    test_parse::<i32>("2147483648", Err(IntErrorKind::PosOverflow));
 
-    assert_eq!("-2147483648".parse::<i32>().ok(), Some(-2_147_483_648i32));
-    assert_eq!("-2147483649".parse::<i32>().ok(), None);
+    test_parse::<i32>("-2147483648", Ok(-2_147_483_648));
+    test_parse::<i32>("-2147483649", Err(IntErrorKind::NegOverflow));
 
-    assert_eq!("9223372036854775807".parse::<i64>().ok(), Some(9_223_372_036_854_775_807i64));
-    assert_eq!("9223372036854775808".parse::<i64>().ok(), None);
+    test_parse::<i64>("9223372036854775807", Ok(9_223_372_036_854_775_807));
+    test_parse::<i64>("9223372036854775808", Err(IntErrorKind::PosOverflow));
 
-    assert_eq!("-9223372036854775808".parse::<i64>().ok(), Some(-9_223_372_036_854_775_808i64));
-    assert_eq!("-9223372036854775809".parse::<i64>().ok(), None);
+    test_parse::<i64>("-9223372036854775808", Ok(-9_223_372_036_854_775_808));
+    test_parse::<i64>("-9223372036854775809", Err(IntErrorKind::NegOverflow));
 }
 
 #[test]
 fn test_leading_plus() {
-    assert_eq!("+127".parse::<u8>().ok(), Some(127));
-    assert_eq!("+9223372036854775807".parse::<i64>().ok(), Some(9223372036854775807));
+    test_parse::<u8>("+127", Ok(127));
+    test_parse::<i64>("+9223372036854775807", Ok(9223372036854775807));
 }
 
 #[test]
 fn test_invalid() {
-    assert_eq!("--129".parse::<i8>().ok(), None);
-    assert_eq!("++129".parse::<i8>().ok(), None);
-    assert_eq!("Съешь".parse::<u8>().ok(), None);
+    test_parse::<i8>("--129", Err(IntErrorKind::InvalidDigit));
+    test_parse::<i8>("++129", Err(IntErrorKind::InvalidDigit));
+    test_parse::<u8>("Съешь", Err(IntErrorKind::InvalidDigit));
+    test_parse::<u8>("123Hello", Err(IntErrorKind::InvalidDigit));
+    test_parse::<i8>("--", Err(IntErrorKind::InvalidDigit));
+    test_parse::<i8>("-", Err(IntErrorKind::InvalidDigit));
+    test_parse::<i8>("+", Err(IntErrorKind::InvalidDigit));
+    test_parse::<u8>("-1", Err(IntErrorKind::InvalidDigit));
 }
 
 #[test]
 fn test_empty() {
-    assert_eq!("-".parse::<i8>().ok(), None);
-    assert_eq!("+".parse::<i8>().ok(), None);
-    assert_eq!("".parse::<u8>().ok(), None);
+    test_parse::<u8>("", Err(IntErrorKind::Empty));
 }
 
 #[test]
diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 161bfe3795c..c256f556b3c 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -1701,10 +1701,14 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
 /// The `dst` path will be a link pointing to the `src` path. Note that systems
 /// often require these two paths to both be located on the same filesystem.
 ///
+/// If `src` names a symbolic link, it is platform-specific whether the symbolic
+/// link is followed. On platforms where it's possible to not follow it, it is
+/// not followed, and the created hard link points to the symbolic link itself.
+///
 /// # Platform-specific behavior
 ///
-/// This function currently corresponds to the `link` function on Unix
-/// and the `CreateHardLink` function on Windows.
+/// This function currently corresponds to the `linkat` function with no flags
+/// on Unix and the `CreateHardLink` function on Windows.
 /// Note that, this [may change in the future][changes].
 ///
 /// [changes]: io#platform-specific-behavior
diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs
index 38fd470a1c3..0642dca8e48 100644
--- a/library/std/src/fs/tests.rs
+++ b/library/std/src/fs/tests.rs
@@ -1336,3 +1336,54 @@ fn metadata_access_times() {
         }
     }
 }
+
+/// Test creating hard links to symlinks.
+#[test]
+fn symlink_hard_link() {
+    let tmpdir = tmpdir();
+
+    // Create "file", a file.
+    check!(fs::File::create(tmpdir.join("file")));
+
+    // Create "symlink", a symlink to "file".
+    check!(symlink_file("file", tmpdir.join("symlink")));
+
+    // Create "hard_link", a hard link to "symlink".
+    check!(fs::hard_link(tmpdir.join("symlink"), tmpdir.join("hard_link")));
+
+    // "hard_link" should appear as a symlink.
+    assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
+
+    // We sould be able to open "file" via any of the above names.
+    let _ = check!(fs::File::open(tmpdir.join("file")));
+    assert!(fs::File::open(tmpdir.join("file.renamed")).is_err());
+    let _ = check!(fs::File::open(tmpdir.join("symlink")));
+    let _ = check!(fs::File::open(tmpdir.join("hard_link")));
+
+    // Rename "file" to "file.renamed".
+    check!(fs::rename(tmpdir.join("file"), tmpdir.join("file.renamed")));
+
+    // Now, the symlink and the hard link should be dangling.
+    assert!(fs::File::open(tmpdir.join("file")).is_err());
+    let _ = check!(fs::File::open(tmpdir.join("file.renamed")));
+    assert!(fs::File::open(tmpdir.join("symlink")).is_err());
+    assert!(fs::File::open(tmpdir.join("hard_link")).is_err());
+
+    // The symlink and the hard link should both still point to "file".
+    assert!(fs::read_link(tmpdir.join("file")).is_err());
+    assert!(fs::read_link(tmpdir.join("file.renamed")).is_err());
+    assert_eq!(check!(fs::read_link(tmpdir.join("symlink"))), Path::new("file"));
+    assert_eq!(check!(fs::read_link(tmpdir.join("hard_link"))), Path::new("file"));
+
+    // Remove "file.renamed".
+    check!(fs::remove_file(tmpdir.join("file.renamed")));
+
+    // Now, we can't open the file by any name.
+    assert!(fs::File::open(tmpdir.join("file")).is_err());
+    assert!(fs::File::open(tmpdir.join("file.renamed")).is_err());
+    assert!(fs::File::open(tmpdir.join("symlink")).is_err());
+    assert!(fs::File::open(tmpdir.join("hard_link")).is_err());
+
+    // "hard_link" should still appear as a symlink.
+    assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
+}
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index d27d6e2c565..96594095cc3 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -1081,7 +1081,20 @@ pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> {
 pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
     let src = cstr(src)?;
     let dst = cstr(dst)?;
-    cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?;
+    cfg_if::cfg_if! {
+        if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android"))] {
+            // VxWorks, Redox, and old versions of Android lack `linkat`, so use
+            // `link` instead. POSIX leaves it implementation-defined whether
+            // `link` follows symlinks, so rely on the `symlink_hard_link` test
+            // in library/std/src/fs/tests.rs to check the behavior.
+            cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?;
+        } else {
+            // Use `linkat` with `AT_FDCWD` instead of `link` as `linkat` gives
+            // us a flag to specify how symlinks should be handled. Pass 0 as
+            // the flags argument, meaning don't follow symlinks.
+            cvt(unsafe { libc::linkat(libc::AT_FDCWD, src.as_ptr(), libc::AT_FDCWD, dst.as_ptr(), 0) })?;
+        }
+    }
     Ok(())
 }
 
diff --git a/library/std/src/sys/unix/process/process_common/tests.rs b/library/std/src/sys/unix/process/process_common/tests.rs
index e72fbf0beb4..10aa34e9443 100644
--- a/library/std/src/sys/unix/process/process_common/tests.rs
+++ b/library/std/src/sys/unix/process/process_common/tests.rs
@@ -14,17 +14,22 @@ macro_rules! t {
     };
 }
 
-// See #14232 for more information, but it appears that signal delivery to a
-// newly spawned process may just be raced in the macOS, so to prevent this
-// test from being flaky we ignore it on macOS.
 #[test]
-#[cfg_attr(target_os = "macos", ignore)]
-// When run under our current QEMU emulation test suite this test fails,
-// although the reason isn't very clear as to why. For now this test is
-// ignored there.
-#[cfg_attr(target_arch = "arm", ignore)]
-#[cfg_attr(target_arch = "aarch64", ignore)]
-#[cfg_attr(target_arch = "riscv64", ignore)]
+#[cfg_attr(
+    any(
+        // See #14232 for more information, but it appears that signal delivery to a
+        // newly spawned process may just be raced in the macOS, so to prevent this
+        // test from being flaky we ignore it on macOS.
+        target_os = "macos",
+        // When run under our current QEMU emulation test suite this test fails,
+        // although the reason isn't very clear as to why. For now this test is
+        // ignored there.
+        target_arch = "arm",
+        target_arch = "aarch64",
+        target_arch = "riscv64",
+    ),
+    ignore
+)]
 fn test_process_mask() {
     unsafe {
         // Test to make sure that a signal mask does not get inherited.
diff --git a/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile
index df65f9df441..95c54ca1abc 100644
--- a/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile
@@ -35,6 +35,5 @@ ENV HOSTS=aarch64-unknown-linux-gnu
 ENV RUST_CONFIGURE_ARGS \
       --enable-full-tools \
       --enable-profiler \
-      --enable-sanitizers \
-      --disable-docs
+      --enable-sanitizers
 ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS
diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml
index 4cbb7b62cd6..889c98966eb 100644
--- a/src/ci/github-actions/ci.yml
+++ b/src/ci/github-actions/ci.yml
@@ -301,6 +301,9 @@ jobs:
           #   Linux/Docker builders   #
           #############################
 
+          - name: aarch64-gnu
+            <<: *job-aarch64-linux
+
           - name: arm-android
             <<: *job-linux-xl
 
@@ -635,23 +638,6 @@ jobs:
               SCRIPT: python x.py dist
             <<: *job-windows-xl
 
-  auto-fallible:
-    <<: *base-ci-job
-    name: auto-fallible
-    env:
-      <<: [*shared-ci-variables, *dummy-variables]
-    if: github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust'
-    strategy:
-      fail-fast: false
-      matrix:
-        include:
-          #############################
-          #   Linux/Docker builders   #
-          #############################
-
-          - name: aarch64-gnu
-            <<: *job-aarch64-linux
-
   try:
     <<: *base-ci-job
     name: try
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 85c6f91f085..8005a5f3563 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -34,6 +34,7 @@ Specifically they will each satisfy the following requirements:
 
 target | std | host | notes
 -------|-----|------|-------
+`aarch64-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (kernel 4.2, glibc 2.17+) [^missing-stack-probes]
 `i686-pc-windows-gnu` | ✓ | ✓ | 32-bit MinGW (Windows 7+)
 `i686-pc-windows-msvc` | ✓ | ✓ | 32-bit MSVC (Windows 7+)
 `i686-unknown-linux-gnu` | ✓ | ✓ | 32-bit Linux (kernel 2.6.32+, glibc 2.11+)
@@ -42,6 +43,12 @@ target | std | host | notes
 `x86_64-pc-windows-msvc` | ✓ | ✓ | 64-bit MSVC (Windows 7+)
 `x86_64-unknown-linux-gnu` | ✓ | ✓ | 64-bit Linux (kernel 2.6.32+, glibc 2.11+)
 
+[^missing-stack-probes]: Stack probes support is missing on
+  `aarch64-unknown-linux-gnu`, but it's planned to be implemented in the near
+  future. The implementation is tracked on [issue #77071][77071].
+
+[77071]: https://github.com/rust-lang/rust/issues/77071
+
 ## Tier 2
 
 Tier 2 platforms can be thought of as "guaranteed to build". Automated tests
@@ -62,7 +69,6 @@ target | std | host | notes
 `aarch64-fuchsia` | ✓ |  | ARM64 Fuchsia
 `aarch64-linux-android` | ✓ |  | ARM64 Android
 `aarch64-pc-windows-msvc` | ✓ | ✓ | ARM64 Windows MSVC
-`aarch64-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (kernel 4.2, glibc 2.17)
 `aarch64-unknown-linux-musl` | ✓ | ✓ | ARM64 Linux with MUSL
 `aarch64-unknown-none` | * |  | Bare ARM64, hardfloat
 `aarch64-unknown-none-softfloat` | * |  | Bare ARM64, softfloat
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 5eca54199d6..4cad6418d6a 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -322,7 +322,8 @@ pub fn run_core(
     let cpath = Some(input.clone());
     let input = Input::File(input);
 
-    let intra_link_resolution_failure_name = lint::builtin::BROKEN_INTRA_DOC_LINKS.name;
+    let broken_intra_doc_links = lint::builtin::BROKEN_INTRA_DOC_LINKS.name;
+    let private_intra_doc_links = lint::builtin::PRIVATE_INTRA_DOC_LINKS.name;
     let missing_docs = rustc_lint::builtin::MISSING_DOCS.name;
     let missing_doc_example = rustc_lint::builtin::MISSING_DOC_CODE_EXAMPLES.name;
     let private_doc_tests = rustc_lint::builtin::PRIVATE_DOC_TESTS.name;
@@ -336,7 +337,8 @@ pub fn run_core(
     // In addition to those specific lints, we also need to allow those given through
     // command line, otherwise they'll get ignored and we don't want that.
     let lints_to_show = vec![
-        intra_link_resolution_failure_name.to_owned(),
+        broken_intra_doc_links.to_owned(),
+        private_intra_doc_links.to_owned(),
         missing_docs.to_owned(),
         missing_doc_example.to_owned(),
         private_doc_tests.to_owned(),
@@ -349,9 +351,8 @@ pub fn run_core(
     ];
 
     let (lint_opts, lint_caps) = init_lints(lints_to_show, lint_opts, |lint| {
-        if lint.name == intra_link_resolution_failure_name
-            || lint.name == invalid_codeblock_attributes_name
-        {
+        // FIXME: why is this necessary?
+        if lint.name == broken_intra_doc_links || lint.name == invalid_codeblock_attributes_name {
             None
         } else {
             Some((lint.name_lower(), lint::Allow))
diff --git a/src/librustdoc/html/static/settings.css b/src/librustdoc/html/static/settings.css
index 4bacd7b245b..fb8990b30e2 100644
--- a/src/librustdoc/html/static/settings.css
+++ b/src/librustdoc/html/static/settings.css
@@ -26,7 +26,8 @@
 }
 
 .toggle input {
-	display: none;
+	opacity: 0;
+	position: absolute;
 }
 
 .select-wrapper {
@@ -90,7 +91,7 @@ input:checked + .slider {
 }
 
 input:focus + .slider {
-	box-shadow: 0 0 1px #2196F3;
+	box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3);
 }
 
 input:checked + .slider:before {
diff --git a/src/test/mir-opt/inline/inline-shims.rs b/src/test/mir-opt/inline/inline-shims.rs
new file mode 100644
index 00000000000..7c8618f71e5
--- /dev/null
+++ b/src/test/mir-opt/inline/inline-shims.rs
@@ -0,0 +1,13 @@
+// ignore-wasm32-bare compiled with panic=abort by default
+#![crate_type = "lib"]
+
+// EMIT_MIR inline_shims.clone.Inline.diff
+pub fn clone<A, B>(f: fn(A, B)) -> fn(A, B) {
+    f.clone()
+}
+
+// EMIT_MIR inline_shims.drop.Inline.diff
+pub fn drop<A, B>(a: *mut Vec<A>, b: *mut Option<B>) {
+    unsafe { std::ptr::drop_in_place(a) }
+    unsafe { std::ptr::drop_in_place(b) }
+}
diff --git a/src/test/mir-opt/inline/inline_shims.clone.Inline.diff b/src/test/mir-opt/inline/inline_shims.clone.Inline.diff
new file mode 100644
index 00000000000..3bdd4f4ff56
--- /dev/null
+++ b/src/test/mir-opt/inline/inline_shims.clone.Inline.diff
@@ -0,0 +1,26 @@
+- // MIR for `clone` before Inline
++ // MIR for `clone` after Inline
+  
+  fn clone(_1: fn(A, B)) -> fn(A, B) {
+      debug f => _1;                       // in scope 0 at $DIR/inline-shims.rs:5:20: 5:21
+      let mut _0: fn(A, B);                // return place in scope 0 at $DIR/inline-shims.rs:5:36: 5:44
+      let mut _2: &fn(A, B);               // in scope 0 at $DIR/inline-shims.rs:6:5: 6:6
++     scope 1 (inlined <fn(A, B) as Clone>::clone - shim(fn(A, B))) { // at $DIR/inline-shims.rs:6:5: 6:14
++     }
+  
+      bb0: {
+          StorageLive(_2);                 // scope 0 at $DIR/inline-shims.rs:6:5: 6:6
+          _2 = &_1;                        // scope 0 at $DIR/inline-shims.rs:6:5: 6:6
+-         _0 = <fn(A, B) as Clone>::clone(move _2) -> bb1; // scope 0 at $DIR/inline-shims.rs:6:5: 6:14
+-                                          // mir::Constant
+-                                          // + span: $DIR/inline-shims.rs:6:7: 6:12
+-                                          // + literal: Const { ty: for<'r> fn(&'r fn(A, B)) -> fn(A, B) {<fn(A, B) as std::clone::Clone>::clone}, val: Value(Scalar(<ZST>)) }
+-     }
+- 
+-     bb1: {
++         _0 = (*_2);                      // scope 1 at $DIR/inline-shims.rs:6:5: 6:14
+          StorageDead(_2);                 // scope 0 at $DIR/inline-shims.rs:6:13: 6:14
+          return;                          // scope 0 at $DIR/inline-shims.rs:7:2: 7:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/inline/inline_shims.drop.Inline.diff b/src/test/mir-opt/inline/inline_shims.drop.Inline.diff
new file mode 100644
index 00000000000..503d8bc6b7a
--- /dev/null
+++ b/src/test/mir-opt/inline/inline_shims.drop.Inline.diff
@@ -0,0 +1,52 @@
+- // MIR for `drop` before Inline
++ // MIR for `drop` after Inline
+  
+  fn drop(_1: *mut Vec<A>, _2: *mut Option<B>) -> () {
+      debug a => _1;                       // in scope 0 at $DIR/inline-shims.rs:10:19: 10:20
+      debug b => _2;                       // in scope 0 at $DIR/inline-shims.rs:10:35: 10:36
+      let mut _0: ();                      // return place in scope 0 at $DIR/inline-shims.rs:10:54: 10:54
+      let _3: ();                          // in scope 0 at $DIR/inline-shims.rs:11:14: 11:40
+      let mut _4: *mut std::vec::Vec<A>;   // in scope 0 at $DIR/inline-shims.rs:11:38: 11:39
+      let mut _5: *mut std::option::Option<B>; // in scope 0 at $DIR/inline-shims.rs:12:38: 12:39
+      scope 1 {
+      }
+      scope 2 {
++         scope 3 (inlined drop_in_place::<Option<B>> - shim(Some(Option<B>))) { // at $DIR/inline-shims.rs:12:14: 12:40
++             let mut _6: isize;           // in scope 3 at $DIR/inline-shims.rs:12:14: 12:40
++             let mut _7: isize;           // in scope 3 at $DIR/inline-shims.rs:12:14: 12:40
++         }
+      }
+  
+      bb0: {
+          StorageLive(_3);                 // scope 0 at $DIR/inline-shims.rs:11:5: 11:42
+          StorageLive(_4);                 // scope 1 at $DIR/inline-shims.rs:11:38: 11:39
+          _4 = _1;                         // scope 1 at $DIR/inline-shims.rs:11:38: 11:39
+          _3 = drop_in_place::<Vec<A>>(move _4) -> bb1; // scope 1 at $DIR/inline-shims.rs:11:14: 11:40
+                                           // mir::Constant
+                                           // + span: $DIR/inline-shims.rs:11:14: 11:37
+                                           // + literal: Const { ty: unsafe fn(*mut std::vec::Vec<A>) {std::intrinsics::drop_in_place::<std::vec::Vec<A>>}, val: Value(Scalar(<ZST>)) }
+      }
+  
+      bb1: {
+          StorageDead(_4);                 // scope 1 at $DIR/inline-shims.rs:11:39: 11:40
+          StorageDead(_3);                 // scope 0 at $DIR/inline-shims.rs:11:41: 11:42
+          StorageLive(_5);                 // scope 2 at $DIR/inline-shims.rs:12:38: 12:39
+          _5 = _2;                         // scope 2 at $DIR/inline-shims.rs:12:38: 12:39
+-         _0 = drop_in_place::<Option<B>>(move _5) -> bb2; // scope 2 at $DIR/inline-shims.rs:12:14: 12:40
+-                                          // mir::Constant
+-                                          // + span: $DIR/inline-shims.rs:12:14: 12:37
+-                                          // + literal: Const { ty: unsafe fn(*mut std::option::Option<B>) {std::intrinsics::drop_in_place::<std::option::Option<B>>}, val: Value(Scalar(<ZST>)) }
++         _6 = discriminant((*_5));        // scope 3 at $DIR/inline-shims.rs:12:14: 12:40
++         switchInt(move _6) -> [0_isize: bb2, otherwise: bb3]; // scope 3 at $DIR/inline-shims.rs:12:14: 12:40
+      }
+  
+      bb2: {
+          StorageDead(_5);                 // scope 2 at $DIR/inline-shims.rs:12:39: 12:40
+          return;                          // scope 0 at $DIR/inline-shims.rs:13:2: 13:2
++     }
++ 
++     bb3: {
++         drop((((*_5) as Some).0: B)) -> bb2; // scope 3 at $DIR/inline-shims.rs:12:14: 12:40
+      }
+  }
+  
diff --git a/src/test/ui/bad/bad-expr-lhs.rs b/src/test/ui/bad/bad-expr-lhs.rs
index d7cf1b77005..39536f12e3b 100644
--- a/src/test/ui/bad/bad-expr-lhs.rs
+++ b/src/test/ui/bad/bad-expr-lhs.rs
@@ -1,10 +1,12 @@
 fn main() {
     1 = 2; //~ ERROR invalid left-hand side of assignment
     1 += 2; //~ ERROR invalid left-hand side of assignment
-    (1, 2) = (3, 4); //~ ERROR invalid left-hand side of assignment
+    (1, 2) = (3, 4); //~ ERROR destructuring assignments are unstable
+    //~| ERROR invalid left-hand side of assignment
+    //~| ERROR invalid left-hand side of assignment
 
     let (a, b) = (1, 2);
-    (a, b) = (3, 4); //~ ERROR invalid left-hand side of assignment
+    (a, b) = (3, 4); //~ ERROR destructuring assignments are unstable
 
     None = Some(3); //~ ERROR invalid left-hand side of assignment
 }
diff --git a/src/test/ui/bad/bad-expr-lhs.stderr b/src/test/ui/bad/bad-expr-lhs.stderr
index a195e1054d0..d4b2193d09f 100644
--- a/src/test/ui/bad/bad-expr-lhs.stderr
+++ b/src/test/ui/bad/bad-expr-lhs.stderr
@@ -1,3 +1,25 @@
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/bad-expr-lhs.rs:4:12
+   |
+LL |     (1, 2) = (3, 4);
+   |     ------ ^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/bad-expr-lhs.rs:9:12
+   |
+LL |     (a, b) = (3, 4);
+   |     ------ ^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
 error[E0070]: invalid left-hand side of assignment
   --> $DIR/bad-expr-lhs.rs:2:7
    |
@@ -18,30 +40,27 @@ error[E0070]: invalid left-hand side of assignment
   --> $DIR/bad-expr-lhs.rs:4:12
    |
 LL |     (1, 2) = (3, 4);
-   |     ------ ^
-   |     |
-   |     cannot assign to this expression
+   |      -     ^
+   |      |
+   |      cannot assign to this expression
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/bad-expr-lhs.rs:7:12
+  --> $DIR/bad-expr-lhs.rs:4:12
    |
-LL |     (a, b) = (3, 4);
-   |     ------ ^
-   |     |
-   |     cannot assign to this expression
-   |
-   = note: destructuring assignments are not currently supported
-   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
+LL |     (1, 2) = (3, 4);
+   |         -  ^
+   |         |
+   |         cannot assign to this expression
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/bad-expr-lhs.rs:9:10
+  --> $DIR/bad-expr-lhs.rs:11:10
    |
 LL |     None = Some(3);
    |     ---- ^
    |     |
    |     cannot assign to this expression
 
-error: aborting due to 5 previous errors
+error: aborting due to 7 previous errors
 
-Some errors have detailed explanations: E0067, E0070.
+Some errors have detailed explanations: E0067, E0070, E0658.
 For more information about an error, try `rustc --explain E0067`.
diff --git a/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.rs b/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.rs
new file mode 100644
index 00000000000..adecd0ff291
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.rs
@@ -0,0 +1,7 @@
+#![feature(destructuring_assignment)]
+
+fn main() {
+    let mut x = &0;
+    let mut y = &0;
+    (x, y) = &(1, 2); //~ ERROR mismatched types
+}
diff --git a/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.stderr b/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.stderr
new file mode 100644
index 00000000000..e6161fdfa24
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.stderr
@@ -0,0 +1,14 @@
+error[E0308]: mismatched types
+  --> $DIR/default-match-bindings-forbidden.rs:6:5
+   |
+LL |     (x, y) = &(1, 2);
+   |     ^^^^^^   ------- this expression has type `&({integer}, {integer})`
+   |     |
+   |     expected reference, found tuple
+   |
+   = note: expected type `&({integer}, {integer})`
+             found tuple `(_, _)`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/destructuring-assignment/note-unsupported.rs b/src/test/ui/destructuring-assignment/note-unsupported.rs
index 876c9efea26..e0cb9dc9158 100644
--- a/src/test/ui/destructuring-assignment/note-unsupported.rs
+++ b/src/test/ui/destructuring-assignment/note-unsupported.rs
@@ -3,23 +3,24 @@ struct S { x: u8, y: u8 }
 fn main() {
     let (a, b) = (1, 2);
 
-    (a, b) = (3, 4); //~ ERROR invalid left-hand side of assignment
+    (a, b) = (3, 4); //~ ERROR destructuring assignments are unstable
     (a, b) += (3, 4); //~ ERROR invalid left-hand side of assignment
-    //~^ ERROR binary assignment operation `+=` cannot be applied
+    //~| ERROR binary assignment operation `+=` cannot be applied
 
     [a, b] = [3, 4]; //~ ERROR invalid left-hand side of assignment
     [a, b] += [3, 4]; //~ ERROR invalid left-hand side of assignment
-    //~^ ERROR binary assignment operation `+=` cannot be applied
+    //~| ERROR binary assignment operation `+=` cannot be applied
 
     let s = S { x: 3, y: 4 };
 
     S { x: a, y: b } = s; //~ ERROR invalid left-hand side of assignment
     S { x: a, y: b } += s; //~ ERROR invalid left-hand side of assignment
-    //~^ ERROR binary assignment operation `+=` cannot be applied
+    //~| ERROR binary assignment operation `+=` cannot be applied
 
-    S { x: a, ..s } = S { x: 3, y: 4 }; //~ ERROR invalid left-hand side of assignment
+    S { x: a, ..s } = S { x: 3, y: 4 };
+    //~^ ERROR invalid left-hand side of assignment
 
     let c = 3;
 
-    ((a, b), c) = ((3, 4), 5); //~ ERROR invalid left-hand side of assignment
+    ((a, b), c) = ((3, 4), 5); //~ ERROR destructuring assignments are unstable
 }
diff --git a/src/test/ui/destructuring-assignment/note-unsupported.stderr b/src/test/ui/destructuring-assignment/note-unsupported.stderr
index d4e25930d22..c5543fab825 100644
--- a/src/test/ui/destructuring-assignment/note-unsupported.stderr
+++ b/src/test/ui/destructuring-assignment/note-unsupported.stderr
@@ -1,4 +1,4 @@
-error[E0070]: invalid left-hand side of assignment
+error[E0658]: destructuring assignments are unstable
   --> $DIR/note-unsupported.rs:6:12
    |
 LL |     (a, b) = (3, 4);
@@ -6,8 +6,19 @@ LL |     (a, b) = (3, 4);
    |     |
    |     cannot assign to this expression
    |
-   = note: destructuring assignments are not currently supported
-   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/note-unsupported.rs:25:17
+   |
+LL |     ((a, b), c) = ((3, 4), 5);
+   |     ----------- ^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
 
 error[E0368]: binary assignment operation `+=` cannot be applied to type `({integer}, {integer})`
   --> $DIR/note-unsupported.rs:7:5
@@ -24,9 +35,6 @@ LL |     (a, b) += (3, 4);
    |     ------ ^^
    |     |
    |     cannot assign to this expression
-   |
-   = note: destructuring assignments are not currently supported
-   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0070]: invalid left-hand side of assignment
   --> $DIR/note-unsupported.rs:10:12
@@ -35,9 +43,6 @@ LL |     [a, b] = [3, 4];
    |     ------ ^
    |     |
    |     cannot assign to this expression
-   |
-   = note: destructuring assignments are not currently supported
-   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0368]: binary assignment operation `+=` cannot be applied to type `[{integer}; 2]`
   --> $DIR/note-unsupported.rs:11:5
@@ -54,9 +59,6 @@ LL |     [a, b] += [3, 4];
    |     ------ ^^
    |     |
    |     cannot assign to this expression
-   |
-   = note: destructuring assignments are not currently supported
-   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0070]: invalid left-hand side of assignment
   --> $DIR/note-unsupported.rs:16:22
@@ -65,9 +67,6 @@ LL |     S { x: a, y: b } = s;
    |     ---------------- ^
    |     |
    |     cannot assign to this expression
-   |
-   = note: destructuring assignments are not currently supported
-   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0368]: binary assignment operation `+=` cannot be applied to type `S`
   --> $DIR/note-unsupported.rs:17:5
@@ -86,9 +85,6 @@ LL |     S { x: a, y: b } += s;
    |     ---------------- ^^
    |     |
    |     cannot assign to this expression
-   |
-   = note: destructuring assignments are not currently supported
-   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error[E0070]: invalid left-hand side of assignment
   --> $DIR/note-unsupported.rs:20:21
@@ -97,22 +93,8 @@ LL |     S { x: a, ..s } = S { x: 3, y: 4 };
    |     --------------- ^
    |     |
    |     cannot assign to this expression
-   |
-   = note: destructuring assignments are not currently supported
-   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
-
-error[E0070]: invalid left-hand side of assignment
-  --> $DIR/note-unsupported.rs:24:17
-   |
-LL |     ((a, b), c) = ((3, 4), 5);
-   |     ----------- ^
-   |     |
-   |     cannot assign to this expression
-   |
-   = note: destructuring assignments are not currently supported
-   = note: for more information, see https://github.com/rust-lang/rfcs/issues/372
 
 error: aborting due to 11 previous errors
 
-Some errors have detailed explanations: E0067, E0070, E0368.
+Some errors have detailed explanations: E0067, E0070, E0368, E0658.
 For more information about an error, try `rustc --explain E0067`.
diff --git a/src/test/ui/destructuring-assignment/tuple_destructure.rs b/src/test/ui/destructuring-assignment/tuple_destructure.rs
new file mode 100644
index 00000000000..16aafc4693f
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/tuple_destructure.rs
@@ -0,0 +1,37 @@
+// run-pass
+
+#![feature(destructuring_assignment)]
+
+fn main() {
+    let (mut a, mut b);
+    (a, b) = (0, 1);
+    assert_eq!((a, b), (0, 1));
+    (b, a) = (a, b);
+    assert_eq!((a, b), (1, 0));
+    (a, .., b) = (1, 2);
+    assert_eq!((a, b), (1, 2));
+    (.., a) = (1, 2);
+    assert_eq!((a, b), (2, 2));
+    (..) = (3, 4);
+    assert_eq!((a, b), (2, 2));
+    (b, ..) = (5, 6, 7);
+    assert_eq!(b, 5);
+
+    // Test for a non-Copy type (String):
+    let (mut c, mut d);
+    (c, d) = ("c".to_owned(), "d".to_owned());
+    assert_eq!(c, "c");
+    assert_eq!(d, "d");
+    (d, c) = (c, d);
+    assert_eq!(c, "d");
+    assert_eq!(d, "c");
+
+    // Test nesting/parentheses:
+    ((a, b)) = (0, 1);
+    assert_eq!((a, b), (0, 1));
+    (((a, b)), (c)) = ((2, 3), d);
+    assert_eq!((a, b), (2, 3));
+    assert_eq!(c, "c");
+    ((a, .., b), .., (..)) = ((4, 5), ());
+    assert_eq!((a, b), (4, 5));
+}
diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs
new file mode 100644
index 00000000000..b76f4968e62
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs
@@ -0,0 +1,10 @@
+#![feature(destructuring_assignment)]
+
+const C: i32 = 1;
+
+fn main() {
+    let (mut a, mut b);
+    (a, .., b, ..) = (0, 1); //~ ERROR `..` can only be used once per tuple pattern
+    (a, a, b) = (1, 2); //~ ERROR mismatched types
+    (C, ..) = (0,1); //~ ERROR invalid left-hand side of assignment
+}
diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr
new file mode 100644
index 00000000000..a60e1cb1eec
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr
@@ -0,0 +1,31 @@
+error: `..` can only be used once per tuple pattern
+  --> $DIR/tuple_destructure_fail.rs:7:16
+   |
+LL |     (a, .., b, ..) = (0, 1);
+   |         --     ^^ can only be used once per tuple pattern
+   |         |
+   |         previously used here
+
+error[E0308]: mismatched types
+  --> $DIR/tuple_destructure_fail.rs:8:5
+   |
+LL |     (a, a, b) = (1, 2);
+   |     ^^^^^^^^^   ------ this expression has type `({integer}, {integer})`
+   |     |
+   |     expected a tuple with 2 elements, found one with 3 elements
+   |
+   = note: expected type `({integer}, {integer})`
+             found tuple `(_, _, _)`
+
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/tuple_destructure_fail.rs:9:13
+   |
+LL |     (C, ..) = (0,1);
+   |      -      ^
+   |      |
+   |      cannot assign to this expression
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0070, E0308.
+For more information about an error, try `rustc --explain E0070`.
diff --git a/src/test/ui/destructuring-assignment/warn-unused-duplication.rs b/src/test/ui/destructuring-assignment/warn-unused-duplication.rs
new file mode 100644
index 00000000000..c1c5c2cd3ce
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/warn-unused-duplication.rs
@@ -0,0 +1,23 @@
+// run-pass
+
+#![feature(destructuring_assignment)]
+
+#![warn(unused_assignments)]
+
+fn main() {
+    let mut a;
+    // Assignment occurs left-to-right.
+    // However, we emit warnings when this happens, so it is clear that this is happening.
+    (a, a) = (0, 1); //~ WARN value assigned to `a` is never read
+    assert_eq!(a, 1);
+
+    // We can't always tell when a variable is being assigned to twice, which is why we don't try
+    // to emit an error, which would be fallible.
+    let mut x = 1;
+    (*foo(&mut x), *foo(&mut x)) = (5, 6);
+    assert_eq!(x, 6);
+}
+
+fn foo<'a>(x: &'a mut u32) -> &'a mut u32 {
+    x
+}
diff --git a/src/test/ui/destructuring-assignment/warn-unused-duplication.stderr b/src/test/ui/destructuring-assignment/warn-unused-duplication.stderr
new file mode 100644
index 00000000000..b87ef6f1571
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/warn-unused-duplication.stderr
@@ -0,0 +1,15 @@
+warning: value assigned to `a` is never read
+  --> $DIR/warn-unused-duplication.rs:11:6
+   |
+LL |     (a, a) = (0, 1);
+   |      ^
+   |
+note: the lint level is defined here
+  --> $DIR/warn-unused-duplication.rs:5:9
+   |
+LL | #![warn(unused_assignments)]
+   |         ^^^^^^^^^^^^^^^^^^
+   = help: maybe it is overwritten before being read?
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/feature-gates/feature-gate-destructuring_assignment.rs b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.rs
new file mode 100644
index 00000000000..e7801f0e8ec
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let (a, b) = (0, 1);
+    (a, b) = (2, 3); //~ ERROR destructuring assignments are unstable
+}
diff --git a/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr
new file mode 100644
index 00000000000..62e71decb32
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr
@@ -0,0 +1,14 @@
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/feature-gate-destructuring_assignment.rs:3:12
+   |
+LL |     (a, b) = (2, 3);
+   |     ------ ^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs
index 9a8f2404e4a..687354dc6ae 100644
--- a/src/tools/build-manifest/src/main.rs
+++ b/src/tools/build-manifest/src/main.rs
@@ -155,6 +155,7 @@ static TARGETS: &[&str] = &[
 ];
 
 static DOCS_TARGETS: &[&str] = &[
+    "aarch64-unknown-linux-gnu",
     "i686-apple-darwin",
     "i686-pc-windows-gnu",
     "i686-pc-windows-msvc",