about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--compiler/rustc/build.rs3
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs27
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs9
-rw-r--r--compiler/rustc_const_eval/src/interpret/mod.rs1
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs212
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs310
-rw-r--r--compiler/rustc_const_eval/src/interpret/projection.rs393
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs30
-rw-r--r--compiler/rustc_const_eval/src/interpret/visitor.rs24
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs2
-rw-r--r--compiler/rustc_error_messages/locales/en-US/expand.ftl5
-rw-r--r--compiler/rustc_error_messages/src/lib.rs3
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs8
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs1
-rw-r--r--compiler/rustc_errors/src/lib.rs2
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs38
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs8
-rw-r--r--compiler/rustc_middle/src/ty/generics.rs4
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs4
-rw-r--r--compiler/rustc_middle/src/ty/util.rs4
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs57
-rw-r--r--compiler/rustc_mir_transform/src/shim.rs9
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs32
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs2
-rw-r--r--compiler/rustc_resolve/src/late.rs24
-rw-r--r--compiler/rustc_span/src/source_map.rs7
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_none.rs1
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs1
-rw-r--r--compiler/rustc_target/src/spec/apple_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/apple_sdk_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/armebv7r_none_eabi.rs1
-rw-r--r--compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs1
-rw-r--r--compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs1
-rw-r--r--compiler/rustc_target/src/spec/armv7a_none_eabi.rs1
-rw-r--r--compiler/rustc_target/src/spec/armv7a_none_eabihf.rs1
-rw-r--r--compiler/rustc_target/src/spec/armv7r_none_eabi.rs1
-rw-r--r--compiler/rustc_target/src/spec/armv7r_none_eabihf.rs1
-rw-r--r--compiler/rustc_target/src/spec/avr_gnu_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/bpf_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/dragonfly_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/freebsd_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/fuchsia_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/haiku_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/hermit_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs1
-rw-r--r--compiler/rustc_target/src/spec/illumos_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/l4re_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/linux_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/mipsel_sony_psp.rs1
-rw-r--r--compiler/rustc_target/src/spec/mipsel_unknown_none.rs1
-rw-r--r--compiler/rustc_target/src/spec/mod.rs5
-rw-r--r--compiler/rustc_target/src/spec/msp430_none_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/msvc_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/netbsd_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs1
-rw-r--r--compiler/rustc_target/src/spec/openbsd_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/redox_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs1
-rw-r--r--compiler/rustc_target/src/spec/solaris_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/solid_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/thumb_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/vxworks_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/wasm_base.rs3
-rw-r--r--compiler/rustc_target/src/spec/windows_gnu_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/windows_gnullvm_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_none.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs27
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_match.rs22
-rw-r--r--compiler/rustc_typeck/src/astconv/errors.rs56
-rw-r--r--compiler/rustc_typeck/src/astconv/mod.rs2
-rw-r--r--compiler/rustc_typeck/src/check/demand.rs4
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs49
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs2
-rw-r--r--compiler/rustc_typeck/src/check/intrinsicck.rs34
-rw-r--r--compiler/rustc_typeck/src/check/method/confirm.rs26
-rw-r--r--compiler/rustc_typeck/src/check/method/mod.rs5
-rw-r--r--compiler/rustc_typeck/src/check/method/probe.rs15
-rw-r--r--compiler/rustc_typeck/src/check/wfcheck.rs86
-rw-r--r--compiler/rustc_typeck/src/outlives/explicit.rs8
-rw-r--r--compiler/rustc_typeck/src/outlives/implicit_infer.rs40
-rw-r--r--compiler/rustc_typeck/src/outlives/mod.rs6
-rw-r--r--compiler/rustc_typeck/src/outlives/utils.rs4
-rw-r--r--compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs2
-rw-r--r--library/std/src/os/windows/ffi.rs1
-rw-r--r--src/bootstrap/CHANGELOG.md1
-rw-r--r--src/bootstrap/check.rs18
-rw-r--r--src/bootstrap/compile.rs33
-rwxr-xr-xsrc/bootstrap/configure.py7
-rw-r--r--src/bootstrap/flags.rs17
-rw-r--r--src/bootstrap/native.rs18
-rw-r--r--src/bootstrap/util.rs24
-rwxr-xr-xsrc/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh2
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile1
-rw-r--r--src/ci/docker/scripts/musl.sh2
-rw-r--r--src/ci/github-actions/ci.yml2
-rwxr-xr-xsrc/ci/pgo.sh161
-rwxr-xr-xsrc/ci/run.sh2
-rwxr-xr-xsrc/ci/scripts/install-clang.sh3
-rw-r--r--src/librustdoc/config.rs22
-rw-r--r--src/librustdoc/doctest.rs73
-rw-r--r--src/test/run-make-fulldeps/target-specs/Makefile2
-rw-r--r--src/test/rustdoc-ui/c-help.rs6
-rw-r--r--src/test/rustdoc-ui/c-help.stdout51
-rw-r--r--src/test/rustdoc-ui/doctest-multiline-crate-attribute.rs10
-rw-r--r--src/test/rustdoc-ui/doctest-multiline-crate-attribute.stdout6
-rw-r--r--src/test/rustdoc-ui/z-help.rs6
-rw-r--r--src/test/rustdoc-ui/z-help.stdout195
-rw-r--r--src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr2
-rw-r--r--src/test/ui/asm/issue-99122-2.rs21
-rw-r--r--src/test/ui/asm/issue-99122.rs13
-rw-r--r--src/test/ui/asm/issue-99122.stderr11
-rw-r--r--src/test/ui/async-await/async-await.rs2
-rw-r--r--src/test/ui/const-generics/float-generic.adt_const_params.stderr11
-rw-r--r--src/test/ui/const-generics/float-generic.rs12
-rw-r--r--src/test/ui/const-generics/float-generic.simple.stderr11
-rw-r--r--src/test/ui/inference/erase-type-params-in-label.stderr8
-rw-r--r--src/test/ui/inference/issue-71732.stderr4
-rw-r--r--src/test/ui/issues/issue-35976.stderr5
-rw-r--r--src/test/ui/lifetimes/bare-trait-object-borrowck.rs24
-rw-r--r--src/test/ui/lifetimes/bare-trait-object.rs25
-rw-r--r--src/test/ui/methods/issues/issue-61525.rs20
-rw-r--r--src/test/ui/methods/issues/issue-61525.stderr39
-rw-r--r--src/test/ui/parser/macro/macro-doc-comments-1.stderr5
-rw-r--r--src/test/ui/parser/macro/macro-doc-comments-2.stderr5
-rw-r--r--src/test/ui/resolve/issue-55673.rs12
-rw-r--r--src/test/ui/resolve/issue-55673.stderr9
-rw-r--r--src/test/ui/rfc-2294-if-let-guard/feature-gate.rs7
-rw-r--r--src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr90
-rw-r--r--src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs168
-rw-r--r--src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr718
-rw-r--r--src/test/ui/rfc-2497-if-let-chains/feature-gate.rs5
-rw-r--r--src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr36
-rw-r--r--src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs34
-rw-r--r--src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr38
-rw-r--r--src/test/ui/suggestions/auxiliary/meow.rs11
-rw-r--r--src/test/ui/suggestions/issue-99080.rs16
-rw-r--r--src/test/ui/suggestions/issue-99080.stderr20
-rw-r--r--src/test/ui/traits/bound/assoc-fn-bound-root-obligation.stderr1
-rw-r--r--src/test/ui/traits/issue-59029-1.stderr4
-rw-r--r--src/test/ui/traits/issue-77982.stderr4
-rw-r--r--src/test/ui/traits/multidispatch-convert-ambig-dest.stderr4
-rw-r--r--src/test/ui/type-alias-impl-trait/not_well_formed.stderr2
-rw-r--r--src/test/ui/type/type-annotation-needed.stderr4
-rw-r--r--src/test/ui/typeck/do-not-suggest-adding-missing-zero-to-floating-point-number.rs21
-rw-r--r--src/test/ui/typeck/do-not-suggest-adding-missing-zero-to-floating-point-number.stderr51
-rw-r--r--src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.fixed11
-rw-r--r--src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.rs11
-rw-r--r--src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.stderr80
-rw-r--r--src/tools/compiletest/src/header.rs12
-rw-r--r--src/tools/compiletest/src/runtest.rs75
165 files changed, 2880 insertions, 1121 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a7be9978c0b..6d9e249ee44 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -410,7 +410,7 @@ jobs:
           - name: dist-x86_64-msvc
             env:
               RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-msvc --host=x86_64-pc-windows-msvc --target=x86_64-pc-windows-msvc --enable-full-tools --enable-profiler"
-              SCRIPT: python x.py dist
+              SCRIPT: PGO_HOST=x86_64-pc-windows-msvc src/ci/pgo.sh python x.py dist
               DIST_REQUIRE_ALL_TOOLS: 1
             os: windows-latest-xl
           - name: dist-i686-msvc
diff --git a/compiler/rustc/build.rs b/compiler/rustc/build.rs
index 24c06c0ddbf..39cf3e094c8 100644
--- a/compiler/rustc/build.rs
+++ b/compiler/rustc/build.rs
@@ -5,6 +5,9 @@ fn main() {
     let target_env = env::var("CARGO_CFG_TARGET_ENV");
     if Ok("windows") == target_os.as_deref() && Ok("msvc") == target_env.as_deref() {
         set_windows_exe_options();
+    } else {
+        // Avoid rerunning the build script every time.
+        println!("cargo:rerun-if-changed=build.rs");
     }
 }
 
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 2dcbd0782ef..fdf60e60914 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1159,6 +1159,33 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         param_mode: ParamMode,
         itctx: ImplTraitContext,
     ) -> hir::Ty<'hir> {
+        // Check whether we should interpret this as a bare trait object.
+        // This check mirrors the one in late resolution.  We only introduce this special case in
+        // the rare occurence we need to lower `Fresh` anonymous lifetimes.
+        // The other cases when a qpath should be opportunistically made a trait object are handled
+        // by `ty_path`.
+        if qself.is_none()
+            && let Some(partial_res) = self.resolver.get_partial_res(t.id)
+            && partial_res.unresolved_segments() == 0
+            && let Res::Def(DefKind::Trait | DefKind::TraitAlias, _) = partial_res.base_res()
+        {
+            let (bounds, lifetime_bound) = self.with_dyn_type_scope(true, |this| {
+                let bound = this.lower_poly_trait_ref(
+                    &PolyTraitRef {
+                        bound_generic_params: vec![],
+                        trait_ref: TraitRef { path: path.clone(), ref_id: t.id },
+                        span: t.span
+                    },
+                    itctx,
+                );
+                let bounds = this.arena.alloc_from_iter([bound]);
+                let lifetime_bound = this.elided_dyn_bound(t.span);
+                (bounds, lifetime_bound)
+            });
+            let kind = hir::TyKind::TraitObject(bounds, lifetime_bound, TraitObjectSyntax::None);
+            return hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.next_id() };
+        }
+
         let id = self.lower_node_id(t.id);
         let qpath = self.lower_qpath(t.id, qself, path, param_mode, itctx);
         self.ty_path(id, t.span, qpath)
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index b9cfc3732dc..687ff0fb505 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -309,7 +309,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 ));
 
                 // Check first whether the source is accessible (issue #87060)
-                if self.infcx.tcx.sess.source_map().span_to_snippet(deref_target).is_ok() {
+                if self.infcx.tcx.sess.source_map().is_span_accessible(deref_target) {
                     err.span_note(deref_target, "deref defined here");
                 }
             }
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index d296a1a0ac6..9c7671eee38 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -975,14 +975,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     if self.fn_self_span_reported.insert(fn_span) {
                         err.span_note(
                             // Check whether the source is accessible
-                            if self
-                                .infcx
-                                .tcx
-                                .sess
-                                .source_map()
-                                .span_to_snippet(self_arg.span)
-                                .is_ok()
-                            {
+                            if self.infcx.tcx.sess.source_map().is_span_accessible(self_arg.span) {
                                 self_arg.span
                             } else {
                                 fn_call_span
diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs
index 92f0a7498e3..2e356f67bf3 100644
--- a/compiler/rustc_const_eval/src/interpret/mod.rs
+++ b/compiler/rustc_const_eval/src/interpret/mod.rs
@@ -9,6 +9,7 @@ mod memory;
 mod operand;
 mod operator;
 mod place;
+mod projection;
 mod step;
 mod terminator;
 mod traits;
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index d11ae7b4925..1465b986293 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -1,7 +1,6 @@
 //! Functions concerning immediate values and operands, and reading from operands.
 //! All high-level functions to read from memory work on operands as sources.
 
-use std::convert::TryFrom;
 use std::fmt::Write;
 
 use rustc_hir::def::Namespace;
@@ -15,7 +14,7 @@ use rustc_target::abi::{VariantIdx, Variants};
 
 use super::{
     alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId,
-    InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer,
+    InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Place, PlaceTy, Pointer,
     PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit,
 };
 
@@ -254,6 +253,11 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> {
     }
 
     #[inline]
+    pub fn uninit(layout: TyAndLayout<'tcx>) -> Self {
+        ImmTy { imm: Immediate::Uninit, layout }
+    }
+
+    #[inline]
     pub fn try_from_uint(i: impl Into<u128>, layout: TyAndLayout<'tcx>) -> Option<Self> {
         Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout))
     }
@@ -280,6 +284,41 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> {
     }
 }
 
+impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> {
+    pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
+        if self.layout.is_unsized() {
+            // There are no unsized immediates.
+            self.assert_mem_place().len(cx)
+        } else {
+            match self.layout.fields {
+                abi::FieldsShape::Array { count, .. } => Ok(count),
+                _ => bug!("len not supported on sized type {:?}", self.layout.ty),
+            }
+        }
+    }
+
+    pub fn offset(
+        &self,
+        offset: Size,
+        meta: MemPlaceMeta<Tag>,
+        layout: TyAndLayout<'tcx>,
+        cx: &impl HasDataLayout,
+    ) -> InterpResult<'tcx, Self> {
+        match self.try_as_mplace() {
+            Ok(mplace) => Ok(mplace.offset(offset, meta, layout, cx)?.into()),
+            Err(imm) => {
+                assert!(
+                    matches!(*imm, Immediate::Uninit),
+                    "Scalar/ScalarPair cannot be offset into"
+                );
+                assert!(!meta.has_meta()); // no place to store metadata here
+                // Every part of an uninit is uninit.
+                Ok(ImmTy::uninit(layout).into())
+            }
+        }
+    }
+}
+
 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
     /// Returns `None` if the layout does not permit loading this as a value.
@@ -296,11 +335,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         }
 
         let Some(alloc) = self.get_place_alloc(mplace)? else {
-            return Ok(Some(ImmTy {
-                // zero-sized type can be left uninit
-                imm: Immediate::Uninit,
-                layout: mplace.layout,
-            }));
+            // zero-sized type can be left uninit
+            return Ok(Some(ImmTy::uninit(mplace.layout)));
         };
 
         // It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point.
@@ -367,6 +403,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// This flag exists only for validity checking.
     ///
     /// This is an internal function that should not usually be used; call `read_immediate` instead.
+    /// ConstProp needs it, though.
     pub fn read_immediate_raw(
         &self,
         src: &OpTy<'tcx, M::PointerTag>,
@@ -421,123 +458,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         Ok(str)
     }
 
-    /// Projection functions
-    pub fn operand_field(
-        &self,
-        op: &OpTy<'tcx, M::PointerTag>,
-        field: usize,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
-        let base = match op.try_as_mplace() {
-            Ok(ref mplace) => {
-                // We can reuse the mplace field computation logic for indirect operands.
-                let field = self.mplace_field(mplace, field)?;
-                return Ok(field.into());
-            }
-            Err(value) => value,
-        };
-
-        let field_layout = base.layout.field(self, field);
-        let offset = base.layout.fields.offset(field);
-        // This makes several assumptions about what layouts we will encounter; we match what
-        // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
-        let field_val: Immediate<_> = match (*base, base.layout.abi) {
-            // the field contains no information, can be left uninit
-            _ if field_layout.is_zst() => Immediate::Uninit,
-            // the field covers the entire type
-            _ if field_layout.size == base.layout.size => {
-                assert!(match (base.layout.abi, field_layout.abi) {
-                    (Abi::Scalar(..), Abi::Scalar(..)) => true,
-                    (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true,
-                    _ => false,
-                });
-                assert!(offset.bytes() == 0);
-                *base
-            }
-            // extract fields from types with `ScalarPair` ABI
-            (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
-                assert!(matches!(field_layout.abi, Abi::Scalar(..)));
-                Immediate::from(if offset.bytes() == 0 {
-                    debug_assert_eq!(field_layout.size, a.size(self));
-                    a_val
-                } else {
-                    debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi));
-                    debug_assert_eq!(field_layout.size, b.size(self));
-                    b_val
-                })
-            }
-            _ => span_bug!(
-                self.cur_span(),
-                "invalid field access on immediate {}, layout {:#?}",
-                base,
-                base.layout
-            ),
-        };
-
-        Ok(OpTy { op: Operand::Immediate(field_val), layout: field_layout, align: None })
-    }
-
-    pub fn operand_index(
-        &self,
-        op: &OpTy<'tcx, M::PointerTag>,
-        index: u64,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
-        if let Ok(index) = usize::try_from(index) {
-            // We can just treat this as a field.
-            self.operand_field(op, index)
-        } else {
-            // Indexing into a big array. This must be an mplace.
-            let mplace = op.assert_mem_place();
-            Ok(self.mplace_index(&mplace, index)?.into())
-        }
-    }
-
-    pub fn operand_downcast(
-        &self,
-        op: &OpTy<'tcx, M::PointerTag>,
-        variant: VariantIdx,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
-        Ok(match op.try_as_mplace() {
-            Ok(ref mplace) => self.mplace_downcast(mplace, variant)?.into(),
-            Err(..) => {
-                // Downcasts only change the layout.
-                // (In particular, no check about whether this is even the active variant -- that's by design,
-                // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.)
-                let layout = op.layout.for_variant(self, variant);
-                OpTy { layout, ..*op }
-            }
-        })
-    }
-
-    #[instrument(skip(self), level = "debug")]
-    pub fn operand_projection(
-        &self,
-        base: &OpTy<'tcx, M::PointerTag>,
-        proj_elem: mir::PlaceElem<'tcx>,
-    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
-        use rustc_middle::mir::ProjectionElem::*;
-        Ok(match proj_elem {
-            Field(field, _) => self.operand_field(base, field.index())?,
-            Downcast(_, variant) => self.operand_downcast(base, variant)?,
-            Deref => self.deref_operand(base)?.into(),
-            Subslice { .. } | ConstantIndex { .. } | Index(_) => {
-                // The rest should only occur as mplace, we do not use Immediates for types
-                // allowing such operations.  This matches place_projection forcing an allocation.
-                let mplace = base.assert_mem_place();
-                self.mplace_projection(&mplace, proj_elem)?.into()
-            }
-        })
-    }
-
     /// Converts a repr(simd) operand into an operand where `place_index` accesses the SIMD elements.
     /// Also returns the number of elements.
+    ///
+    /// Can (but does not always) trigger UB if `op` is uninitialized.
     pub fn operand_to_simd(
         &self,
-        base: &OpTy<'tcx, M::PointerTag>,
+        op: &OpTy<'tcx, M::PointerTag>,
     ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> {
         // Basically we just transmute this place into an array following simd_size_and_type.
         // This only works in memory, but repr(simd) types should never be immediates anyway.
-        assert!(base.layout.ty.is_simd());
-        self.mplace_to_simd(&base.assert_mem_place())
+        assert!(op.layout.ty.is_simd());
+        match op.try_as_mplace() {
+            Ok(mplace) => self.mplace_to_simd(&mplace),
+            Err(imm) => match *imm {
+                Immediate::Uninit => {
+                    throw_ub!(InvalidUninitBytes(None))
+                }
+                Immediate::Scalar(..) | Immediate::ScalarPair(..) => {
+                    bug!("arrays/slices can never have Scalar/ScalarPair layout")
+                }
+            },
+        }
     }
 
     /// Read from a local. Will not actually access the local if reading from a ZST.
@@ -582,30 +524,34 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// avoid allocations.
     pub fn eval_place_to_op(
         &self,
-        place: mir::Place<'tcx>,
+        mir_place: mir::Place<'tcx>,
         layout: Option<TyAndLayout<'tcx>>,
     ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
         // Do not use the layout passed in as argument if the base we are looking at
         // here is not the entire place.
-        let layout = if place.projection.is_empty() { layout } else { None };
+        let layout = if mir_place.projection.is_empty() { layout } else { None };
 
-        let base_op = self.local_to_op(self.frame(), place.local, layout)?;
-
-        let op = place
-            .projection
-            .iter()
-            .try_fold(base_op, |op, elem| self.operand_projection(&op, elem))?;
+        let mut op = self.local_to_op(self.frame(), mir_place.local, layout)?;
+        // Using `try_fold` turned out to be bad for performance, hence the loop.
+        for elem in mir_place.projection.iter() {
+            op = self.operand_projection(&op, elem)?
+        }
 
         trace!("eval_place_to_op: got {:?}", *op);
         // Sanity-check the type we ended up with.
-        debug_assert!(mir_assign_valid_types(
-            *self.tcx,
-            self.param_env,
-            self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
-                place.ty(&self.frame().body.local_decls, *self.tcx).ty
-            )?)?,
-            op.layout,
-        ));
+        debug_assert!(
+            mir_assign_valid_types(
+                *self.tcx,
+                self.param_env,
+                self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
+                    mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty
+                )?)?,
+                op.layout,
+            ),
+            "eval_place of a MIR place with type {:?} produced an interpreter operand with type {:?}",
+            mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty,
+            op.layout.ty,
+        );
         Ok(op)
     }
 
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 57ecad07b42..2001359d199 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -2,17 +2,14 @@
 //! into a place.
 //! All high-level functions to write to memory work on places as destinations.
 
-use std::convert::TryFrom;
 use std::hash::Hash;
 
 use rustc_ast::Mutability;
 use rustc_macros::HashStable;
 use rustc_middle::mir;
+use rustc_middle::ty;
 use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
-use rustc_middle::ty::{self, Ty};
-use rustc_target::abi::{
-    Abi, Align, FieldsShape, HasDataLayout, Size, TagEncoding, VariantIdx, Variants,
-};
+use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, TagEncoding, VariantIdx};
 
 use super::{
     alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg,
@@ -46,7 +43,7 @@ impl<Tag: Provenance> MemPlaceMeta<Tag> {
             }
         }
     }
-    fn has_meta(self) -> bool {
+    pub fn has_meta(self) -> bool {
         match self {
             Self::Meta(_) => true,
             Self::None | Self::Poison => false,
@@ -188,6 +185,7 @@ impl<Tag: Provenance> Place<Tag> {
     /// Asserts that this points to some local variable.
     /// Returns the frame idx and the variable idx.
     #[inline]
+    #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
     pub fn assert_local(&self) -> (usize, mir::Local) {
         match self {
             Place::Local { frame, local } => (*frame, *local),
@@ -250,7 +248,7 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> {
             // Go through the layout.  There are lots of types that support a length,
             // e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!)
             match self.layout.fields {
-                FieldsShape::Array { count, .. } => Ok(count),
+                abi::FieldsShape::Array { count, .. } => Ok(count),
                 _ => bug!("len not supported on sized type {:?}", self.layout.ty),
             }
         }
@@ -281,6 +279,7 @@ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> {
     }
 
     #[inline(always)]
+    #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
     /// Note: do not call `as_ref` on the resulting place. This function should only be used to
     /// read from the resulting mplace, not to get its address back.
     pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Tag> {
@@ -298,16 +297,16 @@ impl<'tcx, Tag: Provenance> PlaceTy<'tcx, Tag> {
         }
     }
 
-    #[inline]
-    pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Tag> {
+    #[inline(always)]
+    #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
+    pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> {
         self.try_as_mplace().unwrap()
     }
 }
 
-// separating the pointer tag for `impl Trait`, see https://github.com/rust-lang/rust/issues/54385
+// FIXME: Working around https://github.com/rust-lang/rust/issues/54385
 impl<'mir, 'tcx: 'mir, Tag, M> InterpCx<'mir, 'tcx, M>
 where
-    // FIXME: Working around https://github.com/rust-lang/rust/issues/54385
     Tag: Provenance + Eq + Hash + 'static,
     M: Machine<'mir, 'tcx, PointerTag = Tag>,
 {
@@ -392,276 +391,29 @@ where
         Ok(())
     }
 
-    /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is
-    /// always possible without allocating, so it can take `&self`. Also return the field's layout.
-    /// This supports both struct and array fields.
-    ///
-    /// This also works for arrays, but then the `usize` index type is restricting.
-    /// For indexing into arrays, use `mplace_index`.
-    #[inline(always)]
-    pub fn mplace_field(
-        &self,
-        base: &MPlaceTy<'tcx, M::PointerTag>,
-        field: usize,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
-        let offset = base.layout.fields.offset(field);
-        let field_layout = base.layout.field(self, field);
-
-        // Offset may need adjustment for unsized fields.
-        let (meta, offset) = if field_layout.is_unsized() {
-            // Re-use parent metadata to determine dynamic field layout.
-            // With custom DSTS, this *will* execute user-defined code, but the same
-            // happens at run-time so that's okay.
-            match self.size_and_align_of(&base.meta, &field_layout)? {
-                Some((_, align)) => (base.meta, offset.align_to(align)),
-                None => {
-                    // For unsized types with an extern type tail we perform no adjustments.
-                    // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend.
-                    assert!(matches!(base.meta, MemPlaceMeta::None));
-                    (base.meta, offset)
-                }
-            }
-        } else {
-            // base.meta could be present; we might be accessing a sized field of an unsized
-            // struct.
-            (MemPlaceMeta::None, offset)
-        };
-
-        // We do not look at `base.layout.align` nor `field_layout.align`, unlike
-        // codegen -- mostly to see if we can get away with that
-        base.offset(offset, meta, field_layout, self)
-    }
-
-    /// Index into an array.
-    #[inline(always)]
-    pub fn mplace_index(
-        &self,
-        base: &MPlaceTy<'tcx, M::PointerTag>,
-        index: u64,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
-        // Not using the layout method because we want to compute on u64
-        match base.layout.fields {
-            FieldsShape::Array { stride, .. } => {
-                let len = base.len(self)?;
-                if index >= len {
-                    // This can only be reached in ConstProp and non-rustc-MIR.
-                    throw_ub!(BoundsCheckFailed { len, index });
-                }
-                let offset = stride * index; // `Size` multiplication
-                // All fields have the same layout.
-                let field_layout = base.layout.field(self, 0);
-
-                assert!(!field_layout.is_unsized());
-                base.offset(offset, MemPlaceMeta::None, field_layout, self)
-            }
-            _ => span_bug!(
-                self.cur_span(),
-                "`mplace_index` called on non-array type {:?}",
-                base.layout.ty
-            ),
-        }
-    }
-
-    // Iterates over all fields of an array. Much more efficient than doing the
-    // same by repeatedly calling `mplace_array`.
-    pub(super) fn mplace_array_fields<'a>(
-        &self,
-        base: &'a MPlaceTy<'tcx, Tag>,
-    ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, MPlaceTy<'tcx, Tag>>> + 'a>
-    {
-        let len = base.len(self)?; // also asserts that we have a type where this makes sense
-        let FieldsShape::Array { stride, .. } = base.layout.fields else {
-            span_bug!(self.cur_span(), "mplace_array_fields: expected an array layout");
-        };
-        let layout = base.layout.field(self, 0);
-        let dl = &self.tcx.data_layout;
-        // `Size` multiplication
-        Ok((0..len).map(move |i| base.offset(stride * i, MemPlaceMeta::None, layout, dl)))
-    }
-
-    fn mplace_subslice(
-        &self,
-        base: &MPlaceTy<'tcx, M::PointerTag>,
-        from: u64,
-        to: u64,
-        from_end: bool,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
-        let len = base.len(self)?; // also asserts that we have a type where this makes sense
-        let actual_to = if from_end {
-            if from.checked_add(to).map_or(true, |to| to > len) {
-                // This can only be reached in ConstProp and non-rustc-MIR.
-                throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) });
-            }
-            len.checked_sub(to).unwrap()
-        } else {
-            to
-        };
-
-        // Not using layout method because that works with usize, and does not work with slices
-        // (that have count 0 in their layout).
-        let from_offset = match base.layout.fields {
-            FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked
-            _ => {
-                span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout)
-            }
-        };
-
-        // Compute meta and new layout
-        let inner_len = actual_to.checked_sub(from).unwrap();
-        let (meta, ty) = match base.layout.ty.kind() {
-            // It is not nice to match on the type, but that seems to be the only way to
-            // implement this.
-            ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(*inner, inner_len)),
-            ty::Slice(..) => {
-                let len = Scalar::from_machine_usize(inner_len, self);
-                (MemPlaceMeta::Meta(len), base.layout.ty)
-            }
-            _ => {
-                span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty)
-            }
-        };
-        let layout = self.layout_of(ty)?;
-        base.offset(from_offset, meta, layout, self)
-    }
-
-    pub(crate) fn mplace_downcast(
-        &self,
-        base: &MPlaceTy<'tcx, M::PointerTag>,
-        variant: VariantIdx,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
-        // Downcasts only change the layout.
-        // (In particular, no check about whether this is even the active variant -- that's by design,
-        // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.)
-        assert!(!base.meta.has_meta());
-        Ok(MPlaceTy { layout: base.layout.for_variant(self, variant), ..*base })
-    }
-
-    /// Project into an mplace
-    #[instrument(skip(self), level = "debug")]
-    pub(super) fn mplace_projection(
-        &self,
-        base: &MPlaceTy<'tcx, M::PointerTag>,
-        proj_elem: mir::PlaceElem<'tcx>,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
-        use rustc_middle::mir::ProjectionElem::*;
-        Ok(match proj_elem {
-            Field(field, _) => self.mplace_field(base, field.index())?,
-            Downcast(_, variant) => self.mplace_downcast(base, variant)?,
-            Deref => self.deref_operand(&base.into())?,
-
-            Index(local) => {
-                let layout = self.layout_of(self.tcx.types.usize)?;
-                let n = self.local_to_op(self.frame(), local, Some(layout))?;
-                let n = self.read_scalar(&n)?;
-                let n = n.to_machine_usize(self)?;
-                self.mplace_index(base, n)?
-            }
-
-            ConstantIndex { offset, min_length, from_end } => {
-                let n = base.len(self)?;
-                if n < min_length {
-                    // This can only be reached in ConstProp and non-rustc-MIR.
-                    throw_ub!(BoundsCheckFailed { len: min_length, index: n });
-                }
-
-                let index = if from_end {
-                    assert!(0 < offset && offset <= min_length);
-                    n.checked_sub(offset).unwrap()
-                } else {
-                    assert!(offset < min_length);
-                    offset
-                };
-
-                self.mplace_index(base, index)?
-            }
-
-            Subslice { from, to, from_end } => self.mplace_subslice(base, from, to, from_end)?,
-        })
-    }
-
     /// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
     /// Also returns the number of elements.
     pub fn mplace_to_simd(
         &self,
-        base: &MPlaceTy<'tcx, M::PointerTag>,
+        mplace: &MPlaceTy<'tcx, M::PointerTag>,
     ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> {
         // Basically we just transmute this place into an array following simd_size_and_type.
         // (Transmuting is okay since this is an in-memory place. We also double-check the size
         // stays the same.)
-        let (len, e_ty) = base.layout.ty.simd_size_and_type(*self.tcx);
+        let (len, e_ty) = mplace.layout.ty.simd_size_and_type(*self.tcx);
         let array = self.tcx.mk_array(e_ty, len);
         let layout = self.layout_of(array)?;
-        assert_eq!(layout.size, base.layout.size);
-        Ok((MPlaceTy { layout, ..*base }, len))
-    }
-
-    /// Gets the place of a field inside the place, and also the field's type.
-    /// Just a convenience function, but used quite a bit.
-    /// This is the only projection that might have a side-effect: We cannot project
-    /// into the field of a local `ScalarPair`, we have to first allocate it.
-    #[instrument(skip(self), level = "debug")]
-    pub fn place_field(
-        &mut self,
-        base: &PlaceTy<'tcx, M::PointerTag>,
-        field: usize,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
-        // FIXME: We could try to be smarter and avoid allocation for fields that span the
-        // entire place.
-        let mplace = self.force_allocation(base)?;
-        Ok(self.mplace_field(&mplace, field)?.into())
-    }
-
-    pub fn place_index(
-        &mut self,
-        base: &PlaceTy<'tcx, M::PointerTag>,
-        index: u64,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
-        let mplace = self.force_allocation(base)?;
-        Ok(self.mplace_index(&mplace, index)?.into())
-    }
-
-    pub fn place_downcast(
-        &self,
-        base: &PlaceTy<'tcx, M::PointerTag>,
-        variant: VariantIdx,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
-        // Downcast just changes the layout
-        Ok(match base.try_as_mplace() {
-            Ok(mplace) => self.mplace_downcast(&mplace, variant)?.into(),
-            Err(..) => {
-                let layout = base.layout.for_variant(self, variant);
-                PlaceTy { layout, ..*base }
-            }
-        })
-    }
-
-    /// Projects into a place.
-    pub fn place_projection(
-        &mut self,
-        base: &PlaceTy<'tcx, M::PointerTag>,
-        &proj_elem: &mir::ProjectionElem<mir::Local, Ty<'tcx>>,
-    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
-        use rustc_middle::mir::ProjectionElem::*;
-        Ok(match proj_elem {
-            Field(field, _) => self.place_field(base, field.index())?,
-            Downcast(_, variant) => self.place_downcast(base, variant)?,
-            Deref => self.deref_operand(&self.place_to_op(base)?)?.into(),
-            // For the other variants, we have to force an allocation.
-            // This matches `operand_projection`.
-            Subslice { .. } | ConstantIndex { .. } | Index(_) => {
-                let mplace = self.force_allocation(base)?;
-                self.mplace_projection(&mplace, proj_elem)?.into()
-            }
-        })
+        assert_eq!(layout.size, mplace.layout.size);
+        Ok((MPlaceTy { layout, ..*mplace }, len))
     }
 
     /// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
     /// Also returns the number of elements.
     pub fn place_to_simd(
         &mut self,
-        base: &PlaceTy<'tcx, M::PointerTag>,
+        place: &PlaceTy<'tcx, M::PointerTag>,
     ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> {
-        let mplace = self.force_allocation(base)?;
+        let mplace = self.force_allocation(place)?;
         self.mplace_to_simd(&mplace)
     }
 
@@ -680,30 +432,30 @@ where
     #[instrument(skip(self), level = "debug")]
     pub fn eval_place(
         &mut self,
-        place: mir::Place<'tcx>,
+        mir_place: mir::Place<'tcx>,
     ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
-        let mut place_ty = self.local_to_place(self.frame_idx(), place.local)?;
-
-        for elem in place.projection.iter() {
-            place_ty = self.place_projection(&place_ty, &elem)?
+        let mut place = self.local_to_place(self.frame_idx(), mir_place.local)?;
+        // Using `try_fold` turned out to be bad for performance, hence the loop.
+        for elem in mir_place.projection.iter() {
+            place = self.place_projection(&place, elem)?
         }
 
-        trace!("{:?}", self.dump_place(place_ty.place));
+        trace!("{:?}", self.dump_place(place.place));
         // Sanity-check the type we ended up with.
         debug_assert!(
             mir_assign_valid_types(
                 *self.tcx,
                 self.param_env,
                 self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
-                    place.ty(&self.frame().body.local_decls, *self.tcx).ty
+                    mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty
                 )?)?,
-                place_ty.layout,
+                place.layout,
             ),
-            "eval_place of a MIR place with type {:?} produced an interpret place with type {:?}",
-            place.ty(&self.frame().body.local_decls, *self.tcx).ty,
-            place_ty.layout.ty,
+            "eval_place of a MIR place with type {:?} produced an interpreter place with type {:?}",
+            mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty,
+            place.layout.ty,
         );
-        Ok(place_ty)
+        Ok(place)
     }
 
     /// Write an immediate to a place
@@ -1058,10 +810,10 @@ where
         }
 
         match dest.layout.variants {
-            Variants::Single { index } => {
+            abi::Variants::Single { index } => {
                 assert_eq!(index, variant_index);
             }
-            Variants::Multiple {
+            abi::Variants::Multiple {
                 tag_encoding: TagEncoding::Direct,
                 tag: tag_layout,
                 tag_field,
@@ -1082,7 +834,7 @@ where
                 let tag_dest = self.place_field(dest, tag_field)?;
                 self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?;
             }
-            Variants::Multiple {
+            abi::Variants::Multiple {
                 tag_encoding:
                     TagEncoding::Niche { dataful_variant, ref niche_variants, niche_start },
                 tag: tag_layout,
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
new file mode 100644
index 00000000000..31fb6a8944d
--- /dev/null
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -0,0 +1,393 @@
+//! This file implements "place projections"; basically a symmetric API for 3 types: MPlaceTy, OpTy, PlaceTy.
+//!
+//! OpTy and PlaceTy genrally work by "let's see if we are actually an MPlaceTy, and do something custom if not".
+//! For PlaceTy, the custom thing is basically always to call `force_allocation` and then use the MPlaceTy logic anyway.
+//! For OpTy, the custom thing on field pojections has to be pretty clever (since `Operand::Immediate` can have fields),
+//! but for array/slice operations it only has to worry about `Operand::Uninit`. That makes the value part trivial,
+//! but we still need to do bounds checking and adjust the layout. To not duplicate that with MPlaceTy, we actually
+//! implement the logic on OpTy, and MPlaceTy calls that.
+
+use std::hash::Hash;
+
+use rustc_middle::mir;
+use rustc_middle::ty;
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_target::abi::{self, Abi, VariantIdx};
+
+use super::{
+    ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, PlaceTy,
+    Provenance, Scalar,
+};
+
+// FIXME: Working around https://github.com/rust-lang/rust/issues/54385
+impl<'mir, 'tcx: 'mir, Tag, M> InterpCx<'mir, 'tcx, M>
+where
+    Tag: Provenance + Eq + Hash + 'static,
+    M: Machine<'mir, 'tcx, PointerTag = Tag>,
+{
+    //# Field access
+
+    /// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is
+    /// always possible without allocating, so it can take `&self`. Also return the field's layout.
+    /// This supports both struct and array fields.
+    ///
+    /// This also works for arrays, but then the `usize` index type is restricting.
+    /// For indexing into arrays, use `mplace_index`.
+    pub fn mplace_field(
+        &self,
+        base: &MPlaceTy<'tcx, M::PointerTag>,
+        field: usize,
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+        let offset = base.layout.fields.offset(field);
+        let field_layout = base.layout.field(self, field);
+
+        // Offset may need adjustment for unsized fields.
+        let (meta, offset) = if field_layout.is_unsized() {
+            // Re-use parent metadata to determine dynamic field layout.
+            // With custom DSTS, this *will* execute user-defined code, but the same
+            // happens at run-time so that's okay.
+            match self.size_and_align_of(&base.meta, &field_layout)? {
+                Some((_, align)) => (base.meta, offset.align_to(align)),
+                None => {
+                    // For unsized types with an extern type tail we perform no adjustments.
+                    // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend.
+                    assert!(matches!(base.meta, MemPlaceMeta::None));
+                    (base.meta, offset)
+                }
+            }
+        } else {
+            // base.meta could be present; we might be accessing a sized field of an unsized
+            // struct.
+            (MemPlaceMeta::None, offset)
+        };
+
+        // We do not look at `base.layout.align` nor `field_layout.align`, unlike
+        // codegen -- mostly to see if we can get away with that
+        base.offset(offset, meta, field_layout, self)
+    }
+
+    /// Gets the place of a field inside the place, and also the field's type.
+    /// Just a convenience function, but used quite a bit.
+    /// This is the only projection that might have a side-effect: We cannot project
+    /// into the field of a local `ScalarPair`, we have to first allocate it.
+    pub fn place_field(
+        &mut self,
+        base: &PlaceTy<'tcx, M::PointerTag>,
+        field: usize,
+    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
+        // FIXME: We could try to be smarter and avoid allocation for fields that span the
+        // entire place.
+        let base = self.force_allocation(base)?;
+        Ok(self.mplace_field(&base, field)?.into())
+    }
+
+    pub fn operand_field(
+        &self,
+        base: &OpTy<'tcx, M::PointerTag>,
+        field: usize,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+        let base = match base.try_as_mplace() {
+            Ok(ref mplace) => {
+                // We can reuse the mplace field computation logic for indirect operands.
+                let field = self.mplace_field(mplace, field)?;
+                return Ok(field.into());
+            }
+            Err(value) => value,
+        };
+
+        let field_layout = base.layout.field(self, field);
+        let offset = base.layout.fields.offset(field);
+        // This makes several assumptions about what layouts we will encounter; we match what
+        // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
+        let field_val: Immediate<_> = match (*base, base.layout.abi) {
+            // the field contains no information, can be left uninit
+            _ if field_layout.is_zst() => Immediate::Uninit,
+            // the field covers the entire type
+            _ if field_layout.size == base.layout.size => {
+                assert!(match (base.layout.abi, field_layout.abi) {
+                    (Abi::Scalar(..), Abi::Scalar(..)) => true,
+                    (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true,
+                    _ => false,
+                });
+                assert!(offset.bytes() == 0);
+                *base
+            }
+            // extract fields from types with `ScalarPair` ABI
+            (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
+                assert!(matches!(field_layout.abi, Abi::Scalar(..)));
+                Immediate::from(if offset.bytes() == 0 {
+                    debug_assert_eq!(field_layout.size, a.size(self));
+                    a_val
+                } else {
+                    debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi));
+                    debug_assert_eq!(field_layout.size, b.size(self));
+                    b_val
+                })
+            }
+            _ => span_bug!(
+                self.cur_span(),
+                "invalid field access on immediate {}, layout {:#?}",
+                base,
+                base.layout
+            ),
+        };
+
+        Ok(ImmTy::from_immediate(field_val, field_layout).into())
+    }
+
+    //# Downcasting
+
+    pub fn mplace_downcast(
+        &self,
+        base: &MPlaceTy<'tcx, M::PointerTag>,
+        variant: VariantIdx,
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+        // Downcasts only change the layout.
+        // (In particular, no check about whether this is even the active variant -- that's by design,
+        // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.)
+        assert!(!base.meta.has_meta());
+        let mut base = *base;
+        base.layout = base.layout.for_variant(self, variant);
+        Ok(base)
+    }
+
+    pub fn place_downcast(
+        &self,
+        base: &PlaceTy<'tcx, M::PointerTag>,
+        variant: VariantIdx,
+    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
+        // Downcast just changes the layout
+        let mut base = *base;
+        base.layout = base.layout.for_variant(self, variant);
+        Ok(base)
+    }
+
+    pub fn operand_downcast(
+        &self,
+        base: &OpTy<'tcx, M::PointerTag>,
+        variant: VariantIdx,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+        // Downcast just changes the layout
+        let mut base = *base;
+        base.layout = base.layout.for_variant(self, variant);
+        Ok(base)
+    }
+
+    //# Slice indexing
+
+    #[inline(always)]
+    pub fn operand_index(
+        &self,
+        base: &OpTy<'tcx, M::PointerTag>,
+        index: u64,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+        // Not using the layout method because we want to compute on u64
+        match base.layout.fields {
+            abi::FieldsShape::Array { stride, count: _ } => {
+                // `count` is nonsense for slices, use the dynamic length instead.
+                let len = base.len(self)?;
+                if index >= len {
+                    // This can only be reached in ConstProp and non-rustc-MIR.
+                    throw_ub!(BoundsCheckFailed { len, index });
+                }
+                let offset = stride * index; // `Size` multiplication
+                // All fields have the same layout.
+                let field_layout = base.layout.field(self, 0);
+                assert!(!field_layout.is_unsized());
+
+                base.offset(offset, MemPlaceMeta::None, field_layout, self)
+            }
+            _ => span_bug!(
+                self.cur_span(),
+                "`mplace_index` called on non-array type {:?}",
+                base.layout.ty
+            ),
+        }
+    }
+
+    // Iterates over all fields of an array. Much more efficient than doing the
+    // same by repeatedly calling `operand_index`.
+    pub fn operand_array_fields<'a>(
+        &self,
+        base: &'a OpTy<'tcx, Tag>,
+    ) -> InterpResult<'tcx, impl Iterator<Item = InterpResult<'tcx, OpTy<'tcx, Tag>>> + 'a> {
+        let len = base.len(self)?; // also asserts that we have a type where this makes sense
+        let abi::FieldsShape::Array { stride, .. } = base.layout.fields else {
+            span_bug!(self.cur_span(), "operand_array_fields: expected an array layout");
+        };
+        let layout = base.layout.field(self, 0);
+        let dl = &self.tcx.data_layout;
+        // `Size` multiplication
+        Ok((0..len).map(move |i| base.offset(stride * i, MemPlaceMeta::None, layout, dl)))
+    }
+
+    /// Index into an array.
+    pub fn mplace_index(
+        &self,
+        base: &MPlaceTy<'tcx, M::PointerTag>,
+        index: u64,
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+        Ok(self.operand_index(&base.into(), index)?.assert_mem_place())
+    }
+
+    pub fn place_index(
+        &mut self,
+        base: &PlaceTy<'tcx, M::PointerTag>,
+        index: u64,
+    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
+        // There's not a lot we can do here, since we cannot have a place to a part of a local. If
+        // we are accessing the only element of a 1-element array, it's still the entire local...
+        // that doesn't seem worth it.
+        let base = self.force_allocation(base)?;
+        Ok(self.mplace_index(&base, index)?.into())
+    }
+
+    //# ConstantIndex support
+
+    fn operand_constant_index(
+        &self,
+        base: &OpTy<'tcx, M::PointerTag>,
+        offset: u64,
+        min_length: u64,
+        from_end: bool,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+        let n = base.len(self)?;
+        if n < min_length {
+            // This can only be reached in ConstProp and non-rustc-MIR.
+            throw_ub!(BoundsCheckFailed { len: min_length, index: n });
+        }
+
+        let index = if from_end {
+            assert!(0 < offset && offset <= min_length);
+            n.checked_sub(offset).unwrap()
+        } else {
+            assert!(offset < min_length);
+            offset
+        };
+
+        self.operand_index(base, index)
+    }
+
+    fn place_constant_index(
+        &mut self,
+        base: &PlaceTy<'tcx, M::PointerTag>,
+        offset: u64,
+        min_length: u64,
+        from_end: bool,
+    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
+        let base = self.force_allocation(base)?;
+        Ok(self
+            .operand_constant_index(&base.into(), offset, min_length, from_end)?
+            .assert_mem_place()
+            .into())
+    }
+
+    //# Subslicing
+
+    fn operand_subslice(
+        &self,
+        base: &OpTy<'tcx, M::PointerTag>,
+        from: u64,
+        to: u64,
+        from_end: bool,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+        let len = base.len(self)?; // also asserts that we have a type where this makes sense
+        let actual_to = if from_end {
+            if from.checked_add(to).map_or(true, |to| to > len) {
+                // This can only be reached in ConstProp and non-rustc-MIR.
+                throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) });
+            }
+            len.checked_sub(to).unwrap()
+        } else {
+            to
+        };
+
+        // Not using layout method because that works with usize, and does not work with slices
+        // (that have count 0 in their layout).
+        let from_offset = match base.layout.fields {
+            abi::FieldsShape::Array { stride, .. } => stride * from, // `Size` multiplication is checked
+            _ => {
+                span_bug!(self.cur_span(), "unexpected layout of index access: {:#?}", base.layout)
+            }
+        };
+
+        // Compute meta and new layout
+        let inner_len = actual_to.checked_sub(from).unwrap();
+        let (meta, ty) = match base.layout.ty.kind() {
+            // It is not nice to match on the type, but that seems to be the only way to
+            // implement this.
+            ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(*inner, inner_len)),
+            ty::Slice(..) => {
+                let len = Scalar::from_machine_usize(inner_len, self);
+                (MemPlaceMeta::Meta(len), base.layout.ty)
+            }
+            _ => {
+                span_bug!(self.cur_span(), "cannot subslice non-array type: `{:?}`", base.layout.ty)
+            }
+        };
+        let layout = self.layout_of(ty)?;
+        base.offset(from_offset, meta, layout, self)
+    }
+
+    pub fn place_subslice(
+        &mut self,
+        base: &PlaceTy<'tcx, M::PointerTag>,
+        from: u64,
+        to: u64,
+        from_end: bool,
+    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
+        let base = self.force_allocation(base)?;
+        Ok(self.operand_subslice(&base.into(), from, to, from_end)?.assert_mem_place().into())
+    }
+
+    //# Applying a general projection
+
+    /// Projects into a place.
+    #[instrument(skip(self), level = "trace")]
+    pub fn place_projection(
+        &mut self,
+        base: &PlaceTy<'tcx, M::PointerTag>,
+        proj_elem: mir::PlaceElem<'tcx>,
+    ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
+        use rustc_middle::mir::ProjectionElem::*;
+        Ok(match proj_elem {
+            Field(field, _) => self.place_field(base, field.index())?,
+            Downcast(_, variant) => self.place_downcast(base, variant)?,
+            Deref => self.deref_operand(&self.place_to_op(base)?)?.into(),
+            Index(local) => {
+                let layout = self.layout_of(self.tcx.types.usize)?;
+                let n = self.local_to_op(self.frame(), local, Some(layout))?;
+                let n = self.read_scalar(&n)?.to_machine_usize(self)?;
+                self.place_index(base, n)?
+            }
+            ConstantIndex { offset, min_length, from_end } => {
+                self.place_constant_index(base, offset, min_length, from_end)?
+            }
+            Subslice { from, to, from_end } => self.place_subslice(base, from, to, from_end)?,
+        })
+    }
+
+    #[instrument(skip(self), level = "trace")]
+    pub fn operand_projection(
+        &self,
+        base: &OpTy<'tcx, M::PointerTag>,
+        proj_elem: mir::PlaceElem<'tcx>,
+    ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
+        use rustc_middle::mir::ProjectionElem::*;
+        Ok(match proj_elem {
+            Field(field, _) => self.operand_field(base, field.index())?,
+            Downcast(_, variant) => self.operand_downcast(base, variant)?,
+            Deref => self.deref_operand(base)?.into(),
+            Index(local) => {
+                let layout = self.layout_of(self.tcx.types.usize)?;
+                let n = self.local_to_op(self.frame(), local, Some(layout))?;
+                let n = self.read_scalar(&n)?.to_machine_usize(self)?;
+                self.operand_index(base, n)?
+            }
+            ConstantIndex { offset, min_length, from_end } => {
+                self.operand_constant_index(base, offset, min_length, from_end)?
+            }
+            Subslice { from, to, from_end } => self.operand_subslice(base, from, to, from_end)?,
+        })
+    }
+}
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index 515cc222dc6..9e74b99ecd7 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -529,7 +529,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 let receiver_place = loop {
                     match receiver.layout.ty.kind() {
                         ty::Ref(..) | ty::RawPtr(..) => break self.deref_operand(&receiver)?,
-                        ty::Dynamic(..) => break receiver.assert_mem_place(),
+                        ty::Dynamic(..) => break receiver.assert_mem_place(), // no immediate unsized values
                         _ => {
                             // Not there yet, search for the only non-ZST field.
                             let mut non_zst_field = None;
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 08102585a7b..5114ce5d452 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -847,6 +847,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                 );
             }
             Abi::Scalar(scalar_layout) => {
+                // We use a 'forced' read because we always need a `Immediate` here
+                // and treating "partially uninit" as "fully uninit" is fine for us.
                 let scalar = self.read_immediate_forced(op)?.to_scalar_or_uninit();
                 self.visit_scalar(scalar, scalar_layout)?;
             }
@@ -856,6 +858,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                 // is subtle due to enums having ScalarPair layout, where one field
                 // is the discriminant.
                 if cfg!(debug_assertions) {
+                    // We use a 'forced' read because we always need a `Immediate` here
+                    // and treating "partially uninit" as "fully uninit" is fine for us.
                     let (a, b) = self.read_immediate_forced(op)?.to_scalar_or_uninit_pair();
                     self.visit_scalar(a, a_layout)?;
                     self.visit_scalar(b, b_layout)?;
@@ -880,7 +884,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
     ) -> InterpResult<'tcx> {
         match op.layout.ty.kind() {
             ty::Str => {
-                let mplace = op.assert_mem_place(); // strings are never immediate
+                let mplace = op.assert_mem_place(); // strings are unsized and hence never immediate
                 let len = mplace.len(self.ecx)?;
                 try_validation!(
                     self.ecx.read_bytes_ptr(mplace.ptr, Size::from_bytes(len)),
@@ -900,14 +904,27 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
             {
                 // Optimized handling for arrays of integer/float type.
 
-                // Arrays cannot be immediate, slices are never immediate.
-                let mplace = op.assert_mem_place();
                 // This is the length of the array/slice.
-                let len = mplace.len(self.ecx)?;
+                let len = op.len(self.ecx)?;
                 // This is the element type size.
                 let layout = self.ecx.layout_of(*tys)?;
                 // This is the size in bytes of the whole array. (This checks for overflow.)
                 let size = layout.size * len;
+                // If the size is 0, there is nothing to check.
+                // (`size` can only be 0 of `len` is 0, and empty arrays are always valid.)
+                if size == Size::ZERO {
+                    return Ok(());
+                }
+                // Now that we definitely have a non-ZST array, we know it lives in memory.
+                let mplace = match op.try_as_mplace() {
+                    Ok(mplace) => mplace,
+                    Err(imm) => match *imm {
+                        Immediate::Uninit =>
+                            throw_validation_failure!(self.path, { "uninitialized bytes" }),
+                        Immediate::Scalar(..) | Immediate::ScalarPair(..) =>
+                            bug!("arrays/slices can never have Scalar/ScalarPair layout"),
+                    }
+                };
 
                 // Optimization: we just check the entire range at once.
                 // NOTE: Keep this in sync with the handling of integer and float
@@ -919,10 +936,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
                 // to reject those pointers, we just do not have the machinery to
                 // talk about parts of a pointer.
                 // We also accept uninit, for consistency with the slow path.
-                let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr, size, mplace.align)? else {
-                    // Size 0, nothing more to check.
-                    return Ok(());
-                };
+                let alloc = self.ecx.get_ptr_alloc(mplace.ptr, size, mplace.align)?.expect("we already excluded size 0");
 
                 match alloc.check_bytes(
                     alloc_range(Size::ZERO, size),
diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs
index ded4c6a557a..c262bca9bb4 100644
--- a/compiler/rustc_const_eval/src/interpret/visitor.rs
+++ b/compiler/rustc_const_eval/src/interpret/visitor.rs
@@ -21,8 +21,10 @@ pub trait Value<'mir, 'tcx, M: Machine<'mir, 'tcx>>: Copy {
     fn to_op(&self, ecx: &InterpCx<'mir, 'tcx, M>)
     -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>>;
 
-    /// Creates this from an `MPlaceTy`.
-    fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self;
+    /// Creates this from an `OpTy`.
+    ///
+    /// If `to_op` only ever produces `Indirect` operands, then this one is definitely `Indirect`.
+    fn from_op(mplace: OpTy<'tcx, M::PointerTag>) -> Self;
 
     /// Projects to the given enum variant.
     fn project_downcast(
@@ -56,8 +58,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M> for OpTy<'tc
     }
 
     #[inline(always)]
-    fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
-        mplace.into()
+    fn from_op(op: OpTy<'tcx, M::PointerTag>) -> Self {
+        op
     }
 
     #[inline(always)]
@@ -96,8 +98,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> Value<'mir, 'tcx, M>
     }
 
     #[inline(always)]
-    fn from_mem_place(mplace: MPlaceTy<'tcx, M::PointerTag>) -> Self {
-        mplace
+    fn from_op(op: OpTy<'tcx, M::PointerTag>) -> Self {
+        // assert is justified because our `to_op` only ever produces `Indirect` operands.
+        op.assert_mem_place()
     }
 
     #[inline(always)]
@@ -218,13 +221,13 @@ macro_rules! make_value_visitor {
                 match *v.layout().ty.kind() {
                     // If it is a trait object, switch to the real type that was used to create it.
                     ty::Dynamic(..) => {
-                        // immediate trait objects are not a thing
+                        // unsized values are never immediate, so we can assert_mem_place
                         let op = v.to_op(self.ecx())?;
                         let dest = op.assert_mem_place();
                         let inner = self.ecx().unpack_dyn_trait(&dest)?.1;
                         trace!("walk_value: dyn object layout: {:#?}", inner.layout);
                         // recurse with the inner type
-                        return self.visit_field(&v, 0, &Value::from_mem_place(inner));
+                        return self.visit_field(&v, 0, &Value::from_op(inner.into()));
                     },
                     // Slices do not need special handling here: they have `Array` field
                     // placement with length 0, so we enter the `Array` case below which
@@ -292,13 +295,12 @@ macro_rules! make_value_visitor {
                     FieldsShape::Array { .. } => {
                         // Let's get an mplace first.
                         let op = v.to_op(self.ecx())?;
-                        let mplace = op.assert_mem_place();
                         // Now we can go over all the fields.
                         // This uses the *run-time length*, i.e., if we are a slice,
                         // the dynamic info from the metadata is used.
-                        let iter = self.ecx().mplace_array_fields(&mplace)?
+                        let iter = self.ecx().operand_array_fields(&op)?
                             .map(|f| f.and_then(|f| {
-                                Ok(Value::from_mem_place(f))
+                                Ok(Value::from_op(f))
                             }));
                         self.visit_aggregate(v, iter)?;
                     }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index 17376e59e09..1d083b0bf82 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -299,7 +299,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
                 err.note(&format!("attempting to deref into `{}`", deref_target_ty));
 
                 // Check first whether the source is accessible (issue #87060)
-                if tcx.sess.source_map().span_to_snippet(deref_target).is_ok() {
+                if tcx.sess.source_map().is_span_accessible(deref_target) {
                     err.span_note(deref_target, "deref defined here");
                 }
 
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
index 6e5a0c813ac..0aa7b117b89 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
@@ -226,7 +226,7 @@ impl Qualif for CustomEq {
         // because that component may be part of an enum variant (e.g.,
         // `Option::<NonStructuralMatchTy>::Some`), in which case some values of this type may be
         // structural-match (`Option::None`).
-        traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty).is_some()
+        traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty, true).is_some()
     }
 
     fn in_adt_inherently<'tcx>(
diff --git a/compiler/rustc_error_messages/locales/en-US/expand.ftl b/compiler/rustc_error_messages/locales/en-US/expand.ftl
new file mode 100644
index 00000000000..8d506a3ea8b
--- /dev/null
+++ b/compiler/rustc_error_messages/locales/en-US/expand.ftl
@@ -0,0 +1,5 @@
+expand-explain-doc-comment-outer =
+    outer doc comments expand to `#[doc = "..."]`, which is what this macro attempted to match
+
+expand-explain-doc-comment-inner =
+    inner doc comments expand to `#![doc = "..."]`, which is what this macro attempted to match
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index 5a482bc5b2c..d16171cb162 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -33,11 +33,12 @@ pub use unic_langid::{langid, LanguageIdentifier};
 fluent_messages! {
     borrowck => "../locales/en-US/borrowck.ftl",
     builtin_macros => "../locales/en-US/builtin_macros.ftl",
+    const_eval => "../locales/en-US/const_eval.ftl",
+    expand => "../locales/en-US/expand.ftl",
     lint => "../locales/en-US/lint.ftl",
     parser => "../locales/en-US/parser.ftl",
     privacy => "../locales/en-US/privacy.ftl",
     typeck => "../locales/en-US/typeck.ftl",
-    const_eval => "../locales/en-US/const_eval.ftl",
 }
 
 pub use fluent_generated::{self as fluent, DEFAULT_LOCALE_RESOURCES};
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 9429ad1a897..da321c45875 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -614,6 +614,14 @@ impl Diagnostic {
         self
     }
 
+    /// Clear any existing suggestions.
+    pub fn clear_suggestions(&mut self) -> &mut Self {
+        if let Ok(suggestions) = &mut self.suggestions {
+            suggestions.clear();
+        }
+        self
+    }
+
     /// Helper for pushing to `self.suggestions`, if available (not disable).
     fn push_suggestion(&mut self, suggestion: CodeSuggestion) {
         if let Ok(suggestions) = &mut self.suggestions {
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index c3341fd68f4..99ac6a3546e 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -461,6 +461,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     forward!(pub fn set_is_lint(&mut self,) -> &mut Self);
 
     forward!(pub fn disable_suggestions(&mut self,) -> &mut Self);
+    forward!(pub fn clear_suggestions(&mut self,) -> &mut Self);
 
     forward!(pub fn multipart_suggestion(
         &mut self,
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index ffe4ecebb2e..e59a74e380a 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -1558,7 +1558,7 @@ pub fn add_elided_lifetime_in_path_suggestion(
     insertion_span: Span,
 ) {
     diag.span_label(path_span, format!("expected lifetime parameter{}", pluralize!(n)));
-    if source_map.span_to_snippet(insertion_span).is_err() {
+    if !source_map.is_span_accessible(insertion_span) {
         // Do not try to suggest anything if generated by a proc-macro.
         return;
     }
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index f40c365cbcc..3e9ddd6aec0 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -14,7 +14,7 @@ use rustc_ast::{NodeId, DUMMY_NODE_ID};
 use rustc_ast_pretty::pprust;
 use rustc_attr::{self as attr, TransparencyError};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
 use rustc_feature::Features;
 use rustc_lint_defs::builtin::{
     RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
@@ -25,6 +25,7 @@ use rustc_session::parse::ParseSess;
 use rustc_session::Session;
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::Transparency;
+use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::{kw, sym, Ident, MacroRulesNormalizedIdent};
 use rustc_span::Span;
 
@@ -345,7 +346,7 @@ fn expand_macro<'cx>(
     if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) {
         err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro");
     }
-
+    annotate_doc_comment(&mut err, sess.source_map(), span);
     // Check whether there's a missing comma in this macro call, like `println!("{}" a);`
     if let Some((arg, comma_span)) = arg.add_comma() {
         for lhs in lhses {
@@ -453,7 +454,10 @@ pub fn compile_declarative_macro(
         Failure(token, msg) => {
             let s = parse_failure_msg(&token);
             let sp = token.span.substitute_dummy(def.span);
-            sess.parse_sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit();
+            let mut err = sess.parse_sess.span_diagnostic.struct_span_err(sp, &s);
+            err.span_label(sp, msg);
+            annotate_doc_comment(&mut err, sess.source_map(), sp);
+            err.emit();
             return dummy_syn_ext();
         }
         Error(sp, msg) => {
@@ -590,6 +594,34 @@ pub fn compile_declarative_macro(
     (mk_syn_ext(expander), rule_spans)
 }
 
+#[derive(SessionSubdiagnostic)]
+enum ExplainDocComment {
+    #[label(expand::explain_doc_comment_inner)]
+    Inner {
+        #[primary_span]
+        span: Span,
+    },
+    #[label(expand::explain_doc_comment_outer)]
+    Outer {
+        #[primary_span]
+        span: Span,
+    },
+}
+
+fn annotate_doc_comment(
+    err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+    sm: &SourceMap,
+    span: Span,
+) {
+    if let Ok(src) = sm.span_to_snippet(span) {
+        if src.starts_with("///") || src.starts_with("/**") {
+            err.subdiagnostic(ExplainDocComment::Outer { span });
+        } else if src.starts_with("//!") || src.starts_with("/*!") {
+            err.subdiagnostic(ExplainDocComment::Inner { span });
+        }
+    }
+}
+
 fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree) -> bool {
     // lhs is going to be like TokenTree::Delimited(...), where the
     // entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index d1477f9e2ae..0ecb83996a8 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -1111,11 +1111,9 @@ macro_rules! visit_place_fns {
             context: PlaceContext,
             location: Location,
         ) {
-            // FIXME: Use PlaceRef::iter_projections, once that exists.
-            let mut cursor = place_ref.projection;
-            while let &[ref proj_base @ .., elem] = cursor {
-                cursor = proj_base;
-                self.visit_projection_elem(place_ref.local, cursor, elem, context, location);
+            for (base, elem) in place_ref.iter_projections().rev() {
+                let base_proj = base.projection;
+                self.visit_projection_elem(place_ref.local, base_proj, elem, context, location);
             }
         }
 
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs
index 84547dca453..add2df25884 100644
--- a/compiler/rustc_middle/src/ty/generics.rs
+++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -85,10 +85,10 @@ impl GenericParamDef {
     ) -> Option<EarlyBinder<ty::GenericArg<'tcx>>> {
         match self.kind {
             GenericParamDefKind::Type { has_default, .. } if has_default => {
-                Some(EarlyBinder(tcx.type_of(self.def_id).into()))
+                Some(tcx.bound_type_of(self.def_id).map_bound(|t| t.into()))
             }
             GenericParamDefKind::Const { has_default } if has_default => {
-                Some(EarlyBinder(tcx.const_param_default(self.def_id).into()))
+                Some(tcx.bound_const_param_default(self.def_id).map_bound(|c| c.into()))
             }
             _ => None,
         }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 4b51daadabf..d663f1a3ec6 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -932,6 +932,10 @@ impl<T> EarlyBinder<T> {
         let value = f(self.0)?;
         Ok(EarlyBinder(value))
     }
+
+    pub fn rebind<U>(&self, value: U) -> EarlyBinder<U> {
+        EarlyBinder(value)
+    }
 }
 
 impl<T> EarlyBinder<Option<T>> {
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 52da6c3a8c0..4d2f69b23fa 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -676,6 +676,10 @@ impl<'tcx> TyCtxt<'tcx> {
     ) -> ty::EarlyBinder<&'tcx ty::List<ty::Predicate<'tcx>>> {
         ty::EarlyBinder(self.item_bounds(def_id))
     }
+
+    pub fn bound_const_param_default(self, def_id: DefId) -> ty::EarlyBinder<ty::Const<'tcx>> {
+        ty::EarlyBinder(self.const_param_default(def_id))
+    }
 }
 
 struct OpaqueTypeExpander<'tcx> {
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 61ac3d14e50..75fd156ebfd 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -432,7 +432,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
                 "`let` bindings require an \"irrefutable pattern\", like a `struct` or \
                  an `enum` with only one variant",
             );
-            if self.tcx.sess.source_map().span_to_snippet(span).is_ok() {
+            if self.tcx.sess.source_map().is_span_accessible(span) {
                 let semi_span = span.shrink_to_hi().with_lo(span.hi() - BytePos(1));
                 let start_span = span.shrink_to_lo();
                 let end_span = semi_span.shrink_to_lo();
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index f22f3f61a01..e32e0b11ba4 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -120,32 +120,37 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
     }
 
     fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> {
-        traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| {
-            with_no_trimmed_paths!(match non_sm_ty.kind {
-                traits::NonStructuralMatchTyKind::Adt(adt) => self.adt_derive_msg(adt),
-                traits::NonStructuralMatchTyKind::Dynamic => {
-                    "trait objects cannot be used in patterns".to_string()
-                }
-                traits::NonStructuralMatchTyKind::Opaque => {
-                    "opaque types cannot be used in patterns".to_string()
-                }
-                traits::NonStructuralMatchTyKind::Closure => {
-                    "closures cannot be used in patterns".to_string()
-                }
-                traits::NonStructuralMatchTyKind::Generator => {
-                    "generators cannot be used in patterns".to_string()
-                }
-                traits::NonStructuralMatchTyKind::Param => {
-                    bug!("use of a constant whose type is a parameter inside a pattern")
-                }
-                traits::NonStructuralMatchTyKind::Projection => {
-                    bug!("use of a constant whose type is a projection inside a pattern")
-                }
-                traits::NonStructuralMatchTyKind::Foreign => {
-                    bug!("use of a value of a foreign type inside a pattern")
-                }
-            })
-        })
+        traits::search_for_structural_match_violation(self.span, self.tcx(), ty, true).map(
+            |non_sm_ty| {
+                with_no_trimmed_paths!(match non_sm_ty.kind {
+                    traits::NonStructuralMatchTyKind::Adt(adt) => self.adt_derive_msg(adt),
+                    traits::NonStructuralMatchTyKind::Dynamic => {
+                        "trait objects cannot be used in patterns".to_string()
+                    }
+                    traits::NonStructuralMatchTyKind::Opaque => {
+                        "opaque types cannot be used in patterns".to_string()
+                    }
+                    traits::NonStructuralMatchTyKind::Closure => {
+                        "closures cannot be used in patterns".to_string()
+                    }
+                    traits::NonStructuralMatchTyKind::Generator => {
+                        "generators cannot be used in patterns".to_string()
+                    }
+                    traits::NonStructuralMatchTyKind::Float => {
+                        "floating-point numbers cannot be used in patterns".to_string()
+                    }
+                    traits::NonStructuralMatchTyKind::Param => {
+                        bug!("use of a constant whose type is a parameter inside a pattern")
+                    }
+                    traits::NonStructuralMatchTyKind::Projection => {
+                        bug!("use of a constant whose type is a projection inside a pattern")
+                    }
+                    traits::NonStructuralMatchTyKind::Foreign => {
+                        bug!("use of a value of a foreign type inside a pattern")
+                    }
+                })
+            },
+        )
     }
 
     fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index 3be1783ae33..f3153a64820 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -537,13 +537,12 @@ fn build_call_shim<'tcx>(
     };
 
     let def_id = instance.def_id();
-    let sig = tcx.fn_sig(def_id);
-    let mut sig = tcx.erase_late_bound_regions(sig);
+    let sig = tcx.bound_fn_sig(def_id);
+    let sig = sig.map_bound(|sig| tcx.erase_late_bound_regions(sig));
 
     assert_eq!(sig_substs.is_some(), !instance.has_polymorphic_mir_body());
-    if let Some(sig_substs) = sig_substs {
-        sig = EarlyBinder(sig).subst(tcx, sig_substs);
-    }
+    let mut sig =
+        if let Some(sig_substs) = sig_substs { sig.subst(tcx, sig_substs) } else { sig.0 };
 
     if let CallKind::Indirect(fnty) = call_kind {
         // `sig` determines our local decls, and thus the callee type in the `Call` terminator. This
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 2c43563b104..f9387e29262 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1393,7 +1393,9 @@ impl<'a> Parser<'a> {
             self.parse_yield_expr(attrs)
         } else if self.is_do_yeet() {
             self.parse_yeet_expr(attrs)
-        } else if self.eat_keyword(kw::Let) {
+        } else if self.check_keyword(kw::Let) {
+            self.manage_let_chains_context();
+            self.bump();
             self.parse_let_expr(attrs)
         } else if self.eat_keyword(kw::Underscore) {
             Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs))
@@ -2355,16 +2357,30 @@ impl<'a> Parser<'a> {
         Ok(cond)
     }
 
+    // Checks if `let` is in an invalid position like `let x = let y = 1;` or
+    // if the current `let` is in a let_chains context but nested in another
+    // expression like `if let Some(_) = _opt && [1, 2, 3][let _ = ()] = 1`.
+    //
+    // This method expects that the current token is `let`.
+    fn manage_let_chains_context(&mut self) {
+        debug_assert!(matches!(self.token.kind, TokenKind::Ident(kw::Let, _)));
+        let is_in_a_let_chains_context_but_nested_in_other_expr = self.let_expr_allowed
+            && !matches!(
+                self.prev_token.kind,
+                TokenKind::AndAnd
+                    | TokenKind::CloseDelim(Delimiter::Brace)
+                    | TokenKind::Ident(kw::If, _)
+                    | TokenKind::Ident(kw::While, _)
+            );
+        if !self.let_expr_allowed || is_in_a_let_chains_context_but_nested_in_other_expr {
+            self.struct_span_err(self.token.span, "expected expression, found `let` statement")
+                .emit();
+        }
+    }
+
     /// Parses a `let $pat = $expr` pseudo-expression.
     /// The `let` token has already been eaten.
     fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
-        if !self.let_expr_allowed {
-            self.struct_span_err(
-                self.prev_token.span,
-                "expected expression, found `let` statement",
-            )
-            .emit();
-        }
         let lo = self.prev_token.span;
         let pat = self.parse_pat_allow_top_alt(
             None,
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index b4a39982b74..2851b08cd93 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -1647,7 +1647,7 @@ impl<'a> Resolver<'a> {
 
     fn binding_description(&self, b: &NameBinding<'_>, ident: Ident, from_prelude: bool) -> String {
         let res = b.res();
-        if b.span.is_dummy() || self.session.source_map().span_to_snippet(b.span).is_err() {
+        if b.span.is_dummy() || !self.session.source_map().is_span_accessible(b.span) {
             // These already contain the "built-in" prefix or look bad with it.
             let add_built_in =
                 !matches!(b.res(), Res::NonMacroAttr(..) | Res::PrimTy(..) | Res::ToolMod);
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index f37acca3d9f..098b5a0c92e 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -611,6 +611,30 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
             TyKind::Path(ref qself, ref path) => {
                 self.diagnostic_metadata.current_type_path = Some(ty);
                 self.smart_resolve_path(ty.id, qself.as_ref(), path, PathSource::Type);
+
+                // Check whether we should interpret this as a bare trait object.
+                if qself.is_none()
+                    && let Some(partial_res) = self.r.partial_res_map.get(&ty.id)
+                    && partial_res.unresolved_segments() == 0
+                    && let Res::Def(DefKind::Trait | DefKind::TraitAlias, _) = partial_res.base_res()
+                {
+                    // This path is actually a bare trait object.  In case of a bare `Fn`-trait
+                    // object with anonymous lifetimes, we need this rib to correctly place the
+                    // synthetic lifetimes.
+                    let span = ty.span.shrink_to_lo().to(path.span.shrink_to_lo());
+                    self.with_generic_param_rib(
+                        &[],
+                        NormalRibKind,
+                        LifetimeRibKind::Generics {
+                            binder: ty.id,
+                            kind: LifetimeBinderKind::PolyTrait,
+                            span,
+                        },
+                        |this| this.visit_path(&path, ty.id),
+                    );
+                    self.diagnostic_metadata.current_type_path = prev_ty;
+                    return;
+                }
             }
             TyKind::ImplicitSelf => {
                 let self_ty = Ident::with_dummy_span(kw::SelfUpper);
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 227127aed50..afbb88e9233 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -597,6 +597,13 @@ impl SourceMap {
         local_begin.sf.src.is_some() && local_end.sf.src.is_some()
     }
 
+    pub fn is_span_accessible(&self, sp: Span) -> bool {
+        self.span_to_source(sp, |src, start_index, end_index| {
+            Ok(src.get(start_index..end_index).is_some())
+        })
+        .map_or(false, |is_accessible| is_accessible)
+    }
+
     /// Returns the source snippet as `String` corresponding to the given `Span`.
     pub fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> {
         self.span_to_source(sp, |src, start_index, end_index| {
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs
index 2c7834c225b..d3fd7051a12 100644
--- a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs
@@ -13,7 +13,6 @@ pub fn target() -> Target {
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".into()),
         features: "+strict-align,+neon,+fp-armv8".into(),
-        executables: true,
         relocation_model: RelocModel::Static,
         disable_redzone: true,
         max_atomic_width: Some(128),
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs
index 1b6525a7c69..6316abe1ba9 100644
--- a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs
@@ -14,7 +14,6 @@ pub fn target() -> Target {
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".into()),
         features: "+strict-align,-neon,-fp-armv8".into(),
-        executables: true,
         relocation_model: RelocModel::Static,
         disable_redzone: true,
         max_atomic_width: Some(128),
diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs
index 713dc9a1f0e..9bfae46ef32 100644
--- a/compiler/rustc_target/src/spec/apple_base.rs
+++ b/compiler/rustc_target/src/spec/apple_base.rs
@@ -25,7 +25,6 @@ pub fn opts(os: &'static str) -> TargetOptions {
         function_sections: false,
         dynamic_linking: true,
         linker_is_gnu: false,
-        executables: true,
         families: cvs!["unix"],
         is_like_osx: true,
         default_dwarf_version: 2,
diff --git a/compiler/rustc_target/src/spec/apple_sdk_base.rs b/compiler/rustc_target/src/spec/apple_sdk_base.rs
index ecb6cbd9f8a..0328ea98c48 100644
--- a/compiler/rustc_target/src/spec/apple_sdk_base.rs
+++ b/compiler/rustc_target/src/spec/apple_sdk_base.rs
@@ -54,7 +54,6 @@ pub fn opts(os: &'static str, arch: Arch) -> TargetOptions {
         abi: target_abi(arch).into(),
         cpu: target_cpu(arch).into(),
         dynamic_linking: false,
-        executables: true,
         link_env_remove: link_env_remove(arch),
         has_thread_local: false,
         ..super::apple_base::opts(os)
diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs
index 0cb18f17310..511693abe98 100644
--- a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs
+++ b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs
@@ -14,7 +14,6 @@ pub fn target() -> Target {
             abi: "eabi".into(),
             endian: Endian::Big,
             linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
-            executables: true,
             linker: Some("rust-lld".into()),
             relocation_model: RelocModel::Static,
             panic_strategy: PanicStrategy::Abort,
diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs
index a5b7c12cc7b..5df4a0a1583 100644
--- a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs
+++ b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs
@@ -14,7 +14,6 @@ pub fn target() -> Target {
             abi: "eabihf".into(),
             endian: Endian::Big,
             linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
-            executables: true,
             linker: Some("rust-lld".into()),
             relocation_model: RelocModel::Static,
             panic_strategy: PanicStrategy::Abort,
diff --git a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs
index 8c2a9bcfde6..1bba3939397 100644
--- a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs
+++ b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs
@@ -23,7 +23,6 @@ pub fn target() -> Target {
             abi: "eabihf".into(),
             linker_flavor: LinkerFlavor::Gcc,
             cpu: "mpcore".into(),
-            executables: true,
             families: cvs!["unix"],
             linker: Some("arm-none-eabi-gcc".into()),
             relocation_model: RelocModel::Static,
diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs
index ff649434312..cb5cbe15836 100644
--- a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs
+++ b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs
@@ -22,7 +22,6 @@ pub fn target() -> Target {
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".into()),
         features: "+v7,+thumb2,+soft-float,-neon,+strict-align".into(),
-        executables: true,
         relocation_model: RelocModel::Static,
         disable_redzone: true,
         max_atomic_width: Some(64),
diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs
index c0321d0bef4..fb5dd2e7574 100644
--- a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs
+++ b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs
@@ -13,7 +13,6 @@ pub fn target() -> Target {
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".into()),
         features: "+v7,+vfp3,-d32,+thumb2,-neon,+strict-align".into(),
-        executables: true,
         relocation_model: RelocModel::Static,
         disable_redzone: true,
         max_atomic_width: Some(64),
diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs
index 2c3f79cc58b..5f1da09b317 100644
--- a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs
+++ b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs
@@ -13,7 +13,6 @@ pub fn target() -> Target {
         options: TargetOptions {
             abi: "eabi".into(),
             linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
-            executables: true,
             linker: Some("rust-lld".into()),
             relocation_model: RelocModel::Static,
             panic_strategy: PanicStrategy::Abort,
diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs
index 5c82e768483..0038ed0df8b 100644
--- a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs
+++ b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs
@@ -13,7 +13,6 @@ pub fn target() -> Target {
         options: TargetOptions {
             abi: "eabihf".into(),
             linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
-            executables: true,
             linker: Some("rust-lld".into()),
             relocation_model: RelocModel::Static,
             panic_strategy: PanicStrategy::Abort,
diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs
index 4fd6c06394d..1d441e558dd 100644
--- a/compiler/rustc_target/src/spec/avr_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs
@@ -16,7 +16,6 @@ pub fn target(target_cpu: &'static str, mmcu: &'static str) -> Target {
             exe_suffix: ".elf".into(),
 
             linker: Some("avr-gcc".into()),
-            executables: true,
             eh_frame_header: false,
             pre_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &[mmcu]),
             late_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &["-lgcc"]),
diff --git a/compiler/rustc_target/src/spec/bpf_base.rs b/compiler/rustc_target/src/spec/bpf_base.rs
index 4fa6e12f5ba..3c4da6f883d 100644
--- a/compiler/rustc_target/src/spec/bpf_base.rs
+++ b/compiler/rustc_target/src/spec/bpf_base.rs
@@ -7,7 +7,6 @@ pub fn opts(endian: Endian) -> TargetOptions {
         endian,
         linker_flavor: LinkerFlavor::BpfLinker,
         atomic_cas: false,
-        executables: true,
         dynamic_linking: true,
         no_builtins: true,
         panic_strategy: PanicStrategy::Abort,
diff --git a/compiler/rustc_target/src/spec/dragonfly_base.rs b/compiler/rustc_target/src/spec/dragonfly_base.rs
index c1e469746cb..de2be781796 100644
--- a/compiler/rustc_target/src/spec/dragonfly_base.rs
+++ b/compiler/rustc_target/src/spec/dragonfly_base.rs
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "dragonfly".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         has_rpath: true,
         position_independent_executables: true,
diff --git a/compiler/rustc_target/src/spec/freebsd_base.rs b/compiler/rustc_target/src/spec/freebsd_base.rs
index 36312d26e37..8c141aaaec3 100644
--- a/compiler/rustc_target/src/spec/freebsd_base.rs
+++ b/compiler/rustc_target/src/spec/freebsd_base.rs
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "freebsd".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         has_rpath: true,
         position_independent_executables: true,
diff --git a/compiler/rustc_target/src/spec/fuchsia_base.rs b/compiler/rustc_target/src/spec/fuchsia_base.rs
index b02b70f76ee..df1e3275f73 100644
--- a/compiler/rustc_target/src/spec/fuchsia_base.rs
+++ b/compiler/rustc_target/src/spec/fuchsia_base.rs
@@ -23,7 +23,6 @@ pub fn opts() -> TargetOptions {
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".into()),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         pre_link_args,
         pre_link_objects: crt_objects::new(&[
diff --git a/compiler/rustc_target/src/spec/haiku_base.rs b/compiler/rustc_target/src/spec/haiku_base.rs
index 61c05a2bdb6..8ab874410aa 100644
--- a/compiler/rustc_target/src/spec/haiku_base.rs
+++ b/compiler/rustc_target/src/spec/haiku_base.rs
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "haiku".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         relro_level: RelroLevel::Full,
         ..Default::default()
diff --git a/compiler/rustc_target/src/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs
index e43153177f0..562ccef7eba 100644
--- a/compiler/rustc_target/src/spec/hermit_base.rs
+++ b/compiler/rustc_target/src/spec/hermit_base.rs
@@ -10,7 +10,6 @@ pub fn opts() -> TargetOptions {
         os: "hermit".into(),
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
         linker: Some("rust-lld".into()),
-        executables: true,
         has_thread_local: true,
         pre_link_args,
         panic_strategy: PanicStrategy::Abort,
diff --git a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs
index 80cf09517cc..cc2c78c69fe 100644
--- a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs
@@ -11,7 +11,6 @@ pub fn target() -> Target {
     base.has_rpath = true;
     base.linker_is_gnu = false;
     base.dynamic_linking = true;
-    base.executables = true;
 
     base.c_enum_min_bits = 8;
 
diff --git a/compiler/rustc_target/src/spec/illumos_base.rs b/compiler/rustc_target/src/spec/illumos_base.rs
index b0e1b109be1..77e000474b8 100644
--- a/compiler/rustc_target/src/spec/illumos_base.rs
+++ b/compiler/rustc_target/src/spec/illumos_base.rs
@@ -27,7 +27,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "illumos".into(),
         dynamic_linking: true,
-        executables: true,
         has_rpath: true,
         families: cvs!["unix"],
         is_like_solaris: true,
diff --git a/compiler/rustc_target/src/spec/l4re_base.rs b/compiler/rustc_target/src/spec/l4re_base.rs
index 7a051532f82..a08756861e5 100644
--- a/compiler/rustc_target/src/spec/l4re_base.rs
+++ b/compiler/rustc_target/src/spec/l4re_base.rs
@@ -5,7 +5,6 @@ pub fn opts() -> TargetOptions {
         os: "l4re".into(),
         env: "uclibc".into(),
         linker_flavor: LinkerFlavor::L4Bender,
-        executables: true,
         panic_strategy: PanicStrategy::Abort,
         linker: Some("l4-bender".into()),
         linker_is_gnu: false,
diff --git a/compiler/rustc_target/src/spec/linux_base.rs b/compiler/rustc_target/src/spec/linux_base.rs
index 0f79ada0d93..f4fce3b4050 100644
--- a/compiler/rustc_target/src/spec/linux_base.rs
+++ b/compiler/rustc_target/src/spec/linux_base.rs
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "linux".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         has_rpath: true,
         position_independent_executables: true,
diff --git a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs
index e3522de6de0..cfc8ec21c2a 100644
--- a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs
+++ b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs
@@ -18,7 +18,6 @@ pub fn target() -> Target {
             vendor: "sony".into(),
             linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
             cpu: "mips2".into(),
-            executables: true,
             linker: Some("rust-lld".into()),
             relocation_model: RelocModel::Static,
 
diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs
index 736af15cf44..fe2aa2de871 100644
--- a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs
+++ b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs
@@ -17,7 +17,6 @@ pub fn target() -> Target {
             cpu: "mips32r2".into(),
             features: "+mips32r2,+soft-float,+noabicalls".into(),
             max_atomic_width: Some(32),
-            executables: true,
             linker: Some("rust-lld".into()),
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 48ccb10f214..ef49fc8e968 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1212,8 +1212,7 @@ pub struct TargetOptions {
     pub dynamic_linking: bool,
     /// If dynamic linking is available, whether only cdylibs are supported.
     pub only_cdylib: bool,
-    /// Whether executables are available on this target. iOS, for example, only allows static
-    /// libraries. Defaults to false.
+    /// Whether executables are available on this target. Defaults to true.
     pub executables: bool,
     /// Relocation model to use in object file. Corresponds to `llc
     /// -relocation-model=$relocation_model`. Defaults to `Pic`.
@@ -1520,7 +1519,7 @@ impl Default for TargetOptions {
             features: "".into(),
             dynamic_linking: false,
             only_cdylib: false,
-            executables: false,
+            executables: true,
             relocation_model: RelocModel::Pic,
             code_model: None,
             tls_model: TlsModel::GeneralDynamic,
diff --git a/compiler/rustc_target/src/spec/msp430_none_elf.rs b/compiler/rustc_target/src/spec/msp430_none_elf.rs
index cedacb60f31..6b09386ae3e 100644
--- a/compiler/rustc_target/src/spec/msp430_none_elf.rs
+++ b/compiler/rustc_target/src/spec/msp430_none_elf.rs
@@ -9,7 +9,6 @@ pub fn target() -> Target {
 
         options: TargetOptions {
             c_int_width: "16".into(),
-            executables: true,
 
             // The LLVM backend currently can't generate object files. To
             // workaround this LLVM generates assembly files which then we feed
diff --git a/compiler/rustc_target/src/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs
index c4df4b546e3..edb30b72bf6 100644
--- a/compiler/rustc_target/src/spec/msvc_base.rs
+++ b/compiler/rustc_target/src/spec/msvc_base.rs
@@ -7,7 +7,6 @@ pub fn opts() -> TargetOptions {
 
     TargetOptions {
         linker_flavor: LinkerFlavor::Msvc,
-        executables: true,
         is_like_windows: true,
         is_like_msvc: true,
         lld_flavor: LldFlavor::Link,
diff --git a/compiler/rustc_target/src/spec/netbsd_base.rs b/compiler/rustc_target/src/spec/netbsd_base.rs
index 40ef04ba043..be94ea23465 100644
--- a/compiler/rustc_target/src/spec/netbsd_base.rs
+++ b/compiler/rustc_target/src/spec/netbsd_base.rs
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "netbsd".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         no_default_libraries: false,
         has_rpath: true,
diff --git a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs
index 9d94ed8aa48..1c5b68001b9 100644
--- a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs
+++ b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs
@@ -26,7 +26,6 @@ pub fn target() -> Target {
 
             // Needed to use `dylib` and `bin` crate types and the linker.
             dynamic_linking: true,
-            executables: true,
 
             // Avoid using dylib because it contain metadata not supported
             // by LLVM NVPTX backend.
diff --git a/compiler/rustc_target/src/spec/openbsd_base.rs b/compiler/rustc_target/src/spec/openbsd_base.rs
index 51cecdd47ea..e7db14e05a4 100644
--- a/compiler/rustc_target/src/spec/openbsd_base.rs
+++ b/compiler/rustc_target/src/spec/openbsd_base.rs
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "openbsd".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         has_rpath: true,
         abi_return_struct_as_int: true,
diff --git a/compiler/rustc_target/src/spec/redox_base.rs b/compiler/rustc_target/src/spec/redox_base.rs
index 1878cc3fc11..468fe478549 100644
--- a/compiler/rustc_target/src/spec/redox_base.rs
+++ b/compiler/rustc_target/src/spec/redox_base.rs
@@ -5,7 +5,6 @@ pub fn opts() -> TargetOptions {
         os: "redox".into(),
         env: "relibc".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         has_rpath: true,
         position_independent_executables: true,
diff --git a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs
index 7124e2df9b3..232139db6ca 100644
--- a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs
@@ -14,7 +14,6 @@ pub fn target() -> Target {
             cpu: "generic-rv32".into(),
             max_atomic_width: Some(0),
             atomic_cas: false,
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             emit_debug_gdb_scripts: false,
diff --git a/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs
index 508982eed68..3e5d2887f43 100644
--- a/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs
@@ -15,7 +15,6 @@ pub fn target() -> Target {
             max_atomic_width: Some(0),
             atomic_cas: false,
             features: "+m".into(),
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             emit_debug_gdb_scripts: false,
diff --git a/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs
index f2bd6249f0a..99317b9f118 100644
--- a/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs
@@ -14,7 +14,6 @@ pub fn target() -> Target {
             cpu: "generic-rv32".into(),
             max_atomic_width: Some(32),
             features: "+m,+a,+c".into(),
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             emit_debug_gdb_scripts: false,
diff --git a/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs b/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs
index b46ca159370..a5de645c984 100644
--- a/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs
+++ b/compiler/rustc_target/src/spec/riscv32imac_unknown_xous_elf.rs
@@ -15,7 +15,6 @@ pub fn target() -> Target {
             cpu: "generic-rv32".into(),
             max_atomic_width: Some(32),
             features: "+m,+a,+c".into(),
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             ..Default::default()
diff --git a/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs b/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs
index 0200862c7e0..03baef65c0d 100644
--- a/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs
+++ b/compiler/rustc_target/src/spec/riscv32imc_esp_espidf.rs
@@ -26,7 +26,6 @@ pub fn target() -> Target {
             atomic_cas: true,
 
             features: "+m,+c".into(),
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             emit_debug_gdb_scripts: false,
diff --git a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs
index 4216968cb77..bf510d204a7 100644
--- a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs
@@ -15,7 +15,6 @@ pub fn target() -> Target {
             max_atomic_width: Some(0),
             atomic_cas: false,
             features: "+m,+c".into(),
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             emit_debug_gdb_scripts: false,
diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs
index 2a93459ef4f..03b3cfd1eb1 100644
--- a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs
@@ -15,7 +15,6 @@ pub fn target() -> Target {
             cpu: "generic-rv64".into(),
             max_atomic_width: Some(64),
             features: "+m,+a,+f,+d,+c".into(),
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             code_model: Some(CodeModel::Medium),
diff --git a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs
index 6a8d8a97de6..2a94c9dd233 100644
--- a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs
@@ -14,7 +14,6 @@ pub fn target() -> Target {
             cpu: "generic-rv64".into(),
             max_atomic_width: Some(64),
             features: "+m,+a,+c".into(),
-            executables: true,
             panic_strategy: PanicStrategy::Abort,
             relocation_model: RelocModel::Static,
             code_model: Some(CodeModel::Medium),
diff --git a/compiler/rustc_target/src/spec/solaris_base.rs b/compiler/rustc_target/src/spec/solaris_base.rs
index d61e1b2ec10..b7e8e8cf7f5 100644
--- a/compiler/rustc_target/src/spec/solaris_base.rs
+++ b/compiler/rustc_target/src/spec/solaris_base.rs
@@ -4,7 +4,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         os: "solaris".into(),
         dynamic_linking: true,
-        executables: true,
         has_rpath: true,
         families: cvs!["unix"],
         is_like_solaris: true,
diff --git a/compiler/rustc_target/src/spec/solid_base.rs b/compiler/rustc_target/src/spec/solid_base.rs
index c5602a4513a..c585a6cd58e 100644
--- a/compiler/rustc_target/src/spec/solid_base.rs
+++ b/compiler/rustc_target/src/spec/solid_base.rs
@@ -5,6 +5,7 @@ pub fn opts(kernel: &str) -> TargetOptions {
     TargetOptions {
         os: format!("solid_{}", kernel).into(),
         vendor: "kmc".into(),
+        executables: false,
         frame_pointer: FramePointer::NonLeaf,
         has_thread_local: true,
         ..Default::default()
diff --git a/compiler/rustc_target/src/spec/thumb_base.rs b/compiler/rustc_target/src/spec/thumb_base.rs
index ef6038e6120..049142b89f1 100644
--- a/compiler/rustc_target/src/spec/thumb_base.rs
+++ b/compiler/rustc_target/src/spec/thumb_base.rs
@@ -34,7 +34,6 @@ pub fn opts() -> TargetOptions {
     // See rust-lang/rfcs#1645 for a discussion about these defaults
     TargetOptions {
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
-        executables: true,
         // In most cases, LLD is good enough
         linker: Some("rust-lld".into()),
         // Because these devices have very little resources having an unwinder is too onerous so we
diff --git a/compiler/rustc_target/src/spec/vxworks_base.rs b/compiler/rustc_target/src/spec/vxworks_base.rs
index 2beb279e398..aa4784b63e7 100644
--- a/compiler/rustc_target/src/spec/vxworks_base.rs
+++ b/compiler/rustc_target/src/spec/vxworks_base.rs
@@ -8,7 +8,6 @@ pub fn opts() -> TargetOptions {
         linker: Some("wr-c++".into()),
         exe_suffix: ".vxe".into(),
         dynamic_linking: true,
-        executables: true,
         families: cvs!["unix"],
         has_rpath: true,
         has_thread_local: true,
diff --git a/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs
index 5736402ae14..9216d3e7b65 100644
--- a/compiler/rustc_target/src/spec/wasm_base.rs
+++ b/compiler/rustc_target/src/spec/wasm_base.rs
@@ -62,9 +62,6 @@ pub fn options() -> TargetOptions {
         dynamic_linking: true,
         only_cdylib: true,
 
-        // This means we'll just embed a `#[start]` function in the wasm module
-        executables: true,
-
         // relatively self-explanatory!
         exe_suffix: ".wasm".into(),
         dll_prefix: "".into(),
diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs
index a0480f386f7..90e0af3e38a 100644
--- a/compiler/rustc_target/src/spec/windows_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs
@@ -67,7 +67,6 @@ pub fn opts() -> TargetOptions {
         function_sections: false,
         linker: Some("gcc".into()),
         dynamic_linking: true,
-        executables: true,
         dll_prefix: "".into(),
         dll_suffix: ".dll".into(),
         exe_suffix: ".exe".into(),
diff --git a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs
index 30f995007a9..bae007dc9f3 100644
--- a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs
+++ b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs
@@ -20,7 +20,6 @@ pub fn opts() -> TargetOptions {
         abi: "llvm".into(),
         linker: Some("clang".into()),
         dynamic_linking: true,
-        executables: true,
         dll_prefix: "".into(),
         dll_suffix: ".dll".into(),
         exe_suffix: ".exe".into(),
diff --git a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
index 334dec43ef7..fa69b919cec 100644
--- a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
@@ -24,7 +24,6 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         abi: "uwp".into(),
         vendor: "uwp".into(),
-        executables: false,
         limit_rdylib_exports: false,
         late_link_args,
         late_link_args_dynamic,
diff --git a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs
index 4348d924579..9d597ea2e62 100644
--- a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs
+++ b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs
@@ -62,7 +62,6 @@ pub fn target() -> Target {
         vendor: "fortanix".into(),
         abi: "fortanix".into(),
         linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld),
-        executables: true,
         linker: Some("rust-lld".into()),
         max_atomic_width: Some(64),
         cpu: "x86-64".into(),
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs
index 0c510dfaa12..809fd642d41 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs
@@ -24,7 +24,6 @@ pub fn target() -> Target {
         features:
             "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float"
                 .into(),
-        executables: true,
         disable_redzone: true,
         panic_strategy: PanicStrategy::Abort,
         code_model: Some(CodeModel::Kernel),
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index aa1c9136289..8d7c6b26ba1 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -673,6 +673,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                             if !self.report_similar_impl_candidates(
                                 impl_candidates,
                                 trait_ref,
+                                obligation.cause.body_id,
                                 &mut err,
                             ) {
                                 // This is *almost* equivalent to
@@ -707,6 +708,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                                     self.report_similar_impl_candidates(
                                         impl_candidates,
                                         trait_ref,
+                                        obligation.cause.body_id,
                                         &mut err,
                                     );
                                 }
@@ -1353,6 +1355,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
         &self,
         impl_candidates: Vec<ImplCandidate<'tcx>>,
         trait_ref: ty::PolyTraitRef<'tcx>,
+        body_id: hir::HirId,
         err: &mut Diagnostic,
     ) -> bool;
 
@@ -1735,6 +1738,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
         &self,
         impl_candidates: Vec<ImplCandidate<'tcx>>,
         trait_ref: ty::PolyTraitRef<'tcx>,
+        body_id: hir::HirId,
         err: &mut Diagnostic,
     ) -> bool {
         let report = |mut candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| {
@@ -1805,8 +1809,24 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
                         || self.tcx.is_builtin_derive(def_id)
                 })
                 .filter_map(|def_id| self.tcx.impl_trait_ref(def_id))
-                // Avoid mentioning type parameters.
-                .filter(|trait_ref| !matches!(trait_ref.self_ty().kind(), ty::Param(_)))
+                .filter(|trait_ref| {
+                    let self_ty = trait_ref.self_ty();
+                    // Avoid mentioning type parameters.
+                    if let ty::Param(_) = self_ty.kind() {
+                        false
+                    }
+                    // Avoid mentioning types that are private to another crate
+                    else if let ty::Adt(def, _) = self_ty.peel_refs().kind() {
+                        // FIXME(compiler-errors): This could be generalized, both to
+                        // be more granular, and probably look past other `#[fundamental]`
+                        // types, too.
+                        self.tcx
+                            .visibility(def.did())
+                            .is_accessible_from(body_id.owner.to_def_id(), self.tcx)
+                    } else {
+                        true
+                    }
+                })
                 .collect();
             return report(normalized_impl_candidates, err);
         }
@@ -2074,6 +2094,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
                         //    |
                         //    = note: cannot satisfy `_: Tt`
 
+                        // Clear any more general suggestions in favor of our specific one
+                        err.clear_suggestions();
+
                         err.span_suggestion_verbose(
                             span.shrink_to_hi(),
                             &format!(
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index e1131140c39..4862631980e 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -12,7 +12,7 @@ use rustc_index::bit_set::GrowableBitSet;
 use rustc_infer::infer::InferOk;
 use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef};
-use rustc_middle::ty::{self, EarlyBinder, GenericParamDefKind, Ty, TyCtxt};
+use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt};
 use rustc_middle::ty::{ToPolyTraitRef, ToPredicate};
 use rustc_span::def_id::DefId;
 
@@ -555,7 +555,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
                         let bound_vars = tcx.mk_bound_variable_kinds(bound_vars.into_iter());
                         let bound =
-                            EarlyBinder(bound.0.kind().skip_binder()).subst(tcx, assoc_ty_substs);
+                            bound.map_bound(|b| b.kind().skip_binder()).subst(tcx, assoc_ty_substs);
                         tcx.mk_predicate(ty::Binder::bind_with_vars(bound, bound_vars))
                     };
                 let normalized_bound = normalize_with_depth_to(
diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs
index 94ca138b9d2..6c0b83fbd03 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_match.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs
@@ -26,6 +26,7 @@ pub enum NonStructuralMatchTyKind<'tcx> {
     Closure,
     Generator,
     Projection,
+    Float,
 }
 
 /// This method traverses the structure of `ty`, trying to find an
@@ -53,12 +54,16 @@ pub enum NonStructuralMatchTyKind<'tcx> {
 /// For more background on why Rust has this requirement, and issues
 /// that arose when the requirement was not enforced completely, see
 /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
+///
+/// The floats_allowed flag is used to deny constants in floating point
 pub fn search_for_structural_match_violation<'tcx>(
     span: Span,
     tcx: TyCtxt<'tcx>,
     ty: Ty<'tcx>,
+    floats_allowed: bool,
 ) -> Option<NonStructuralMatchTy<'tcx>> {
-    ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default() }).break_value()
+    ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), floats_allowed })
+        .break_value()
 }
 
 /// This method returns true if and only if `adt_ty` itself has been marked as
@@ -119,6 +124,8 @@ struct Search<'tcx> {
     /// Tracks ADTs previously encountered during search, so that
     /// we will not recur on them again.
     seen: FxHashSet<hir::def_id::DefId>,
+
+    floats_allowed: bool,
 }
 
 impl<'tcx> Search<'tcx> {
@@ -192,13 +199,24 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
                 // for empty array.
                 return ControlFlow::CONTINUE;
             }
-            ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => {
+            ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::Never => {
                 // These primitive types are always structural match.
                 //
                 // `Never` is kind of special here, but as it is not inhabitable, this should be fine.
                 return ControlFlow::CONTINUE;
             }
 
+            ty::Float(_) => {
+                if self.floats_allowed {
+                    return ControlFlow::CONTINUE;
+                } else {
+                    return ControlFlow::Break(NonStructuralMatchTy {
+                        ty,
+                        kind: NonStructuralMatchTyKind::Float,
+                    });
+                }
+            }
+
             ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => {
                 // First check all contained types and then tell the caller to continue searching.
                 return ty.super_visit_with(self);
diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs
index d111008e82c..c873cf27e42 100644
--- a/compiler/rustc_typeck/src/astconv/errors.rs
+++ b/compiler/rustc_typeck/src/astconv/errors.rs
@@ -164,10 +164,62 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 suggested_name,
                 Applicability::MaybeIncorrect,
             );
-        } else {
-            err.span_label(span, format!("associated type `{}` not found", assoc_name));
+            return err.emit();
         }
 
+        // If we didn't find a good item in the supertraits (or couldn't get
+        // the supertraits), like in ItemCtxt, then look more generally from
+        // all visible traits. If there's one clear winner, just suggest that.
+
+        let visible_traits: Vec<_> = self
+            .tcx()
+            .all_traits()
+            .filter(|trait_def_id| {
+                let viz = self.tcx().visibility(*trait_def_id);
+                if let Some(def_id) = self.item_def_id() {
+                    viz.is_accessible_from(def_id, self.tcx())
+                } else {
+                    viz.is_visible_locally()
+                }
+            })
+            .collect();
+
+        let wider_candidate_names: Vec<_> = visible_traits
+            .iter()
+            .flat_map(|trait_def_id| {
+                self.tcx().associated_items(*trait_def_id).in_definition_order()
+            })
+            .filter_map(
+                |item| if item.kind == ty::AssocKind::Type { Some(item.name) } else { None },
+            )
+            .collect();
+
+        if let (Some(suggested_name), true) = (
+            find_best_match_for_name(&wider_candidate_names, assoc_name.name, None),
+            assoc_name.span != DUMMY_SP,
+        ) {
+            if let [best_trait] = visible_traits
+                .iter()
+                .filter(|trait_def_id| {
+                    self.tcx()
+                        .associated_items(*trait_def_id)
+                        .filter_by_name_unhygienic(suggested_name)
+                        .any(|item| item.kind == ty::AssocKind::Type)
+                })
+                .collect::<Vec<_>>()[..]
+            {
+                err.span_label(
+                    assoc_name.span,
+                    format!(
+                        "there is a similarly named associated type `{suggested_name}` in the trait `{}`",
+                        self.tcx().def_path_str(*best_trait)
+                    ),
+                );
+                return err.emit();
+            }
+        }
+
+        err.span_label(span, format!("associated type `{}` not found", assoc_name));
         err.emit()
     }
 
diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
index 0a2b54eec47..1d4e64b6bfc 100644
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -550,7 +550,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     GenericParamDefKind::Const { has_default } => {
                         let ty = tcx.at(self.span).type_of(param.def_id);
                         if !infer_args && has_default {
-                            EarlyBinder(tcx.const_param_default(param.def_id))
+                            tcx.bound_const_param_default(param.def_id)
                                 .subst(tcx, substs.unwrap())
                                 .into()
                         } else {
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs
index eb7e52c5ed3..dc553d1441e 100644
--- a/compiler/rustc_typeck/src/check/demand.rs
+++ b/compiler/rustc_typeck/src/check/demand.rs
@@ -760,7 +760,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     if let Some(call_span) =
                         iter::successors(Some(expr.span), |s| s.parent_callsite())
                             .find(|&s| sp.contains(s))
-                        && sm.span_to_snippet(call_span).is_ok()
+                        && sm.is_span_accessible(call_span)
                     {
                         return Some((
                             sp.with_hi(call_span.lo()),
@@ -773,7 +773,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     return None;
                 }
                 if sp.contains(expr.span)
-                    && sm.span_to_snippet(expr.span).is_ok()
+                    && sm.is_span_accessible(expr.span)
                 {
                     return Some((
                         sp.with_hi(expr.span.lo()),
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 58c01a34cad..1d3608048f2 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -48,7 +48,7 @@ use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable};
 use rustc_session::parse::feature_err;
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::lev_distance::find_best_match_for_name;
-use rustc_span::source_map::Span;
+use rustc_span::source_map::{Span, Spanned};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{BytePos, Pos};
 use rustc_target::spec::abi::Abi::RustIntrinsic;
@@ -2162,14 +2162,55 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         } else if !expr_t.is_primitive_ty() {
             self.ban_nonexisting_field(field, base, expr, expr_t);
         } else {
-            type_error_struct!(
+            let field_name = field.to_string();
+            let mut err = type_error_struct!(
                 self.tcx().sess,
                 field.span,
                 expr_t,
                 E0610,
                 "`{expr_t}` is a primitive type and therefore doesn't have fields",
-            )
-            .emit();
+            );
+            let is_valid_suffix = |field: String| {
+                if field == "f32" || field == "f64" {
+                    return true;
+                }
+                let mut chars = field.chars().peekable();
+                match chars.peek() {
+                    Some('e') | Some('E') => {
+                        chars.next();
+                        if let Some(c) = chars.peek()
+                            && !c.is_numeric() && *c != '-' && *c != '+'
+                        {
+                            return false;
+                        }
+                        while let Some(c) = chars.peek() {
+                            if !c.is_numeric() {
+                                break;
+                            }
+                            chars.next();
+                        }
+                    }
+                    _ => (),
+                }
+                let suffix = chars.collect::<String>();
+                suffix.is_empty() || suffix == "f32" || suffix == "f64"
+            };
+            if let ty::Infer(ty::IntVar(_)) = expr_t.kind()
+                && let ExprKind::Lit(Spanned {
+                    node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed),
+                    ..
+                }) = base.kind
+                && !base.span.from_expansion()
+                && is_valid_suffix(field_name)
+            {
+                err.span_suggestion_verbose(
+                    field.span.shrink_to_lo(),
+                    "If the number is meant to be a floating point number, consider adding a `0` after the period",
+                    '0',
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            err.emit();
         }
 
         self.tcx().ty_error()
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
index cf7de1dc016..d15d40bc247 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
@@ -1426,7 +1426,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                     GenericParamDefKind::Const { has_default } => {
                         if !infer_args && has_default {
-                            EarlyBinder(tcx.const_param_default(param.def_id))
+                            tcx.bound_const_param_default(param.def_id)
                                 .subst(tcx, substs.unwrap())
                                 .into()
                         } else {
diff --git a/compiler/rustc_typeck/src/check/intrinsicck.rs b/compiler/rustc_typeck/src/check/intrinsicck.rs
index cc91f2431e0..a5add1e9a8a 100644
--- a/compiler/rustc_typeck/src/check/intrinsicck.rs
+++ b/compiler/rustc_typeck/src/check/intrinsicck.rs
@@ -4,7 +4,7 @@ use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_index::vec::Idx;
 use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
-use rustc_middle::ty::{self, Article, FloatTy, InferTy, IntTy, Ty, TyCtxt, TypeVisitable, UintTy};
+use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitable, UintTy};
 use rustc_session::lint;
 use rustc_span::{Span, Symbol, DUMMY_SP};
 use rustc_target::abi::{Pointer, VariantIdx};
@@ -99,8 +99,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err.emit();
     }
 
+    // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()`
     fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
-        if ty.is_sized(self.tcx.at(DUMMY_SP), self.param_env) {
+        // Type still may have region variables, but `Sized` does not depend
+        // on those, so just erase them before querying.
+        if self.tcx.erase_regions(ty).is_sized(self.tcx.at(DUMMY_SP), self.param_env) {
             return true;
         }
         if let ty::Foreign(..) = ty.kind() {
@@ -128,30 +131,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             64 => InlineAsmType::I64,
             _ => unreachable!(),
         };
+
+        // Expect types to be fully resolved, no const or type variables.
+        if ty.has_infer_types_or_consts() {
+            assert!(self.is_tainted_by_errors());
+            return None;
+        }
+
         let asm_ty = match *ty.kind() {
             // `!` is allowed for input but not for output (issue #87802)
             ty::Never if is_input => return None,
             ty::Error(_) => return None,
             ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8),
             ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16),
-            // Somewhat of a hack: fallback in the presence of errors does not actually
-            // fall back to i32, but to ty::Error. For integer inference variables this
-            // means that they don't get any fallback and stay as `{integer}`.
-            // Since compilation can't succeed anyway, it's fine to use this to avoid printing
-            // "cannot use value of type `{integer}`", even though that would absolutely
-            // work due due i32 fallback if the current function had no other errors.
-            ty::Infer(InferTy::IntVar(_)) => {
-                assert!(self.is_tainted_by_errors());
-                Some(InlineAsmType::I32)
-            }
             ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32),
             ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64),
             ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128),
             ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize),
-            ty::Infer(InferTy::FloatVar(_)) => {
-                assert!(self.is_tainted_by_errors());
-                Some(InlineAsmType::F32)
-            }
             ty::Float(FloatTy::F32) => Some(InlineAsmType::F32),
             ty::Float(FloatTy::F64) => Some(InlineAsmType::F64),
             ty::FnPtr(_) => Some(asm_ty_isize),
@@ -191,6 +187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     _ => None,
                 }
             }
+            ty::Infer(_) => unreachable!(),
             _ => None,
         };
         let Some(asm_ty) = asm_ty else {
@@ -204,11 +201,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return None;
         };
 
-        if ty.has_infer_types_or_consts() {
-            assert!(self.is_tainted_by_errors());
-            return None;
-        }
-
         // Check that the type implements Copy. The only case where this can
         // possibly fail is for SIMD types which don't #[derive(Copy)].
         if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, DUMMY_SP) {
diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs
index d8cdc9275f4..b14f3d6de4e 100644
--- a/compiler/rustc_typeck/src/check/method/confirm.rs
+++ b/compiler/rustc_typeck/src/check/method/confirm.rs
@@ -81,11 +81,25 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
         let rcvr_substs = self.fresh_receiver_substs(self_ty, &pick);
         let all_substs = self.instantiate_method_substs(&pick, segment, rcvr_substs);
 
-        debug!("all_substs={:?}", all_substs);
+        debug!("rcvr_substs={rcvr_substs:?}, all_substs={all_substs:?}");
 
         // Create the final signature for the method, replacing late-bound regions.
         let (method_sig, method_predicates) = self.instantiate_method_sig(&pick, all_substs);
 
+        // If there is a `Self: Sized` bound and `Self` is a trait object, it is possible that
+        // something which derefs to `Self` actually implements the trait and the caller
+        // wanted to make a static dispatch on it but forgot to import the trait.
+        // See test `src/test/ui/issue-35976.rs`.
+        //
+        // In that case, we'll error anyway, but we'll also re-run the search with all traits
+        // in scope, and if we find another method which can be used, we'll output an
+        // appropriate hint suggesting to import the trait.
+        let filler_substs = rcvr_substs
+            .extend_to(self.tcx, pick.item.def_id, |def, _| self.tcx.mk_param_from_def(def));
+        let illegal_sized_bound = self.predicates_require_illegal_sized_bound(
+            &self.tcx.predicates_of(pick.item.def_id).instantiate(self.tcx, filler_substs),
+        );
+
         // Unify the (adjusted) self type with what the method expects.
         //
         // SUBTLE: if we want good error messages, because of "guessing" while matching
@@ -106,16 +120,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
         // Make sure nobody calls `drop()` explicitly.
         self.enforce_illegal_method_limitations(&pick);
 
-        // If there is a `Self: Sized` bound and `Self` is a trait object, it is possible that
-        // something which derefs to `Self` actually implements the trait and the caller
-        // wanted to make a static dispatch on it but forgot to import the trait.
-        // See test `src/test/ui/issue-35976.rs`.
-        //
-        // In that case, we'll error anyway, but we'll also re-run the search with all traits
-        // in scope, and if we find another method which can be used, we'll output an
-        // appropriate hint suggesting to import the trait.
-        let illegal_sized_bound = self.predicates_require_illegal_sized_bound(&method_predicates);
-
         // Add any trait/regions obligations specified on the method's type parameters.
         // We won't add these if we encountered an illegal sized bound, so that we can use
         // a custom error in that case.
diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs
index e29f0275bf4..c0b3a23fde4 100644
--- a/compiler/rustc_typeck/src/check/method/mod.rs
+++ b/compiler/rustc_typeck/src/check/method/mod.rs
@@ -20,8 +20,8 @@ use rustc_hir::def_id::DefId;
 use rustc_infer::infer::{self, InferOk};
 use rustc_middle::ty::subst::Subst;
 use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
-use rustc_middle::ty::GenericParamDefKind;
 use rustc_middle::ty::{self, ToPredicate, Ty, TypeVisitable};
+use rustc_middle::ty::{DefIdTree, GenericParamDefKind};
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
 use rustc_trait_selection::traits;
@@ -221,7 +221,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
 
             // We probe again, taking all traits into account (not only those in scope).
-            let candidates = match self.lookup_probe(
+            let mut candidates = match self.lookup_probe(
                 span,
                 segment.ident,
                 self_ty,
@@ -243,6 +243,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .collect(),
                 _ => Vec::new(),
             };
+            candidates.retain(|candidate| *candidate != self.tcx.parent(result.callee.def_id));
 
             return Err(IllegalSizedBound(candidates, needs_mut, span));
         }
diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs
index e9b91414a07..2de22530356 100644
--- a/compiler/rustc_typeck/src/check/method/probe.rs
+++ b/compiler/rustc_typeck/src/check/method/probe.rs
@@ -21,9 +21,7 @@ use rustc_middle::middle::stability;
 use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
 use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
 use rustc_middle::ty::GenericParamDefKind;
-use rustc_middle::ty::{
-    self, EarlyBinder, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable,
-};
+use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable};
 use rustc_session::lint;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::lev_distance::{
@@ -713,7 +711,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             }
 
             let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id);
-            let impl_ty = EarlyBinder(impl_ty).subst(self.tcx, impl_substs);
+            let impl_ty = impl_ty.subst(self.tcx, impl_substs);
 
             debug!("impl_ty: {:?}", impl_ty);
 
@@ -1811,9 +1809,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         self.erase_late_bound_regions(xform_fn_sig)
     }
 
-    /// Gets the type of an impl and generate substitutions with placeholders.
-    fn impl_ty_and_substs(&self, impl_def_id: DefId) -> (Ty<'tcx>, SubstsRef<'tcx>) {
-        (self.tcx.type_of(impl_def_id), self.fresh_item_substs(impl_def_id))
+    /// Gets the type of an impl and generate substitutions with inference vars.
+    fn impl_ty_and_substs(
+        &self,
+        impl_def_id: DefId,
+    ) -> (ty::EarlyBinder<Ty<'tcx>>, SubstsRef<'tcx>) {
+        (self.tcx.bound_type_of(impl_def_id), self.fresh_item_substs(impl_def_id))
     }
 
     fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> {
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs
index f93f567fb20..5621cf2e1a4 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_typeck/src/check/wfcheck.rs
@@ -824,50 +824,62 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
                 }
 
                 if let Some(non_structural_match_ty) =
-                    traits::search_for_structural_match_violation(param.span, tcx, ty)
+                    traits::search_for_structural_match_violation(param.span, tcx, ty, false)
                 {
                     // We use the same error code in both branches, because this is really the same
                     // issue: we just special-case the message for type parameters to make it
                     // clearer.
-                    if let ty::Param(_) = ty.peel_refs().kind() {
-                        // Const parameters may not have type parameters as their types,
-                        // because we cannot be sure that the type parameter derives `PartialEq`
-                        // and `Eq` (just implementing them is not enough for `structural_match`).
-                        struct_span_err!(
-                            tcx.sess,
-                            hir_ty.span,
-                            E0741,
-                            "`{}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \
-                            used as the type of a const parameter",
-                            ty,
-                        )
-                        .span_label(
-                            hir_ty.span,
-                            format!("`{}` may not derive both `PartialEq` and `Eq`", ty),
-                        )
-                        .note(
-                            "it is not currently possible to use a type parameter as the type of a \
-                            const parameter",
-                        )
-                        .emit();
-                    } else {
-                        let mut diag = struct_span_err!(
-                            tcx.sess,
-                            hir_ty.span,
-                            E0741,
-                            "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \
-                            the type of a const parameter",
-                            non_structural_match_ty.ty,
-                        );
-
-                        if ty == non_structural_match_ty.ty {
-                            diag.span_label(
+                    match ty.peel_refs().kind() {
+                        ty::Param(_) => {
+                            // Const parameters may not have type parameters as their types,
+                            // because we cannot be sure that the type parameter derives `PartialEq`
+                            // and `Eq` (just implementing them is not enough for `structural_match`).
+                            struct_span_err!(
+                                tcx.sess,
                                 hir_ty.span,
-                                format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"),
-                            );
+                                E0741,
+                                "`{ty}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \
+                                used as the type of a const parameter",
+                            )
+                            .span_label(
+                                hir_ty.span,
+                                format!("`{ty}` may not derive both `PartialEq` and `Eq`"),
+                            )
+                            .note(
+                                "it is not currently possible to use a type parameter as the type of a \
+                                const parameter",
+                            )
+                            .emit();
+                        }
+                        ty::Float(_) => {
+                            struct_span_err!(
+                                tcx.sess,
+                                hir_ty.span,
+                                E0741,
+                                "`{ty}` is forbidden as the type of a const generic parameter",
+                            )
+                            .note("floats do not derive `Eq` or `Ord`, which are required for const parameters")
+                            .emit();
                         }
+                        _ => {
+                            let mut diag = struct_span_err!(
+                                tcx.sess,
+                                hir_ty.span,
+                                E0741,
+                                "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \
+                                the type of a const parameter",
+                                non_structural_match_ty.ty,
+                            );
 
-                        diag.emit();
+                            if ty == non_structural_match_ty.ty {
+                                diag.span_label(
+                                    hir_ty.span,
+                                    format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"),
+                                );
+                            }
+
+                            diag.emit();
+                        }
                     }
                 }
             } else {
diff --git a/compiler/rustc_typeck/src/outlives/explicit.rs b/compiler/rustc_typeck/src/outlives/explicit.rs
index bbf31de527e..7534482cce9 100644
--- a/compiler/rustc_typeck/src/outlives/explicit.rs
+++ b/compiler/rustc_typeck/src/outlives/explicit.rs
@@ -6,7 +6,7 @@ use super::utils::*;
 
 #[derive(Debug)]
 pub struct ExplicitPredicatesMap<'tcx> {
-    map: FxHashMap<DefId, RequiredPredicates<'tcx>>,
+    map: FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>,
 }
 
 impl<'tcx> ExplicitPredicatesMap<'tcx> {
@@ -14,11 +14,11 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
         ExplicitPredicatesMap { map: FxHashMap::default() }
     }
 
-    pub fn explicit_predicates_of(
+    pub(crate) fn explicit_predicates_of(
         &mut self,
         tcx: TyCtxt<'tcx>,
         def_id: DefId,
-    ) -> &RequiredPredicates<'tcx> {
+    ) -> &ty::EarlyBinder<RequiredPredicates<'tcx>> {
         self.map.entry(def_id).or_insert_with(|| {
             let predicates = if def_id.is_local() {
                 tcx.explicit_predicates_of(def_id)
@@ -63,7 +63,7 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
                 }
             }
 
-            required_predicates
+            ty::EarlyBinder(required_predicates)
         })
     }
 }
diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_typeck/src/outlives/implicit_infer.rs
index 52f9e386441..257a9520eeb 100644
--- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs
+++ b/compiler/rustc_typeck/src/outlives/implicit_infer.rs
@@ -2,7 +2,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
-use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::Span;
 
 use super::explicit::ExplicitPredicatesMap;
@@ -13,20 +13,19 @@ use super::utils::*;
 /// `global_inferred_outlives`: this is initially the empty map that
 ///     was generated by walking the items in the crate. This will
 ///     now be filled with inferred predicates.
-pub fn infer_predicates<'tcx>(
+pub(super) fn infer_predicates<'tcx>(
     tcx: TyCtxt<'tcx>,
-    explicit_map: &mut ExplicitPredicatesMap<'tcx>,
-) -> FxHashMap<DefId, RequiredPredicates<'tcx>> {
+) -> FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>> {
     debug!("infer_predicates");
 
-    let mut predicates_added = true;
+    let mut explicit_map = ExplicitPredicatesMap::new();
 
     let mut global_inferred_outlives = FxHashMap::default();
 
     // If new predicates were added then we need to re-calculate
     // all crates since there could be new implied predicates.
-    while predicates_added {
-        predicates_added = false;
+    'outer: loop {
+        let mut predicates_added = false;
 
         // Visit all the crates and infer predicates
         for id in tcx.hir().items() {
@@ -53,9 +52,9 @@ pub fn infer_predicates<'tcx>(
                             tcx,
                             field_ty,
                             field_span,
-                            &mut global_inferred_outlives,
+                            &global_inferred_outlives,
                             &mut item_required_predicates,
-                            explicit_map,
+                            &mut explicit_map,
                         );
                     }
                 }
@@ -70,12 +69,17 @@ pub fn infer_predicates<'tcx>(
             // we walk the crates again and re-calculate predicates for all
             // items.
             let item_predicates_len: usize =
-                global_inferred_outlives.get(&item_did.to_def_id()).map_or(0, |p| p.len());
+                global_inferred_outlives.get(&item_did.to_def_id()).map_or(0, |p| p.0.len());
             if item_required_predicates.len() > item_predicates_len {
                 predicates_added = true;
-                global_inferred_outlives.insert(item_did.to_def_id(), item_required_predicates);
+                global_inferred_outlives
+                    .insert(item_did.to_def_id(), ty::EarlyBinder(item_required_predicates));
             }
         }
+
+        if !predicates_added {
+            break 'outer;
+        }
     }
 
     global_inferred_outlives
@@ -85,7 +89,7 @@ fn insert_required_predicates_to_be_wf<'tcx>(
     tcx: TyCtxt<'tcx>,
     field_ty: Ty<'tcx>,
     field_span: Span,
-    global_inferred_outlives: &FxHashMap<DefId, RequiredPredicates<'tcx>>,
+    global_inferred_outlives: &FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>,
     required_predicates: &mut RequiredPredicates<'tcx>,
     explicit_map: &mut ExplicitPredicatesMap<'tcx>,
 ) {
@@ -133,11 +137,13 @@ fn insert_required_predicates_to_be_wf<'tcx>(
                 // 'a` holds for `Foo`.
                 debug!("Adt");
                 if let Some(unsubstituted_predicates) = global_inferred_outlives.get(&def.did()) {
-                    for (unsubstituted_predicate, &span) in unsubstituted_predicates {
+                    for (unsubstituted_predicate, &span) in &unsubstituted_predicates.0 {
                         // `unsubstituted_predicate` is `U: 'b` in the
                         // example above.  So apply the substitution to
                         // get `T: 'a` (or `predicate`):
-                        let predicate = EarlyBinder(*unsubstituted_predicate).subst(tcx, substs);
+                        let predicate = unsubstituted_predicates
+                            .rebind(*unsubstituted_predicate)
+                            .subst(tcx, substs);
                         insert_outlives_predicate(
                             tcx,
                             predicate.0,
@@ -224,7 +230,7 @@ fn insert_required_predicates_to_be_wf<'tcx>(
 /// will give us `U: 'static` and `U: Foo`. The latter we
 /// can ignore, but we will want to process `U: 'static`,
 /// applying the substitution as above.
-pub fn check_explicit_predicates<'tcx>(
+fn check_explicit_predicates<'tcx>(
     tcx: TyCtxt<'tcx>,
     def_id: DefId,
     substs: &[GenericArg<'tcx>],
@@ -242,7 +248,7 @@ pub fn check_explicit_predicates<'tcx>(
     );
     let explicit_predicates = explicit_map.explicit_predicates_of(tcx, def_id);
 
-    for (outlives_predicate, &span) in explicit_predicates {
+    for (outlives_predicate, &span) in &explicit_predicates.0 {
         debug!("outlives_predicate = {:?}", &outlives_predicate);
 
         // Careful: If we are inferring the effects of a `dyn Trait<..>`
@@ -287,7 +293,7 @@ pub fn check_explicit_predicates<'tcx>(
             continue;
         }
 
-        let predicate = EarlyBinder(*outlives_predicate).subst(tcx, substs);
+        let predicate = explicit_predicates.rebind(*outlives_predicate).subst(tcx, substs);
         debug!("predicate = {:?}", &predicate);
         insert_outlives_predicate(tcx, predicate.0, predicate.1, span, required_predicates);
     }
diff --git a/compiler/rustc_typeck/src/outlives/mod.rs b/compiler/rustc_typeck/src/outlives/mod.rs
index dccfee19960..8fa65d51e3b 100644
--- a/compiler/rustc_typeck/src/outlives/mod.rs
+++ b/compiler/rustc_typeck/src/outlives/mod.rs
@@ -88,9 +88,7 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> {
     // for the type.
 
     // Compute the inferred predicates
-    let mut exp_map = explicit::ExplicitPredicatesMap::new();
-
-    let global_inferred_outlives = implicit_infer::infer_predicates(tcx, &mut exp_map);
+    let global_inferred_outlives = implicit_infer::infer_predicates(tcx);
 
     // Convert the inferred predicates into the "collected" form the
     // global data structure expects.
@@ -100,7 +98,7 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> {
     let predicates = global_inferred_outlives
         .iter()
         .map(|(&def_id, set)| {
-            let predicates = &*tcx.arena.alloc_from_iter(set.iter().filter_map(
+            let predicates = &*tcx.arena.alloc_from_iter(set.0.iter().filter_map(
                 |(ty::OutlivesPredicate(kind1, region2), &span)| {
                     match kind1.unpack() {
                         GenericArgKind::Type(ty1) => Some((
diff --git a/compiler/rustc_typeck/src/outlives/utils.rs b/compiler/rustc_typeck/src/outlives/utils.rs
index 14e3048cadc..b718ca94213 100644
--- a/compiler/rustc_typeck/src/outlives/utils.rs
+++ b/compiler/rustc_typeck/src/outlives/utils.rs
@@ -7,12 +7,12 @@ use std::collections::BTreeMap;
 
 /// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred
 /// must be added to the struct header.
-pub type RequiredPredicates<'tcx> =
+pub(crate) type RequiredPredicates<'tcx> =
     BTreeMap<ty::OutlivesPredicate<GenericArg<'tcx>, ty::Region<'tcx>>, Span>;
 
 /// Given a requirement `T: 'a` or `'b: 'a`, deduce the
 /// outlives_component and add it to `required_predicates`
-pub fn insert_outlives_predicate<'tcx>(
+pub(crate) fn insert_outlives_predicate<'tcx>(
     tcx: TyCtxt<'tcx>,
     kind: GenericArg<'tcx>,
     outlived_region: Region<'tcx>,
diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
index 265a57c3929..469f7d1172a 100644
--- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
+++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
@@ -812,7 +812,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     /// Builds the `type defined here` message.
     fn show_definition(&self, err: &mut Diagnostic) {
         let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) {
-            if self.tcx.sess.source_map().span_to_snippet(def_span).is_ok() {
+            if self.tcx.sess.source_map().is_span_accessible(def_span) {
                 def_span.into()
             } else {
                 return;
diff --git a/library/std/src/os/windows/ffi.rs b/library/std/src/os/windows/ffi.rs
index a9493a94cac..96bab59d3f8 100644
--- a/library/std/src/os/windows/ffi.rs
+++ b/library/std/src/os/windows/ffi.rs
@@ -129,6 +129,7 @@ pub trait OsStrExt: Sealed {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl OsStrExt for OsStr {
+    #[inline]
     fn encode_wide(&self) -> EncodeWide<'_> {
         self.as_inner().inner.encode_wide()
     }
diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md
index 206bc38efb3..85afc1f5f6c 100644
--- a/src/bootstrap/CHANGELOG.md
+++ b/src/bootstrap/CHANGELOG.md
@@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - The options `infodir`, `localstatedir`, and `gpg-password-file` are no longer allowed in config.toml. Previously, they were ignored without warning. Note that `infodir` and `localstatedir` are still accepted by `./configure`, with a warning. [#82451](https://github.com/rust-lang/rust/pull/82451)
 - Change the names for `dist` commands to match the component they generate. [#90684](https://github.com/rust-lang/rust/pull/90684)
 - The `build.fast-submodules` option has been removed. Fast submodule checkouts are enabled unconditionally. Automatic submodule handling can still be disabled with `build.submodules = false`.
+- Several unsupported `./configure` options have been removed: `optimize`, `parallel-compiler`. These can still be enabled with `--set`, although it isn't recommended.
 
 ### Non-breaking changes
 
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index 4985b054678..9196b78c513 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -20,7 +20,15 @@ fn args(builder: &Builder<'_>) -> Vec<String> {
         arr.iter().copied().map(String::from)
     }
 
-    if let Subcommand::Clippy { fix, .. } = builder.config.cmd {
+    if let Subcommand::Clippy {
+        fix,
+        clippy_lint_allow,
+        clippy_lint_deny,
+        clippy_lint_warn,
+        clippy_lint_forbid,
+        ..
+    } = &builder.config.cmd
+    {
         // disable the most spammy clippy lints
         let ignored_lints = vec![
             "many_single_char_names", // there are a lot in stdarch
@@ -32,7 +40,7 @@ fn args(builder: &Builder<'_>) -> Vec<String> {
             "wrong_self_convention",
         ];
         let mut args = vec![];
-        if fix {
+        if *fix {
             #[rustfmt::skip]
             args.extend(strings(&[
                 "--fix", "-Zunstable-options",
@@ -44,6 +52,12 @@ fn args(builder: &Builder<'_>) -> Vec<String> {
         }
         args.extend(strings(&["--", "--cap-lints", "warn"]));
         args.extend(ignored_lints.iter().map(|lint| format!("-Aclippy::{}", lint)));
+        let mut clippy_lint_levels: Vec<String> = Vec::new();
+        clippy_lint_allow.iter().for_each(|v| clippy_lint_levels.push(format!("-A{}", v)));
+        clippy_lint_deny.iter().for_each(|v| clippy_lint_levels.push(format!("-D{}", v)));
+        clippy_lint_warn.iter().for_each(|v| clippy_lint_levels.push(format!("-W{}", v)));
+        clippy_lint_forbid.iter().for_each(|v| clippy_lint_levels.push(format!("-F{}", v)));
+        args.extend(clippy_lint_levels);
         args
     } else {
         vec![]
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index c099fedc3a7..3d678b2290d 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -25,6 +25,7 @@ use crate::config::{LlvmLibunwind, TargetSelection};
 use crate::dist;
 use crate::native;
 use crate::tool::SourceType;
+use crate::util::get_clang_cl_resource_dir;
 use crate::util::{exe, is_debug_info, is_dylib, output, symlink_dir, t, up_to_date};
 use crate::LLVM_TOOLS;
 use crate::{CLang, Compiler, DependencyType, GitRepo, Mode};
@@ -772,10 +773,38 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS
         if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
             cargo.env("CFG_LLVM_ROOT", s);
         }
-        // Some LLVM linker flags (-L and -l) may be needed to link rustc_llvm.
+
+        // Some LLVM linker flags (-L and -l) may be needed to link `rustc_llvm`. Its build script
+        // expects these to be passed via the `LLVM_LINKER_FLAGS` env variable, separated by
+        // whitespace.
+        //
+        // For example:
+        // - on windows, when `clang-cl` is used with instrumentation, we need to manually add
+        // clang's runtime library resource directory so that the profiler runtime library can be
+        // found. This is to avoid the linker errors about undefined references to
+        // `__llvm_profile_instrument_memop` when linking `rustc_driver`.
+        let mut llvm_linker_flags = String::new();
+        if builder.config.llvm_profile_generate && target.contains("msvc") {
+            if let Some(ref clang_cl_path) = builder.config.llvm_clang_cl {
+                // Add clang's runtime library directory to the search path
+                let clang_rt_dir = get_clang_cl_resource_dir(clang_cl_path);
+                llvm_linker_flags.push_str(&format!("-L{}", clang_rt_dir.display()));
+            }
+        }
+
+        // The config can also specify its own llvm linker flags.
         if let Some(ref s) = builder.config.llvm_ldflags {
-            cargo.env("LLVM_LINKER_FLAGS", s);
+            if !llvm_linker_flags.is_empty() {
+                llvm_linker_flags.push_str(" ");
+            }
+            llvm_linker_flags.push_str(s);
+        }
+
+        // Set the linker flags via the env var that `rustc_llvm`'s build script will read.
+        if !llvm_linker_flags.is_empty() {
+            cargo.env("LLVM_LINKER_FLAGS", llvm_linker_flags);
         }
+
         // Building with a static libstdc++ is only supported on linux right now,
         // not for MSVC or macOS
         if builder.config.llvm_static_stdcpp
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index 2fc036082cb..6b139decb55 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -31,11 +31,10 @@ def v(*args):
     options.append(Option(*args, value=True))
 
 
-o("debug", "rust.debug", "enables debugging environment; does not affect optimization of bootstrapped code (use `--disable-optimize` for that)")
+o("debug", "rust.debug", "enables debugging environment; does not affect optimization of bootstrapped code")
 o("docs", "build.docs", "build standard library documentation")
 o("compiler-docs", "build.compiler-docs", "build compiler documentation")
 o("optimize-tests", "rust.optimize-tests", "build tests with optimizations")
-o("parallel-compiler", "rust.parallel-compiler", "build a multi-threaded rustc")
 o("verbose-tests", "rust.verbose-tests", "enable verbose output when running tests")
 o("ccache", "llvm.ccache", "invoke gcc/clang via ccache to reuse object files between builds")
 o("sccache", None, "invoke gcc/clang via sccache to reuse object files between builds")
@@ -70,7 +69,6 @@ v("llvm-libunwind", "rust.llvm-libunwind", "use LLVM libunwind")
 
 # Optimization and debugging options. These may be overridden by the release
 # channel, etc.
-o("optimize", "rust.optimize", "build optimized rust code")
 o("optimize-llvm", "llvm.optimize", "build optimized LLVM")
 o("llvm-assertions", "llvm.assertions", "build LLVM with assertions")
 o("llvm-plugins", "llvm.plugins", "build LLVM with plugin interface")
@@ -161,7 +159,7 @@ v("default-linker", "rust.default-linker", "the default linker")
 # Many of these are saved below during the "writing configuration" step
 # (others are conditionally saved).
 o("manage-submodules", "build.submodules", "let the build manage the git submodules")
-o("full-bootstrap", "build.full-bootstrap", "build three compilers instead of two")
+o("full-bootstrap", "build.full-bootstrap", "build three compilers instead of two (not recommended except for testing reproducible builds)")
 o("extended", "build.extended", "build an extended rust tool set")
 
 v("tools", None, "List of extended tools will be installed")
@@ -493,4 +491,3 @@ with bootstrap.output('Makefile') as f:
 
 p("")
 p("run `python {}/x.py --help`".format(rust_dir))
-p("")
diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs
index eec19ab4fc9..1822c2936b7 100644
--- a/src/bootstrap/flags.rs
+++ b/src/bootstrap/flags.rs
@@ -91,6 +91,10 @@ pub enum Subcommand {
     Clippy {
         fix: bool,
         paths: Vec<PathBuf>,
+        clippy_lint_allow: Vec<String>,
+        clippy_lint_deny: Vec<String>,
+        clippy_lint_warn: Vec<String>,
+        clippy_lint_forbid: Vec<String>,
     },
     Fix {
         paths: Vec<PathBuf>,
@@ -246,6 +250,10 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
         opts.optopt("", "rust-profile-use", "use PGO profile for rustc build", "PROFILE");
         opts.optflag("", "llvm-profile-generate", "generate PGO profile with llvm built for rustc");
         opts.optopt("", "llvm-profile-use", "use PGO profile for llvm build", "PROFILE");
+        opts.optmulti("A", "", "allow certain clippy lints", "OPT");
+        opts.optmulti("D", "", "deny certain clippy lints", "OPT");
+        opts.optmulti("W", "", "warn about certain clippy lints", "OPT");
+        opts.optmulti("F", "", "forbid certain clippy lints", "OPT");
 
         // We can't use getopt to parse the options until we have completed specifying which
         // options are valid, but under the current implementation, some options are conditional on
@@ -544,7 +552,14 @@ Arguments:
                 }
                 Subcommand::Check { paths }
             }
-            Kind::Clippy => Subcommand::Clippy { paths, fix: matches.opt_present("fix") },
+            Kind::Clippy => Subcommand::Clippy {
+                paths,
+                fix: matches.opt_present("fix"),
+                clippy_lint_allow: matches.opt_strs("A"),
+                clippy_lint_warn: matches.opt_strs("W"),
+                clippy_lint_deny: matches.opt_strs("D"),
+                clippy_lint_forbid: matches.opt_strs("F"),
+            },
             Kind::Fix => Subcommand::Fix { paths },
             Kind::Test => Subcommand::Test {
                 paths,
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index 01ba0169b20..01e70b3fb2d 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -18,6 +18,7 @@ use std::process::Command;
 
 use crate::builder::{Builder, RunConfig, ShouldRun, Step};
 use crate::config::TargetSelection;
+use crate::util::get_clang_cl_resource_dir;
 use crate::util::{self, exe, output, program_out_of_date, t, up_to_date};
 use crate::{CLang, GitRepo};
 
@@ -776,7 +777,22 @@ impl Step for Lld {
         t!(fs::create_dir_all(&out_dir));
 
         let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/lld"));
-        configure_cmake(builder, target, &mut cfg, true, LdFlags::default());
+        let mut ldflags = LdFlags::default();
+
+        // When building LLD as part of a build with instrumentation on windows, for example
+        // when doing PGO on CI, cmake or clang-cl don't automatically link clang's
+        // profiler runtime in. In that case, we need to manually ask cmake to do it, to avoid
+        // linking errors, much like LLVM's cmake setup does in that situation.
+        if builder.config.llvm_profile_generate && target.contains("msvc") {
+            if let Some(clang_cl_path) = builder.config.llvm_clang_cl.as_ref() {
+                // Find clang's runtime library directory and push that as a search path to the
+                // cmake linker flags.
+                let clang_rt_dir = get_clang_cl_resource_dir(clang_cl_path);
+                ldflags.push_all(&format!("/libpath:{}", clang_rt_dir.display()));
+            }
+        }
+
+        configure_cmake(builder, target, &mut cfg, true, ldflags);
 
         // This is an awful, awful hack. Discovered when we migrated to using
         // clang-cl to compile LLVM/LLD it turns out that LLD, when built out of
diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs
index b627e503789..c5d62a8810a 100644
--- a/src/bootstrap/util.rs
+++ b/src/bootstrap/util.rs
@@ -576,3 +576,27 @@ fn absolute_windows(path: &std::path::Path) -> std::io::Result<std::path::PathBu
         }
     }
 }
+
+/// Adapted from https://github.com/llvm/llvm-project/blob/782e91224601e461c019e0a4573bbccc6094fbcd/llvm/cmake/modules/HandleLLVMOptions.cmake#L1058-L1079
+///
+/// When `clang-cl` is used with instrumentation, we need to add clang's runtime library resource
+/// directory to the linker flags, otherwise there will be linker errors about the profiler runtime
+/// missing. This function returns the path to that directory.
+pub fn get_clang_cl_resource_dir(clang_cl_path: &str) -> PathBuf {
+    // Similar to how LLVM does it, to find clang's library runtime directory:
+    // - we ask `clang-cl` to locate the `clang_rt.builtins` lib.
+    let mut builtins_locator = Command::new(clang_cl_path);
+    builtins_locator.args(&["/clang:-print-libgcc-file-name", "/clang:--rtlib=compiler-rt"]);
+
+    let clang_rt_builtins = output(&mut builtins_locator);
+    let clang_rt_builtins = Path::new(clang_rt_builtins.trim());
+    assert!(
+        clang_rt_builtins.exists(),
+        "`clang-cl` must correctly locate the library runtime directory"
+    );
+
+    // - the profiler runtime will be located in the same directory as the builtins lib, like
+    // `$LLVM_DISTRO_ROOT/lib/clang/$LLVM_VERSION/lib/windows`.
+    let clang_rt_dir = clang_rt_builtins.parent().expect("The clang lib folder should exist");
+    clang_rt_dir.to_path_buf()
+}
diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh
index 495c539d069..1025f5bce80 100755
--- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh
+++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-clang.sh
@@ -4,7 +4,7 @@ set -ex
 
 source shared.sh
 
-LLVM=llvmorg-14.0.2
+LLVM=llvmorg-14.0.5
 
 mkdir llvm-project
 cd llvm-project
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile
index 7651947c525..13d440423b2 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile
@@ -35,7 +35,6 @@ ENV RUST_CONFIGURE_ARGS \
       --build=x86_64-unknown-linux-gnu \
       --enable-debug \
       --enable-lld \
-      --enable-optimize \
       --set llvm.use-linker=lld \
       --set target.x86_64-unknown-linux-gnu.linker=clang \
       --set target.x86_64-unknown-linux-gnu.cc=clang \
diff --git a/src/ci/docker/scripts/musl.sh b/src/ci/docker/scripts/musl.sh
index 65e15950559..3e5dc4af04a 100644
--- a/src/ci/docker/scripts/musl.sh
+++ b/src/ci/docker/scripts/musl.sh
@@ -33,7 +33,7 @@ if [ ! -d $MUSL ]; then
 fi
 
 cd $MUSL
-./configure --enable-optimize --enable-debug --disable-shared --prefix=/musl-$TAG "$@"
+./configure --enable-debug --disable-shared --prefix=/musl-$TAG "$@"
 if [ "$TAG" = "i586" -o "$TAG" = "i686" ]; then
   hide_output make -j$(nproc) AR=ar RANLIB=ranlib
 else
diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml
index 57832ac2b95..f92e46b0a97 100644
--- a/src/ci/github-actions/ci.yml
+++ b/src/ci/github-actions/ci.yml
@@ -625,7 +625,7 @@ jobs:
                 --target=x86_64-pc-windows-msvc
                 --enable-full-tools
                 --enable-profiler
-              SCRIPT: python x.py dist
+              SCRIPT: PGO_HOST=x86_64-pc-windows-msvc src/ci/pgo.sh python x.py dist
               DIST_REQUIRE_ALL_TOOLS: 1
             <<: *job-windows-xl
 
diff --git a/src/ci/pgo.sh b/src/ci/pgo.sh
index 9de970c9c2a..28bed1fa035 100755
--- a/src/ci/pgo.sh
+++ b/src/ci/pgo.sh
@@ -3,44 +3,82 @@
 
 set -euxo pipefail
 
+ci_dir=`cd $(dirname $0) && pwd`
+source "$ci_dir/shared.sh"
+
+# The root checkout, where the source is located
+CHECKOUT=/checkout
+
+DOWNLOADED_LLVM=/rustroot
+
+# The main directory where the build occurs, which can be different between linux and windows
+BUILD_ROOT=$CHECKOUT/obj
+
+if isWindows; then
+    CHECKOUT=$(pwd)
+    DOWNLOADED_LLVM=$CHECKOUT/citools/clang-rust
+    BUILD_ROOT=$CHECKOUT
+fi
+
+# The various build artifacts used in other commands: to launch rustc builds, build the perf
+# collector, and run benchmarks to gather profiling data
+BUILD_ARTIFACTS=$BUILD_ROOT/build/$PGO_HOST
+RUSTC_STAGE_0=$BUILD_ARTIFACTS/stage0/bin/rustc
+CARGO_STAGE_0=$BUILD_ARTIFACTS/stage0/bin/cargo
+RUSTC_STAGE_2=$BUILD_ARTIFACTS/stage2/bin/rustc
+
+# Windows needs these to have the .exe extension
+if isWindows; then
+    RUSTC_STAGE_0="${RUSTC_STAGE_0}.exe"
+    CARGO_STAGE_0="${CARGO_STAGE_0}.exe"
+    RUSTC_STAGE_2="${RUSTC_STAGE_2}.exe"
+fi
+
+# Make sure we have a temporary PGO work folder
+PGO_TMP=/tmp/tmp-pgo
+mkdir -p $PGO_TMP
+rm -rf $PGO_TMP/*
+
+RUSTC_PERF=$PGO_TMP/rustc-perf
+
 # Compile several crates to gather execution PGO profiles.
 # Arg0 => profiles (Debug, Opt)
 # Arg1 => scenarios (Full, IncrFull, All)
 # Arg2 => crates (syn, cargo, ...)
 gather_profiles () {
-  cd /checkout/obj
+  cd $BUILD_ROOT
 
   # Compile libcore, both in opt-level=0 and opt-level=3
-  RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc \
-      --edition=2021 --crate-type=lib ../library/core/src/lib.rs
-  RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc \
-      --edition=2021 --crate-type=lib -Copt-level=3 ../library/core/src/lib.rs
+  RUSTC_BOOTSTRAP=1 $RUSTC_STAGE_2 \
+      --edition=2021 --crate-type=lib $CHECKOUT/library/core/src/lib.rs \
+      --out-dir $PGO_TMP
+  RUSTC_BOOTSTRAP=1 $RUSTC_STAGE_2 \
+      --edition=2021 --crate-type=lib -Copt-level=3 $CHECKOUT/library/core/src/lib.rs \
+      --out-dir $PGO_TMP
 
-  cd rustc-perf
+  cd $RUSTC_PERF
 
   # Run rustc-perf benchmarks
   # Benchmark using profile_local with eprintln, which essentially just means
   # don't actually benchmark -- just make sure we run rustc a bunch of times.
   RUST_LOG=collector=debug \
-  RUSTC=/checkout/obj/build/$PGO_HOST/stage0/bin/rustc \
+  RUSTC=$RUSTC_STAGE_0 \
   RUSTC_BOOTSTRAP=1 \
-  /checkout/obj/build/$PGO_HOST/stage0/bin/cargo run -p collector --bin collector -- \
-          profile_local \
-          eprintln \
-          /checkout/obj/build/$PGO_HOST/stage2/bin/rustc \
-          --id Test \
-          --profiles $1 \
-          --cargo /checkout/obj/build/$PGO_HOST/stage0/bin/cargo \
-          --scenarios $2 \
-          --include $3
-
-  cd /checkout/obj
+  $CARGO_STAGE_0 run -p collector --bin collector -- \
+      profile_local \
+      eprintln \
+      $RUSTC_STAGE_2 \
+      --id Test \
+      --profiles $1 \
+      --cargo $CARGO_STAGE_0 \
+      --scenarios $2 \
+      --include $3
+
+  cd $BUILD_ROOT
 }
 
-rm -rf /tmp/rustc-pgo
-
 # This path has to be absolute
-LLVM_PROFILE_DIRECTORY_ROOT=/tmp/llvm-pgo
+LLVM_PROFILE_DIRECTORY_ROOT=$PGO_TMP/llvm-pgo
 
 # We collect LLVM profiling information and rustc profiling information in
 # separate phases. This increases build time -- though not by a huge amount --
@@ -49,34 +87,47 @@ LLVM_PROFILE_DIRECTORY_ROOT=/tmp/llvm-pgo
 # LLVM IR PGO does not respect LLVM_PROFILE_FILE, so we have to set the profiling file
 # path through our custom environment variable. We include the PID in the directory path
 # to avoid updates to profile files being lost because of race conditions.
-LLVM_PROFILE_DIR=${LLVM_PROFILE_DIRECTORY_ROOT}/prof-%p python3 ../x.py build \
+LLVM_PROFILE_DIR=${LLVM_PROFILE_DIRECTORY_ROOT}/prof-%p python3 $CHECKOUT/x.py build \
     --target=$PGO_HOST \
     --host=$PGO_HOST \
     --stage 2 library/std \
     --llvm-profile-generate
 
-# Compile rustc perf
-cp -r /tmp/rustc-perf ./
-chown -R $(whoami): ./rustc-perf
-cd rustc-perf
-
-# Build the collector ahead of time, which is needed to make sure the rustc-fake
-# binary used by the collector is present.
-RUSTC=/checkout/obj/build/$PGO_HOST/stage0/bin/rustc \
+# Compile rustc-perf:
+# - get the expected commit source code: on linux, the Dockerfile downloads a source archive before
+# running this script. On Windows, we do that here.
+if isLinux; then
+    cp -r /tmp/rustc-perf $RUSTC_PERF
+    chown -R $(whoami): $RUSTC_PERF
+else
+    # rustc-perf version from 2022-05-18
+    PERF_COMMIT=f66cc8f3e04392b0e2fd811f21fd1ece6ebaded3
+    retry curl -LS -o $PGO_TMP/perf.zip \
+        https://github.com/rust-lang/rustc-perf/archive/$PERF_COMMIT.zip && \
+        cd $PGO_TMP && unzip -q perf.zip && \
+        mv rustc-perf-$PERF_COMMIT $RUSTC_PERF && \
+        rm perf.zip
+fi
+
+# - build rustc-perf's collector ahead of time, which is needed to make sure the rustc-fake binary
+# used by the collector is present.
+cd $RUSTC_PERF
+
+RUSTC=$RUSTC_STAGE_0 \
 RUSTC_BOOTSTRAP=1 \
-/checkout/obj/build/$PGO_HOST/stage0/bin/cargo build -p collector
+$CARGO_STAGE_0 build -p collector
 
 # Here we're profiling LLVM, so we only care about `Debug` and `Opt`, because we want to stress
 # codegen. We also profile some of the most prolific crates.
 gather_profiles "Debug,Opt" "Full" \
-"syn-1.0.89,cargo-0.60.0,serde-1.0.136,ripgrep-13.0.0,regex-1.5.5,clap-3.1.6,hyper-0.14.18"
+    "syn-1.0.89,cargo-0.60.0,serde-1.0.136,ripgrep-13.0.0,regex-1.5.5,clap-3.1.6,hyper-0.14.18"
 
-LLVM_PROFILE_MERGED_FILE=/tmp/llvm-pgo.profdata
+LLVM_PROFILE_MERGED_FILE=$PGO_TMP/llvm-pgo.profdata
 
 # Merge the profile data we gathered for LLVM
 # Note that this uses the profdata from the clang we used to build LLVM,
 # which likely has a different version than our in-tree clang.
-/rustroot/bin/llvm-profdata merge -o ${LLVM_PROFILE_MERGED_FILE} ${LLVM_PROFILE_DIRECTORY_ROOT}
+$DOWNLOADED_LLVM/bin/llvm-profdata merge -o ${LLVM_PROFILE_MERGED_FILE} ${LLVM_PROFILE_DIRECTORY_ROOT}
 
 echo "LLVM PGO statistics"
 du -sh ${LLVM_PROFILE_MERGED_FILE}
@@ -84,34 +135,45 @@ du -sh ${LLVM_PROFILE_DIRECTORY_ROOT}
 echo "Profile file count"
 find ${LLVM_PROFILE_DIRECTORY_ROOT} -type f | wc -l
 
+# We don't need the individual .profraw files now that they have been merged into a final .profdata
+rm -r $LLVM_PROFILE_DIRECTORY_ROOT
+
 # Rustbuild currently doesn't support rebuilding LLVM when PGO options
 # change (or any other llvm-related options); so just clear out the relevant
 # directories ourselves.
-rm -r ./build/$PGO_HOST/llvm ./build/$PGO_HOST/lld
+rm -r $BUILD_ARTIFACTS/llvm $BUILD_ARTIFACTS/lld
 
 # Okay, LLVM profiling is done, switch to rustc PGO.
 
 # The path has to be absolute
-RUSTC_PROFILE_DIRECTORY_ROOT=/tmp/rustc-pgo
+RUSTC_PROFILE_DIRECTORY_ROOT=$PGO_TMP/rustc-pgo
 
-python3 ../x.py build --target=$PGO_HOST --host=$PGO_HOST \
+python3 $CHECKOUT/x.py build --target=$PGO_HOST --host=$PGO_HOST \
     --stage 2 library/std \
     --rust-profile-generate=${RUSTC_PROFILE_DIRECTORY_ROOT}
 
 # Here we're profiling the `rustc` frontend, so we also include `Check`.
 # The benchmark set includes various stress tests that put the frontend under pressure.
-# The profile data is written into a single filepath that is being repeatedly merged when each
-# rustc invocation ends. Empirically, this can result in some profiling data being lost.
-# That's why we override the profile path to include the PID. This will produce many more profiling
-# files, but the resulting profile will produce a slightly faster rustc binary.
-LLVM_PROFILE_FILE=${RUSTC_PROFILE_DIRECTORY_ROOT}/default_%m_%p.profraw gather_profiles \
-  "Check,Debug,Opt" "All" \
-  "externs,ctfe-stress-5,cargo-0.60.0,token-stream-stress,match-stress,tuple-stress,diesel-1.4.8,bitmaps-3.1.0"
-
-RUSTC_PROFILE_MERGED_FILE=/tmp/rustc-pgo.profdata
+if isLinux; then
+    # The profile data is written into a single filepath that is being repeatedly merged when each
+    # rustc invocation ends. Empirically, this can result in some profiling data being lost. That's
+    # why we override the profile path to include the PID. This will produce many more profiling
+    # files, but the resulting profile will produce a slightly faster rustc binary.
+    LLVM_PROFILE_FILE=${RUSTC_PROFILE_DIRECTORY_ROOT}/default_%m_%p.profraw gather_profiles \
+        "Check,Debug,Opt" "All" \
+        "externs,ctfe-stress-5,cargo-0.60.0,token-stream-stress,match-stress,tuple-stress,diesel-1.4.8,bitmaps-3.1.0"
+else
+    # On windows, we don't do that yet (because it generates a lot of data, hitting disk space
+    # limits on the builder), and use the default profraw merging behavior.
+    gather_profiles \
+        "Check,Debug,Opt" "All" \
+        "externs,ctfe-stress-5,cargo-0.60.0,token-stream-stress,match-stress,tuple-stress,diesel-1.4.8,bitmaps-3.1.0"
+fi
+
+RUSTC_PROFILE_MERGED_FILE=$PGO_TMP/rustc-pgo.profdata
 
 # Merge the profile data we gathered
-./build/$PGO_HOST/llvm/bin/llvm-profdata \
+$BUILD_ARTIFACTS/llvm/bin/llvm-profdata \
     merge -o ${RUSTC_PROFILE_MERGED_FILE} ${RUSTC_PROFILE_DIRECTORY_ROOT}
 
 echo "Rustc PGO statistics"
@@ -120,10 +182,13 @@ du -sh ${RUSTC_PROFILE_DIRECTORY_ROOT}
 echo "Profile file count"
 find ${RUSTC_PROFILE_DIRECTORY_ROOT} -type f | wc -l
 
+# We don't need the individual .profraw files now that they have been merged into a final .profdata
+rm -r $RUSTC_PROFILE_DIRECTORY_ROOT
+
 # Rustbuild currently doesn't support rebuilding LLVM when PGO options
 # change (or any other llvm-related options); so just clear out the relevant
 # directories ourselves.
-rm -r ./build/$PGO_HOST/llvm ./build/$PGO_HOST/lld
+rm -r $BUILD_ARTIFACTS/llvm $BUILD_ARTIFACTS/lld
 
 # This produces the actual final set of artifacts, using both the LLVM and rustc
 # collected profiling data.
diff --git a/src/ci/run.sh b/src/ci/run.sh
index b0314047c07..16d8bdb8153 100755
--- a/src/ci/run.sh
+++ b/src/ci/run.sh
@@ -132,7 +132,7 @@ trap datecheck EXIT
 SCCACHE_IDLE_TIMEOUT=10800 sccache --start-server || true
 
 if [ "$RUN_CHECK_WITH_PARALLEL_QUERIES" != "" ]; then
-  $SRC/configure --enable-parallel-compiler
+  $SRC/configure --set rust.parallel-compiler
   CARGO_INCREMENTAL=0 $PYTHON ../x.py check
   rm -f config.toml
   rm -rf build
diff --git a/src/ci/scripts/install-clang.sh b/src/ci/scripts/install-clang.sh
index 48eb88c9f92..0bc8a0389a8 100755
--- a/src/ci/scripts/install-clang.sh
+++ b/src/ci/scripts/install-clang.sh
@@ -1,4 +1,5 @@
 #!/bin/bash
+# ignore-tidy-linelength
 # This script installs clang on the local machine. Note that we don't install
 # clang on Linux since its compiler story is just so different. Each container
 # has its own toolchain configured appropriately already.
@@ -9,7 +10,7 @@ IFS=$'\n\t'
 source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
 
 # Update both macOS's and Windows's tarballs when bumping the version here.
-LLVM_VERSION="14.0.2"
+LLVM_VERSION="14.0.5"
 
 if isMacOS; then
     # If the job selects a specific Xcode version, use that instead of
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 272188f8299..1f30c7006f5 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -329,6 +329,17 @@ impl Options {
             return Err(0);
         }
 
+        let z_flags = matches.opt_strs("Z");
+        if z_flags.iter().any(|x| *x == "help") {
+            print_flag_list("-Z", config::DB_OPTIONS);
+            return Err(0);
+        }
+        let c_flags = matches.opt_strs("C");
+        if c_flags.iter().any(|x| *x == "help") {
+            print_flag_list("-C", config::CG_OPTIONS);
+            return Err(0);
+        }
+
         let color = config::parse_color(matches);
         let config::JsonConfig { json_rendered, json_unused_externs, .. } =
             config::parse_json(matches);
@@ -343,17 +354,6 @@ impl Options {
         // check for deprecated options
         check_deprecated_options(matches, &diag);
 
-        let z_flags = matches.opt_strs("Z");
-        if z_flags.iter().any(|x| *x == "help") {
-            print_flag_list("-Z", config::DB_OPTIONS);
-            return Err(0);
-        }
-        let c_flags = matches.opt_strs("C");
-        if c_flags.iter().any(|x| *x == "help") {
-            print_flag_list("-C", config::CG_OPTIONS);
-            return Err(0);
-        }
-
         if matches.opt_strs("passes") == ["list"] {
             println!("Available passes for running rustdoc:");
             for pass in passes::PASSES {
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 509c4253f0f..39ec6a60856 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -726,31 +726,58 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool {
         // Empty content so nothing to check in here...
         return true;
     }
-    rustc_span::create_session_if_not_set_then(edition, |_| {
-        let filename = FileName::anon_source_code(source);
-        let sess = ParseSess::with_silent_emitter(None);
-        let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source.to_owned())
-        {
-            Ok(p) => p,
-            Err(_) => {
-                debug!("Cannot build a parser to check mod attr so skipping...");
-                return true;
+    rustc_driver::catch_fatal_errors(|| {
+        rustc_span::create_session_if_not_set_then(edition, |_| {
+            use rustc_errors::emitter::EmitterWriter;
+            use rustc_errors::Handler;
+            use rustc_span::source_map::FilePathMapping;
+
+            let filename = FileName::anon_source_code(source);
+            // Any errors in parsing should also appear when the doctest is compiled for real, so just
+            // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr.
+            let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+            let fallback_bundle =
+                rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+
+            let emitter = EmitterWriter::new(
+                box io::sink(),
+                None,
+                None,
+                fallback_bundle,
+                false,
+                false,
+                false,
+                None,
+                false,
+            );
+
+            let handler = Handler::with_emitter(false, None, box emitter);
+            let sess = ParseSess::with_span_handler(handler, sm);
+            let mut parser =
+                match maybe_new_parser_from_source_str(&sess, filename, source.to_owned()) {
+                    Ok(p) => p,
+                    Err(_) => {
+                        debug!("Cannot build a parser to check mod attr so skipping...");
+                        return true;
+                    }
+                };
+            // If a parsing error happened, it's very likely that the attribute is incomplete.
+            if let Err(e) = parser.parse_attribute(InnerAttrPolicy::Permitted) {
+                e.cancel();
+                return false;
             }
-        };
-        // If a parsing error happened, it's very likely that the attribute is incomplete.
-        if parser.parse_attribute(InnerAttrPolicy::Permitted).is_err() {
-            return false;
-        }
-        // We now check if there is an unclosed delimiter for the attribute. To do so, we look at
-        // the `unclosed_delims` and see if the opening square bracket was closed.
-        parser
-            .unclosed_delims()
-            .get(0)
-            .map(|unclosed| {
-                unclosed.unclosed_span.map(|s| s.lo()).unwrap_or(BytePos(0)) != BytePos(2)
-            })
-            .unwrap_or(true)
+            // We now check if there is an unclosed delimiter for the attribute. To do so, we look at
+            // the `unclosed_delims` and see if the opening square bracket was closed.
+            parser
+                .unclosed_delims()
+                .get(0)
+                .map(|unclosed| {
+                    unclosed.unclosed_span.map(|s| s.lo()).unwrap_or(BytePos(0)) != BytePos(2)
+                })
+                .unwrap_or(true)
+        })
     })
+    .unwrap_or(false)
 }
 
 fn partition_source(s: &str, edition: Edition) -> (String, String, String) {
diff --git a/src/test/run-make-fulldeps/target-specs/Makefile b/src/test/run-make-fulldeps/target-specs/Makefile
index dff7a966c96..fb95ee5539a 100644
--- a/src/test/run-make-fulldeps/target-specs/Makefile
+++ b/src/test/run-make-fulldeps/target-specs/Makefile
@@ -8,4 +8,4 @@ all:
 	RUST_TARGET_PATH=. $(RUSTC) foo.rs --target=my-x86_64-unknown-linux-gnu-platform --crate-type=lib --emit=asm
 	$(RUSTC) -Z unstable-options --target=my-awesome-platform.json --print target-spec-json > $(TMPDIR)/test-platform.json && $(RUSTC) -Z unstable-options --target=$(TMPDIR)/test-platform.json --print target-spec-json | diff -q $(TMPDIR)/test-platform.json -
 	$(RUSTC) foo.rs --target=definitely-not-builtin-target 2>&1 | $(CGREP) 'may not set is_builtin'
-	$(RUSTC) foo.rs --target=mismatching-data-layout
+	$(RUSTC) foo.rs --target=mismatching-data-layout --crate-type=lib
diff --git a/src/test/rustdoc-ui/c-help.rs b/src/test/rustdoc-ui/c-help.rs
new file mode 100644
index 00000000000..e166edf8b61
--- /dev/null
+++ b/src/test/rustdoc-ui/c-help.rs
@@ -0,0 +1,6 @@
+// check-pass
+// compile-flags: -Chelp
+// check-stdout
+// regex-error-pattern: -C\s+incremental
+
+pub struct Foo;
diff --git a/src/test/rustdoc-ui/c-help.stdout b/src/test/rustdoc-ui/c-help.stdout
new file mode 100644
index 00000000000..75b2e2a2a43
--- /dev/null
+++ b/src/test/rustdoc-ui/c-help.stdout
@@ -0,0 +1,51 @@
+    -C                       ar=val -- this option is deprecated and does nothing
+    -C               code-model=val -- choose the code model to use (`rustc --print code-models` for details)
+    -C            codegen-units=val -- divide crate into N units to optimize in parallel
+    -C       control-flow-guard=val -- use Windows Control Flow Guard (default: no)
+    -C         debug-assertions=val -- explicitly enable the `cfg(debug_assertions)` directive
+    -C                debuginfo=val -- debug info emission level (0 = no debug info, 1 = line tables only, 2 = full debug info with variable and type information; default: 0)
+    -C default-linker-libraries=val -- allow the linker to link its default libraries (default: no)
+    -C            embed-bitcode=val -- emit bitcode in rlibs (default: yes)
+    -C           extra-filename=val -- extra data to put in each output filename
+    -C     force-frame-pointers=val -- force use of the frame pointers
+    -C      force-unwind-tables=val -- force use of unwind tables
+    -C              incremental=val -- enable incremental compilation
+    -C         inline-threshold=val -- set the threshold for inlining a function
+    -C      instrument-coverage=val -- instrument the generated code to support LLVM source-based code coverage reports (note, the compiler build config must include `profiler = true`); implies `-C symbol-mangling-version=v0`. Optional values are:
+        `=all` (implicit value)
+        `=except-unused-generics`
+        `=except-unused-functions`
+        `=off` (default)
+    -C                 link-arg=val -- a single extra argument to append to the linker invocation (can be used several times)
+    -C                link-args=val -- extra arguments to append to the linker invocation (space separated)
+    -C           link-dead-code=val -- keep dead code at link time (useful for code coverage) (default: no)
+    -C      link-self-contained=val -- control whether to link Rust provided C objects/libraries or rely
+        on C toolchain installed in the system
+    -C                   linker=val -- system linker to link outputs with
+    -C            linker-flavor=val -- linker flavor
+    -C        linker-plugin-lto=val -- generate build artifacts that are compatible with linker-based LTO
+    -C                llvm-args=val -- a list of arguments to pass to LLVM (space separated)
+    -C                      lto=val -- perform LLVM link-time optimizations
+    -C                 metadata=val -- metadata to mangle symbol names with
+    -C    no-prepopulate-passes=val -- give an empty list of passes to the pass manager
+    -C               no-redzone=val -- disable the use of the redzone
+    -C           no-stack-check=val -- this option is deprecated and does nothing
+    -C       no-vectorize-loops=val -- disable loop vectorization optimization passes
+    -C         no-vectorize-slp=val -- disable LLVM's SLP vectorization pass
+    -C                opt-level=val -- optimization level (0-3, s, or z; default: 0)
+    -C          overflow-checks=val -- use overflow checks for integer arithmetic
+    -C                    panic=val -- panic strategy to compile crate with
+    -C                   passes=val -- a list of extra LLVM passes to run (space separated)
+    -C           prefer-dynamic=val -- prefer dynamic linking to static linking (default: no)
+    -C         profile-generate=val -- compile the program with profiling instrumentation
+    -C              profile-use=val -- use the given `.profdata` file for profile-guided optimization
+    -C         relocation-model=val -- control generation of position-independent code (PIC) (`rustc --print relocation-models` for details)
+    -C                   remark=val -- print remarks for these optimization passes (space separated, or "all")
+    -C                    rpath=val -- set rpath values in libs/exes (default: no)
+    -C               save-temps=val -- save all temporary output files during compilation (default: no)
+    -C               soft-float=val -- use soft float ABI (*eabihf targets only) (default: no)
+    -C          split-debuginfo=val -- how to handle split-debuginfo, a platform-specific option
+    -C                    strip=val -- tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)
+    -C  symbol-mangling-version=val -- which mangling version to use for symbol names ('legacy' (default) or 'v0')
+    -C               target-cpu=val -- select target processor (`rustc --print target-cpus` for details)
+    -C           target-feature=val -- target specific attributes. (`rustc --print target-features` for details). This feature is unsafe.
diff --git a/src/test/rustdoc-ui/doctest-multiline-crate-attribute.rs b/src/test/rustdoc-ui/doctest-multiline-crate-attribute.rs
new file mode 100644
index 00000000000..a30472ac56b
--- /dev/null
+++ b/src/test/rustdoc-ui/doctest-multiline-crate-attribute.rs
@@ -0,0 +1,10 @@
+// compile-flags:--test --test-args=--test-threads=1
+// normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR"
+// normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME"
+// check-pass
+
+/// ```
+/// #![deprecated(since = "5.2", note = "foo was rarely used. \
+///    Users should instead use bar")]
+/// ```
+pub fn f() {}
diff --git a/src/test/rustdoc-ui/doctest-multiline-crate-attribute.stdout b/src/test/rustdoc-ui/doctest-multiline-crate-attribute.stdout
new file mode 100644
index 00000000000..07a4f657dea
--- /dev/null
+++ b/src/test/rustdoc-ui/doctest-multiline-crate-attribute.stdout
@@ -0,0 +1,6 @@
+
+running 1 test
+test $DIR/doctest-multiline-crate-attribute.rs - f (line 6) ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
+
diff --git a/src/test/rustdoc-ui/z-help.rs b/src/test/rustdoc-ui/z-help.rs
new file mode 100644
index 00000000000..c7cf841b937
--- /dev/null
+++ b/src/test/rustdoc-ui/z-help.rs
@@ -0,0 +1,6 @@
+// check-pass
+// compile-flags: -Zhelp
+// check-stdout
+// regex-error-pattern: -Z\s+self-profile
+
+pub struct Foo;
diff --git a/src/test/rustdoc-ui/z-help.stdout b/src/test/rustdoc-ui/z-help.stdout
new file mode 100644
index 00000000000..7296b35788a
--- /dev/null
+++ b/src/test/rustdoc-ui/z-help.stdout
@@ -0,0 +1,195 @@
+    -Z                          allow-features=val -- only allow the listed language features to be enabled in code (space separated)
+    -Z                       always-encode-mir=val -- encode MIR of all functions into the crate metadata (default: no)
+    -Z               assume-incomplete-release=val -- make cfg(version) treat the current version as incomplete (default: no)
+    -Z                            asm-comments=val -- generate comments into the assembly (may change behavior) (default: no)
+    -Z                       assert-incr-state=val -- assert that the incremental cache is in given state: either `loaded` or `not-loaded`.
+    -Z                      binary-dep-depinfo=val -- include artifacts (sysroot, crate dependencies) used during compilation in dep-info (default: no)
+    -Z                       branch-protection=val -- set options for branch target identification and pointer authentication on AArch64
+    -Z                           cf-protection=val -- instrument control-flow architecture protection
+    -Z               cgu-partitioning-strategy=val -- the codegen unit partitioning strategy to use
+    -Z                                   chalk=val -- enable the experimental Chalk-based trait solving engine
+    -Z                         codegen-backend=val -- the backend to use
+    -Z                             combine-cgu=val -- combine CGUs into a single one
+    -Z                              crate-attr=val -- inject the given attribute in the crate
+    -Z                debug-info-for-profiling=val -- emit discriminators and other data necessary for AutoFDO
+    -Z                            debug-macros=val -- emit line numbers debug info inside macros (default: no)
+    -Z                 deduplicate-diagnostics=val -- deduplicate identical diagnostics (default: yes)
+    -Z                  dep-info-omit-d-target=val -- in dep-info output, omit targets for tracking dependencies of the dep-info files themselves (default: no)
+    -Z                               dep-tasks=val -- print tasks that execute and the color their dep node gets (requires debug build) (default: no)
+    -Z                                 dlltool=val -- import library generation tool (windows-gnu only)
+    -Z                 dont-buffer-diagnostics=val -- emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) (default: no)
+    -Z                           drop-tracking=val -- enables drop tracking in generators (default: no)
+    -Z                        dual-proc-macros=val -- load proc macros for both target and host, but only link to the target (default: no)
+    -Z                          dump-dep-graph=val -- dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) (default: no)
+    -Z                  dump-drop-tracking-cfg=val -- dump drop-tracking control-flow graph as a `.dot` file (default: no)
+    -Z                                dump-mir=val -- dump MIR state to file.
+        `val` is used to select which passes and functions to dump. For example:
+        `all` matches all passes and functions,
+        `foo` matches all passes for functions whose name contains 'foo',
+        `foo & ConstProp` only the 'ConstProp' pass for function names containing 'foo',
+        `foo | bar` all passes for function names containing 'foo' or 'bar'.
+    -Z                       dump-mir-dataflow=val -- in addition to `.mir` files, create graphviz `.dot` files with dataflow results (default: no)
+    -Z                            dump-mir-dir=val -- the directory the MIR is dumped into (default: `mir_dump`)
+    -Z            dump-mir-exclude-pass-number=val -- exclude the pass number when dumping MIR (used in tests) (default: no)
+    -Z                       dump-mir-graphviz=val -- in addition to `.mir` files, create graphviz `.dot` files (and with `-Z instrument-coverage`, also create a `.dot` file for the MIR-derived coverage graph) (default: no)
+    -Z                       dump-mir-spanview=val -- in addition to `.mir` files, create `.html` files to view spans for all `statement`s (including terminators), only `terminator` spans, or computed `block` spans (one span encompassing a block's terminator and all statements). If `-Z instrument-coverage` is also enabled, create an additional `.html` file showing the computed coverage spans.
+    -Z                           dwarf-version=val -- version of DWARF debug information to emit (default: 2 or 4, depending on platform)
+    -Z                        emit-stack-sizes=val -- emit a section containing stack size metadata (default: no)
+    -Z                             fewer-names=val -- reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) (default: no)
+    -Z              force-unstable-if-unmarked=val -- force all crates to be `rustc_private` unstable (default: no)
+    -Z                                    fuel=val -- set the optimization fuel quota for a crate
+    -Z                       function-sections=val -- whether each function should go in its own section
+    -Z                    future-incompat-test=val -- forces all lints to be future incompatible, used for internal testing (default: no)
+    -Z                                  gcc-ld=val -- implementation of ld used by cc
+    -Z                      graphviz-dark-mode=val -- use dark-themed colors in graphviz output (default: no)
+    -Z                           graphviz-font=val -- use the given `fontname` in graphviz output; can be overridden by setting environment variable `RUSTC_GRAPHVIZ_FONT` (default: `Courier, monospace`)
+    -Z                               hir-stats=val -- print some statistics about AST and HIR (default: no)
+    -Z                human-readable-cgu-names=val -- generate human-readable, predictable names for codegen units (default: no)
+    -Z                        identify-regions=val -- display unnamed regions as `'<id>`, using a non-ident unique id (default: no)
+    -Z                incremental-ignore-spans=val -- ignore spans during ICH computation -- used for testing (default: no)
+    -Z                        incremental-info=val -- print high-level information about incremental reuse (or the lack thereof) (default: no)
+    -Z              incremental-relative-spans=val -- hash spans relative to their parent item for incr. comp. (default: no)
+    -Z                  incremental-verify-ich=val -- verify incr. comp. hashes of green query instances (default: no)
+    -Z                              inline-mir=val -- enable MIR inlining (default: no)
+    -Z                    inline-mir-threshold=val -- a default MIR inlining threshold (default: 50)
+    -Z               inline-mir-hint-threshold=val -- inlining threshold for functions with inline hint (default: 100)
+    -Z                      inline-in-all-cgus=val -- control whether `#[inline]` functions are in all CGUs
+    -Z                             input-stats=val -- gather statistics about the input (default: no)
+    -Z                     instrument-coverage=val -- instrument the generated code to support LLVM source-based code coverage reports (note, the compiler build config must include `profiler = true`); implies `-C symbol-mangling-version=v0`. Optional values are:
+        `=all` (implicit value)
+        `=except-unused-generics`
+        `=except-unused-functions`
+        `=off` (default)
+    -Z                       instrument-mcount=val -- insert function instrument code for mcount-based tracing (default: no)
+    -Z                       keep-hygiene-data=val -- keep hygiene data after analysis (default: no)
+    -Z                   link-native-libraries=val -- link native libraries in the linker invocation (default: yes)
+    -Z                               link-only=val -- link the `.rlink` file generated by `-Z no-link` (default: no)
+    -Z                            llvm-plugins=val -- a list LLVM plugins to enable (space separated)
+    -Z                         llvm-time-trace=val -- generate JSON tracing data file from LLVM data (default: no)
+    -Z                         location-detail=val -- comma separated list of location details to be tracked when using caller_location valid options are `file`, `line`, and `column` (default: all)
+    -Z                                      ls=val -- list the symbols defined by a library crate (default: no)
+    -Z                         macro-backtrace=val -- show macro backtraces (default: no)
+    -Z                         merge-functions=val -- control the operation of the MergeFunctions LLVM pass, taking the same values as the target option of the same name
+    -Z                              meta-stats=val -- gather metadata statistics (default: no)
+    -Z                          mir-emit-retag=val -- emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 (default: no)
+    -Z                       mir-enable-passes=val -- use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be enabled, overriding all other checks. Passes that are not specified are enabled or disabled by other flags as usual.
+    -Z                           mir-opt-level=val -- MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)
+    -Z                         move-size-limit=val -- the size at which the `large_assignments` lint starts to be emitted
+    -Z                         mutable-noalias=val -- emit noalias metadata for mutable references (default: yes)
+    -Z                   new-llvm-pass-manager=val -- use new LLVM pass manager (default: no)
+    -Z                               nll-facts=val -- dump facts from NLL analysis into side files (default: no)
+    -Z                           nll-facts-dir=val -- the directory the NLL facts are dumped into (default: `nll-facts`)
+    -Z                             no-analysis=val -- parse and expand the source, but run no analysis
+    -Z                              no-codegen=val -- run all passes except codegen; no output
+    -Z              no-generate-arange-section=val -- omit DWARF address ranges that give faster lookups
+    -Z                     no-interleave-lints=val -- execute lints separately; allows benchmarking individual lints
+    -Z                           no-leak-check=val -- disable the 'leak check' for subtyping; unsound, but useful for tests
+    -Z                                 no-link=val -- compile without linking
+    -Z                        no-parallel-llvm=val -- run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)
+    -Z                 no-unique-section-names=val -- do not use unique names for text and data sections when -Z function-sections is used
+    -Z                     no-profiler-runtime=val -- prevent automatic injection of the profiler_builtins crate
+    -Z                          normalize-docs=val -- normalize associated items in rustdoc when generating documentation
+    -Z                                     oom=val -- panic strategy for out-of-memory handling
+    -Z                  osx-rpath-install-name=val -- pass `-install_name @rpath/...` to the macOS linker (default: no)
+    -Z                        diagnostic-width=val -- set the current output width for diagnostic truncation
+    -Z                       panic-abort-tests=val -- support compiling tests with panic=abort (default: no)
+    -Z                           panic-in-drop=val -- panic strategy for panics in drops
+    -Z                              parse-only=val -- parse only; do not compile, assemble, or link (default: no)
+    -Z                              perf-stats=val -- print some performance-related statistics (default: no)
+    -Z pick-stable-methods-before-any-unstable=val -- try to pick stable methods first before picking any unstable methods (default: yes)
+    -Z                                     plt=val -- whether to use the PLT when calling into shared libraries;
+        only has effect for PIC code on systems with ELF binaries
+        (default: PLT is disabled if full relro is enabled)
+    -Z                                polonius=val -- enable polonius-based borrow-checker (default: no)
+    -Z                            polymorphize=val -- perform polymorphization analysis
+    -Z                            pre-link-arg=val -- a single extra argument to prepend the linker invocation (can be used several times)
+    -Z                           pre-link-args=val -- extra arguments to prepend to the linker invocation (space separated)
+    -Z           precise-enum-drop-elaboration=val -- use a more precise version of drop elaboration for matches on enums (default: yes). This results in better codegen, but has caused miscompilations on some tier 2 platforms. See #77382 and #74551.
+    -Z                              print-fuel=val -- make rustc print the total optimization fuel used by a crate
+    -Z                       print-llvm-passes=val -- print the LLVM optimization passes being run (default: no)
+    -Z                        print-mono-items=val -- print the result of the monomorphization collection pass
+    -Z                        print-type-sizes=val -- print layout information for each type encountered (default: no)
+    -Z                    proc-macro-backtrace=val -- show backtraces for panics during proc-macro execution (default: no)
+    -Z                                 profile=val -- insert profiling code (default: no)
+    -Z                        profile-closures=val -- profile size of closures
+    -Z                            profile-emit=val -- file path to emit profiling data at runtime when using 'profile' (default based on relative source path)
+    -Z                        profiler-runtime=val -- name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)
+    -Z                      profile-sample-use=val -- use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)
+    -Z                         query-dep-graph=val -- enable queries of the dependency graph for regression testing (default: no)
+    -Z                        randomize-layout=val -- randomize the layout of types (default: no)
+    -Z                             layout-seed=val -- seed layout randomization
+    -Z                   relax-elf-relocations=val -- whether ELF relocations can be relaxed
+    -Z                             relro-level=val -- choose which RELRO level to use
+    -Z                        remap-cwd-prefix=val -- remap paths under the current working directory to this path prefix
+    -Z         simulate-remapped-rust-src-base=val -- simulate the effect of remap-debuginfo = true at bootstrapping by remapping path to rust's source base directory. only meant for testing purposes
+    -Z                     report-delayed-bugs=val -- immediately print bugs registered with `delay_span_bug` (default: no)
+    -Z                               sanitizer=val -- use a sanitizer
+    -Z          sanitizer-memory-track-origins=val -- enable origins tracking in MemorySanitizer
+    -Z                       sanitizer-recover=val -- enable recovery for selected sanitizers
+    -Z                  saturating-float-casts=val -- make float->int casts UB-free: numbers outside the integer type's range are clipped to the max/min integer respectively, and NaN is mapped to 0 (default: yes)
+    -Z                           save-analysis=val -- write syntax and type analysis (in JSON format) information, in addition to normal output (default: no)
+    -Z                            self-profile=val -- run the self profiler and output the raw event data
+    -Z                     self-profile-events=val -- specify the events recorded by the self profiler;
+        for example: `-Z self-profile-events=default,query-keys`
+        all options: none, all, default, generic-activity, query-provider, query-cache-hit
+                     query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes
+    -Z                    self-profile-counter=val -- counter used by the self profiler (default: `wall-time`), one of:
+        `wall-time` (monotonic clock, i.e. `std::time::Instant`)
+        `instructions:u` (retired instructions, userspace-only)
+        `instructions-minus-irqs:u` (subtracting hardware interrupt counts for extra accuracy)
+    -Z                          share-generics=val -- make the current crate share its generic instantiations
+    -Z                               show-span=val -- show spans for compiler debugging (expr|pat|ty)
+    -Z                              span-debug=val -- forward proc_macro::Span's `Debug` impl to `Span`
+    -Z                       span-free-formats=val -- exclude spans when debug-printing compiler state (default: no)
+    -Z                      src-hash-algorithm=val -- hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)
+    -Z                         stack-protector=val -- control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)
+    -Z                      strict-init-checks=val -- control if mem::uninitialized and mem::zeroed panic on more UB
+    -Z                                   strip=val -- tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)
+    -Z                        split-dwarf-kind=val -- split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform)
+        (default: `split`)
+
+        `split`: sections which do not require relocation are written into a DWARF object (`.dwo`)
+                 file which is ignored by the linker
+        `single`: sections which do not require relocation are written into object file but ignored
+                  by the linker
+    -Z                    split-dwarf-inlining=val -- provide minimal debug info in the object/executable to facilitate online symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF
+    -Z                 symbol-mangling-version=val -- which mangling version to use for symbol names ('legacy' (default) or 'v0')
+    -Z                                   teach=val -- show extended diagnostic help (default: no)
+    -Z                               temps-dir=val -- the directory the intermediate files are written to
+    -Z                          translate-lang=val -- language identifier for diagnostic output
+    -Z                translate-additional-ftl=val -- additional fluent translation to preferentially use (for testing translation)
+    -Z        translate-directionality-markers=val -- emit directionality isolation markers in translated diagnostics
+    -Z                                tune-cpu=val -- select processor to schedule for (`rustc --print target-cpus` for details)
+    -Z                                 thinlto=val -- enable ThinLTO when possible
+    -Z                           thir-unsafeck=val -- use the THIR unsafety checker (default: no)
+    -Z                                 threads=val -- use a thread pool with N threads
+    -Z                                    time=val -- measure time of rustc processes (default: no)
+    -Z                        time-llvm-passes=val -- measure time of each LLVM pass (default: no)
+    -Z                             time-passes=val -- measure time of each rustc pass (default: no)
+    -Z                               tls-model=val -- choose the TLS model to use (`rustc --print tls-models` for details)
+    -Z                            trace-macros=val -- for every macro invocation, print its name and arguments (default: no)
+    -Z   translate-remapped-path-to-local-path=val -- translate remapped paths into local paths when possible (default: yes)
+    -Z                        trap-unreachable=val -- generate trap instructions for unreachable intrinsics (default: use target setting, usually yes)
+    -Z                        treat-err-as-bug=val -- treat error number `val` that occurs as bug
+    -Z                   trim-diagnostic-paths=val -- in diagnostics, use heuristics to shorten paths referring to items
+    -Z                              ui-testing=val -- emit compiler diagnostics in a form suitable for UI testing (default: no)
+    -Z            uninit-const-chunk-threshold=val -- allow generating const initializers with mixed init/uninit chunks, and set the maximum number of chunks for which this is allowed (default: 16)
+    -Z          unleash-the-miri-inside-of-you=val -- take the brakes off const evaluation. NOTE: this is unsound (default: no)
+    -Z                                unpretty=val -- present the input source, unstable (and less-pretty) variants;
+        `normal`, `identified`,
+        `expanded`, `expanded,identified`,
+        `expanded,hygiene` (with internal representations),
+        `ast-tree` (raw AST before expansion),
+        `ast-tree,expanded` (raw AST after expansion),
+        `hir` (the HIR), `hir,identified`,
+        `hir,typed` (HIR with types for each node),
+        `hir-tree` (dump the raw HIR),
+        `mir` (the MIR), or `mir-cfg` (graphviz formatted MIR)
+    -Z                        unsound-mir-opts=val -- enable unsound and buggy MIR optimizations (default: no)
+    -Z                        unstable-options=val -- adds unstable command line options to rustc interface (default: no)
+    -Z                       use-ctors-section=val -- use legacy .ctors section for initializers rather than .init_array
+    -Z                            validate-mir=val -- validate MIR after each transformation
+    -Z                                 verbose=val -- in general, enable more debug printouts (default: no)
+    -Z                          verify-llvm-ir=val -- verify LLVM IR (default: no)
+    -Z            virtual-function-elimination=val -- enables dead virtual function elimination optimization. Requires `-Clto[=[fat,yes]]`
+    -Z                         wasi-exec-model=val -- whether to build a wasi command or reactor
diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
index 98c22af387e..e282884289d 100644
--- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
+++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
@@ -411,7 +411,7 @@ LL | #[derive(SessionDiagnostic)]
    |
    = help: normalized in stderr
 note: required by a bound in `DiagnosticBuilder::<'a, G>::set_arg`
-  --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:538:19
+  --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:539:19
    |
 LL |         arg: impl IntoDiagnosticArg,
    |                   ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg`
diff --git a/src/test/ui/asm/issue-99122-2.rs b/src/test/ui/asm/issue-99122-2.rs
new file mode 100644
index 00000000000..cfb9fd90a55
--- /dev/null
+++ b/src/test/ui/asm/issue-99122-2.rs
@@ -0,0 +1,21 @@
+// check-pass
+// needs-asm-support
+// only-x86_64
+
+// This demonstrates why we need to erase regions before sized check in intrinsicck
+
+struct NoCopy;
+
+struct Wrap<'a, T, Tail: ?Sized>(&'a T, Tail);
+
+pub unsafe fn test() {
+    let i = NoCopy;
+    let j = Wrap(&i, ());
+    let pointer = &j as *const _;
+    core::arch::asm!(
+        "nop",
+        in("eax") pointer,
+    );
+}
+
+fn main() {}
diff --git a/src/test/ui/asm/issue-99122.rs b/src/test/ui/asm/issue-99122.rs
new file mode 100644
index 00000000000..744a563d3d1
--- /dev/null
+++ b/src/test/ui/asm/issue-99122.rs
@@ -0,0 +1,13 @@
+// needs-asm-support
+// only-x86_64
+
+pub unsafe fn test() {
+    let pointer = 1u32 as *const _;
+    //~^ ERROR cannot cast to a pointer of an unknown kind
+    core::arch::asm!(
+        "nop",
+        in("eax") pointer,
+    );
+}
+
+fn main() {}
diff --git a/src/test/ui/asm/issue-99122.stderr b/src/test/ui/asm/issue-99122.stderr
new file mode 100644
index 00000000000..2758a4ac437
--- /dev/null
+++ b/src/test/ui/asm/issue-99122.stderr
@@ -0,0 +1,11 @@
+error[E0641]: cannot cast to a pointer of an unknown kind
+  --> $DIR/issue-99122.rs:5:27
+   |
+LL |     let pointer = 1u32 as *const _;
+   |                           ^^^^^^^^ needs more type information
+   |
+   = note: the type information given here is insufficient to check whether the pointer cast is valid
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0641`.
diff --git a/src/test/ui/async-await/async-await.rs b/src/test/ui/async-await/async-await.rs
index 3d22025bf28..9cabf16f8bb 100644
--- a/src/test/ui/async-await/async-await.rs
+++ b/src/test/ui/async-await/async-await.rs
@@ -6,7 +6,7 @@
 
 #![allow(unused)]
 
-// edition:2018
+// edition: 2018
 // aux-build:arc_wake.rs
 
 extern crate arc_wake;
diff --git a/src/test/ui/const-generics/float-generic.adt_const_params.stderr b/src/test/ui/const-generics/float-generic.adt_const_params.stderr
new file mode 100644
index 00000000000..fef5ef0d1fa
--- /dev/null
+++ b/src/test/ui/const-generics/float-generic.adt_const_params.stderr
@@ -0,0 +1,11 @@
+error[E0741]: `f32` is forbidden as the type of a const generic parameter
+  --> $DIR/float-generic.rs:5:17
+   |
+LL | fn foo<const F: f32>() {}
+   |                 ^^^
+   |
+   = note: floats do not derive `Eq` or `Ord`, which are required for const parameters
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0741`.
diff --git a/src/test/ui/const-generics/float-generic.rs b/src/test/ui/const-generics/float-generic.rs
new file mode 100644
index 00000000000..b72059b5b1c
--- /dev/null
+++ b/src/test/ui/const-generics/float-generic.rs
@@ -0,0 +1,12 @@
+// revisions: simple adt_const_params
+#![cfg_attr(adt_const_params, feature(adt_const_params))]
+#![cfg_attr(adt_const_params, allow(incomplete_features))]
+
+fn foo<const F: f32>() {}
+//~^ ERROR `f32` is forbidden as the type of a const generic parameter
+
+const C: f32 = 1.0;
+
+fn main() {
+    foo::<C>();
+}
diff --git a/src/test/ui/const-generics/float-generic.simple.stderr b/src/test/ui/const-generics/float-generic.simple.stderr
new file mode 100644
index 00000000000..89ca36b0f63
--- /dev/null
+++ b/src/test/ui/const-generics/float-generic.simple.stderr
@@ -0,0 +1,11 @@
+error: `f32` is forbidden as the type of a const generic parameter
+  --> $DIR/float-generic.rs:5:17
+   |
+LL | fn foo<const F: f32>() {}
+   |                 ^^^
+   |
+   = note: the only supported types are integers, `bool` and `char`
+   = help: more complex types are supported with `#![feature(adt_const_params)]`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/inference/erase-type-params-in-label.stderr b/src/test/ui/inference/erase-type-params-in-label.stderr
index 7bb281802d2..5c52e7bcfab 100644
--- a/src/test/ui/inference/erase-type-params-in-label.stderr
+++ b/src/test/ui/inference/erase-type-params-in-label.stderr
@@ -10,10 +10,6 @@ note: required by a bound in `foo`
    |
 LL | fn foo<T, K, W: Default, Z: Default>(t: T, k: K) -> Foo<T, K, W, Z> {
    |                 ^^^^^^^ required by this bound in `foo`
-help: consider giving `foo` an explicit type, where the type for type parameter `W` is specified
-   |
-LL |     let foo: Foo<i32, &str, W, Z> = foo(1, "");
-   |            ++++++++++++++++++++++
 help: consider specifying the type arguments in the function call
    |
 LL |     let foo = foo::<T, K, W, Z>(1, "");
@@ -31,10 +27,6 @@ note: required by a bound in `bar`
    |
 LL | fn bar<T, K, Z: Default>(t: T, k: K) -> Bar<T, K, Z> {
    |                 ^^^^^^^ required by this bound in `bar`
-help: consider giving `bar` an explicit type, where the type for type parameter `Z` is specified
-   |
-LL |     let bar: Bar<i32, &str, Z> = bar(1, "");
-   |            +++++++++++++++++++
 help: consider specifying the type arguments in the function call
    |
 LL |     let bar = bar::<T, K, Z>(1, "");
diff --git a/src/test/ui/inference/issue-71732.stderr b/src/test/ui/inference/issue-71732.stderr
index db153d38aaa..04673a375cf 100644
--- a/src/test/ui/inference/issue-71732.stderr
+++ b/src/test/ui/inference/issue-71732.stderr
@@ -13,10 +13,6 @@ note: required by a bound in `HashMap::<K, V, S>::get`
    |
 LL |         K: Borrow<Q>,
    |            ^^^^^^^^^ required by this bound in `HashMap::<K, V, S>::get`
-help: consider specifying the generic argument
-   |
-LL |         .get::<Q>(&"key".into())
-   |             +++++
 help: consider specifying the type argument in the function call
    |
 LL |         .get::<Q>(&"key".into())
diff --git a/src/test/ui/issues/issue-35976.stderr b/src/test/ui/issues/issue-35976.stderr
index f9b9b7dbd34..fe16f97b9d0 100644
--- a/src/test/ui/issues/issue-35976.stderr
+++ b/src/test/ui/issues/issue-35976.stderr
@@ -6,11 +6,6 @@ LL |         fn wait(&self) where Self: Sized;
 ...
 LL |     arg.wait();
    |         ^^^^
-   |
-help: another candidate was found in the following trait, perhaps add a `use` for it:
-   |
-LL | use private::Future;
-   |
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/lifetimes/bare-trait-object-borrowck.rs b/src/test/ui/lifetimes/bare-trait-object-borrowck.rs
new file mode 100644
index 00000000000..45f5e4ae129
--- /dev/null
+++ b/src/test/ui/lifetimes/bare-trait-object-borrowck.rs
@@ -0,0 +1,24 @@
+#![allow(bare_trait_objects)]
+// check-pass
+pub struct FormatWith<'a, I, F> {
+    sep: &'a str,
+    /// FormatWith uses interior mutability because Display::fmt takes &self.
+    inner: RefCell<Option<(I, F)>>,
+}
+
+use std::cell::RefCell;
+use std::fmt;
+
+struct Layout;
+
+pub fn new_format<'a, I, F>(iter: I, separator: &'a str, f: F) -> FormatWith<'a, I, F>
+where
+    I: Iterator,
+    F: FnMut(I::Item, &mut FnMut(&fmt::Display) -> fmt::Result) -> fmt::Result,
+{
+    FormatWith { sep: separator, inner: RefCell::new(Some((iter, f))) }
+}
+
+fn main() {
+    let _ = new_format(0..32, " | ", |i, f| f(&format_args!("0x{:x}", i)));
+}
diff --git a/src/test/ui/lifetimes/bare-trait-object.rs b/src/test/ui/lifetimes/bare-trait-object.rs
new file mode 100644
index 00000000000..9eff618c734
--- /dev/null
+++ b/src/test/ui/lifetimes/bare-trait-object.rs
@@ -0,0 +1,25 @@
+// Verify that lifetime resolution correctly accounts for `Fn` bare trait objects.
+// check-pass
+#![allow(bare_trait_objects)]
+
+// This should work as: fn next_u32(fill_buf: &mut dyn FnMut(&mut [u8]))
+fn next_u32(fill_buf: &mut FnMut(&mut [u8])) {
+    let mut buf: [u8; 4] = [0; 4];
+    fill_buf(&mut buf);
+}
+
+fn explicit(fill_buf: &mut dyn FnMut(&mut [u8])) {
+    let mut buf: [u8; 4] = [0; 4];
+    fill_buf(&mut buf);
+}
+
+fn main() {
+    let _: fn(&mut FnMut(&mut [u8])) = next_u32;
+    let _: &dyn Fn(&mut FnMut(&mut [u8])) = &next_u32;
+    let _: fn(&mut FnMut(&mut [u8])) = explicit;
+    let _: &dyn Fn(&mut FnMut(&mut [u8])) = &explicit;
+    let _: fn(&mut dyn FnMut(&mut [u8])) = next_u32;
+    let _: &dyn Fn(&mut dyn FnMut(&mut [u8])) = &next_u32;
+    let _: fn(&mut dyn FnMut(&mut [u8])) = explicit;
+    let _: &dyn Fn(&mut dyn FnMut(&mut [u8])) = &explicit;
+}
diff --git a/src/test/ui/methods/issues/issue-61525.rs b/src/test/ui/methods/issues/issue-61525.rs
new file mode 100644
index 00000000000..c5ca0326e43
--- /dev/null
+++ b/src/test/ui/methods/issues/issue-61525.rs
@@ -0,0 +1,20 @@
+pub trait Example {
+    fn query<Q>(self, q: Q);
+}
+
+impl Example for i32 {
+    fn query<Q>(self, _: Q) {
+        unimplemented!()
+    }
+}
+
+mod nested {
+    use super::Example;
+    fn example() {
+        1.query::<dyn ToString>("")
+        //~^ ERROR the size for values of type `dyn ToString` cannot be known at compilation time
+        //~| ERROR mismatched types
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/methods/issues/issue-61525.stderr b/src/test/ui/methods/issues/issue-61525.stderr
new file mode 100644
index 00000000000..aec968d7c44
--- /dev/null
+++ b/src/test/ui/methods/issues/issue-61525.stderr
@@ -0,0 +1,39 @@
+error[E0277]: the size for values of type `dyn ToString` cannot be known at compilation time
+  --> $DIR/issue-61525.rs:14:33
+   |
+LL |         1.query::<dyn ToString>("")
+   |           -----                 ^^ doesn't have a size known at compile-time
+   |           |
+   |           required by a bound introduced by this call
+   |
+   = help: the trait `Sized` is not implemented for `dyn ToString`
+note: required by a bound in `Example::query`
+  --> $DIR/issue-61525.rs:2:14
+   |
+LL |     fn query<Q>(self, q: Q);
+   |              ^ required by this bound in `Example::query`
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL |     fn query<Q: ?Sized>(self, q: Q);
+   |               ++++++++
+
+error[E0308]: mismatched types
+  --> $DIR/issue-61525.rs:14:33
+   |
+LL |         1.query::<dyn ToString>("")
+   |           --------------------- ^^ expected trait object `dyn ToString`, found `&str`
+   |           |
+   |           arguments to this function are incorrect
+   |
+   = note: expected trait object `dyn ToString`
+                 found reference `&'static str`
+note: associated function defined here
+  --> $DIR/issue-61525.rs:2:8
+   |
+LL |     fn query<Q>(self, q: Q);
+   |        ^^^^^
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0277, E0308.
+For more information about an error, try `rustc --explain E0277`.
diff --git a/src/test/ui/parser/macro/macro-doc-comments-1.stderr b/src/test/ui/parser/macro/macro-doc-comments-1.stderr
index 96bdb9808ee..0ebf3d52b63 100644
--- a/src/test/ui/parser/macro/macro-doc-comments-1.stderr
+++ b/src/test/ui/parser/macro/macro-doc-comments-1.stderr
@@ -5,7 +5,10 @@ LL | macro_rules! outer {
    | ------------------ when calling this macro
 ...
 LL |     //! Inner
-   |     ^^^^^^^^^ no rules expected this token in macro call
+   |     ^^^^^^^^^
+   |     |
+   |     no rules expected this token in macro call
+   |     inner doc comments expand to `#![doc = "..."]`, which is what this macro attempted to match
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/macro/macro-doc-comments-2.stderr b/src/test/ui/parser/macro/macro-doc-comments-2.stderr
index 023d1a3e039..346d865868d 100644
--- a/src/test/ui/parser/macro/macro-doc-comments-2.stderr
+++ b/src/test/ui/parser/macro/macro-doc-comments-2.stderr
@@ -5,7 +5,10 @@ LL | macro_rules! inner {
    | ------------------ when calling this macro
 ...
 LL |     /// Outer
-   |     ^^^^^^^^^ no rules expected this token in macro call
+   |     ^^^^^^^^^
+   |     |
+   |     no rules expected this token in macro call
+   |     outer doc comments expand to `#[doc = "..."]`, which is what this macro attempted to match
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/resolve/issue-55673.rs b/src/test/ui/resolve/issue-55673.rs
new file mode 100644
index 00000000000..0436bd39742
--- /dev/null
+++ b/src/test/ui/resolve/issue-55673.rs
@@ -0,0 +1,12 @@
+trait Foo {
+    type Bar;
+}
+
+fn foo<T: Foo>()
+where
+    T::Baa: std::fmt::Debug,
+    //~^ ERROR associated type `Baa` not found for `T`
+{
+}
+
+fn main() {}
diff --git a/src/test/ui/resolve/issue-55673.stderr b/src/test/ui/resolve/issue-55673.stderr
new file mode 100644
index 00000000000..39318f95905
--- /dev/null
+++ b/src/test/ui/resolve/issue-55673.stderr
@@ -0,0 +1,9 @@
+error[E0220]: associated type `Baa` not found for `T`
+  --> $DIR/issue-55673.rs:7:8
+   |
+LL |     T::Baa: std::fmt::Debug,
+   |        ^^^ there is a similarly named associated type `Bar` in the trait `Foo`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0220`.
diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs b/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs
index bb1aff70d89..d8febd273c9 100644
--- a/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs
+++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs
@@ -9,9 +9,11 @@ fn _if_let_guard() {
 
         () if (let 0 = 1) => {}
         //~^ ERROR `let` expressions in this position are unstable
+        //~| ERROR expected expression, found `let` statement
 
         () if (((let 0 = 1))) => {}
         //~^ ERROR `let` expressions in this position are unstable
+        //~| ERROR expected expression, found `let` statement
 
         () if true && let 0 = 1 => {}
         //~^ ERROR `if let` guards are experimental
@@ -23,13 +25,17 @@ fn _if_let_guard() {
 
         () if (let 0 = 1) && true => {}
         //~^ ERROR `let` expressions in this position are unstable
+        //~| ERROR expected expression, found `let` statement
 
         () if true && (let 0 = 1) => {}
         //~^ ERROR `let` expressions in this position are unstable
+        //~| ERROR expected expression, found `let` statement
 
         () if (let 0 = 1) && (let 0 = 1) => {}
         //~^ ERROR `let` expressions in this position are unstable
         //~| ERROR `let` expressions in this position are unstable
+        //~| ERROR expected expression, found `let` statement
+        //~| ERROR expected expression, found `let` statement
 
         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
         //~^ ERROR `if let` guards are experimental
@@ -38,6 +44,7 @@ fn _if_let_guard() {
         //~| ERROR `let` expressions in this position are unstable
         //~| ERROR `let` expressions in this position are unstable
         //~| ERROR `let` expressions in this position are unstable
+        //~| ERROR expected expression, found `let` statement
 
         () if let Range { start: _, end: _ } = (true..true) && false => {}
         //~^ ERROR `if let` guards are experimental
diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr
index 370a57318fd..52b5bca628a 100644
--- a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr
+++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr
@@ -1,17 +1,59 @@
 error: expected expression, found `let` statement
-  --> $DIR/feature-gate.rs:59:16
+  --> $DIR/feature-gate.rs:10:16
+   |
+LL |         () if (let 0 = 1) => {}
+   |                ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:14:18
+   |
+LL |         () if (((let 0 = 1))) => {}
+   |                  ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:26:16
+   |
+LL |         () if (let 0 = 1) && true => {}
+   |                ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:30:24
+   |
+LL |         () if true && (let 0 = 1) => {}
+   |                        ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:34:16
+   |
+LL |         () if (let 0 = 1) && (let 0 = 1) => {}
+   |                ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:34:31
+   |
+LL |         () if (let 0 = 1) && (let 0 = 1) => {}
+   |                               ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:40:42
+   |
+LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
+   |                                          ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/feature-gate.rs:66:16
    |
 LL |     use_expr!((let 0 = 1 && 0 == 0));
    |                ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/feature-gate.rs:62:16
+  --> $DIR/feature-gate.rs:69:16
    |
 LL |     use_expr!((let 0 = 1));
    |                ^^^
 
 error: no rules expected the token `let`
-  --> $DIR/feature-gate.rs:71:15
+  --> $DIR/feature-gate.rs:78:15
    |
 LL |     macro_rules! use_expr {
    |     --------------------- when calling this macro
@@ -30,7 +72,7 @@ LL |         () if let 0 = 1 => {}
    = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
 
 error[E0658]: `if let` guards are experimental
-  --> $DIR/feature-gate.rs:16:12
+  --> $DIR/feature-gate.rs:18:12
    |
 LL |         () if true && let 0 = 1 => {}
    |            ^^^^^^^^^^^^^^^^^^^^
@@ -40,7 +82,7 @@ LL |         () if true && let 0 = 1 => {}
    = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
 
 error[E0658]: `if let` guards are experimental
-  --> $DIR/feature-gate.rs:20:12
+  --> $DIR/feature-gate.rs:22:12
    |
 LL |         () if let 0 = 1 && true => {}
    |            ^^^^^^^^^^^^^^^^^^^^
@@ -50,7 +92,7 @@ LL |         () if let 0 = 1 && true => {}
    = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
 
 error[E0658]: `if let` guards are experimental
-  --> $DIR/feature-gate.rs:34:12
+  --> $DIR/feature-gate.rs:40:12
    |
 LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -60,7 +102,7 @@ LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 =
    = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
 
 error[E0658]: `if let` guards are experimental
-  --> $DIR/feature-gate.rs:42:12
+  --> $DIR/feature-gate.rs:49:12
    |
 LL |         () if let Range { start: _, end: _ } = (true..true) && false => {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -70,7 +112,7 @@ LL |         () if let Range { start: _, end: _ } = (true..true) && false => {}
    = help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
 
 error[E0658]: `if let` guards are experimental
-  --> $DIR/feature-gate.rs:67:12
+  --> $DIR/feature-gate.rs:74:12
    |
 LL |         () if let 0 = 1 => {}
    |            ^^^^^^^^^^^^
@@ -89,7 +131,7 @@ LL |         () if (let 0 = 1) => {}
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:13:18
+  --> $DIR/feature-gate.rs:14:18
    |
 LL |         () if (((let 0 = 1))) => {}
    |                  ^^^^^^^^^
@@ -98,7 +140,7 @@ LL |         () if (((let 0 = 1))) => {}
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:16:23
+  --> $DIR/feature-gate.rs:18:23
    |
 LL |         () if true && let 0 = 1 => {}
    |                       ^^^^^^^^^
@@ -107,7 +149,7 @@ LL |         () if true && let 0 = 1 => {}
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:20:15
+  --> $DIR/feature-gate.rs:22:15
    |
 LL |         () if let 0 = 1 && true => {}
    |               ^^^^^^^^^
@@ -116,7 +158,7 @@ LL |         () if let 0 = 1 && true => {}
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:24:16
+  --> $DIR/feature-gate.rs:26:16
    |
 LL |         () if (let 0 = 1) && true => {}
    |                ^^^^^^^^^
@@ -125,7 +167,7 @@ LL |         () if (let 0 = 1) && true => {}
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:27:24
+  --> $DIR/feature-gate.rs:30:24
    |
 LL |         () if true && (let 0 = 1) => {}
    |                        ^^^^^^^^^
@@ -134,7 +176,7 @@ LL |         () if true && (let 0 = 1) => {}
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:30:16
+  --> $DIR/feature-gate.rs:34:16
    |
 LL |         () if (let 0 = 1) && (let 0 = 1) => {}
    |                ^^^^^^^^^
@@ -143,7 +185,7 @@ LL |         () if (let 0 = 1) && (let 0 = 1) => {}
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:30:31
+  --> $DIR/feature-gate.rs:34:31
    |
 LL |         () if (let 0 = 1) && (let 0 = 1) => {}
    |                               ^^^^^^^^^
@@ -152,7 +194,7 @@ LL |         () if (let 0 = 1) && (let 0 = 1) => {}
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:34:15
+  --> $DIR/feature-gate.rs:40:15
    |
 LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
    |               ^^^^^^^^^
@@ -161,7 +203,7 @@ LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 =
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:34:28
+  --> $DIR/feature-gate.rs:40:28
    |
 LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
    |                            ^^^^^^^^^
@@ -170,7 +212,7 @@ LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 =
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:34:42
+  --> $DIR/feature-gate.rs:40:42
    |
 LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
    |                                          ^^^^^^^^^
@@ -179,7 +221,7 @@ LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 =
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:34:55
+  --> $DIR/feature-gate.rs:40:55
    |
 LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
    |                                                       ^^^^^^^^^
@@ -188,7 +230,7 @@ LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 =
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:34:68
+  --> $DIR/feature-gate.rs:40:68
    |
 LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
    |                                                                    ^^^^^^^^^
@@ -197,7 +239,7 @@ LL |         () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 =
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:42:15
+  --> $DIR/feature-gate.rs:49:15
    |
 LL |         () if let Range { start: _, end: _ } = (true..true) && false => {}
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -206,7 +248,7 @@ LL |         () if let Range { start: _, end: _ } = (true..true) && false => {}
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:59:16
+  --> $DIR/feature-gate.rs:66:16
    |
 LL |     use_expr!((let 0 = 1 && 0 == 0));
    |                ^^^^^^^^^
@@ -215,7 +257,7 @@ LL |     use_expr!((let 0 = 1 && 0 == 0));
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:62:16
+  --> $DIR/feature-gate.rs:69:16
    |
 LL |     use_expr!((let 0 = 1));
    |                ^^^^^^^^^
@@ -223,6 +265,6 @@ LL |     use_expr!((let 0 = 1));
    = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
-error: aborting due to 25 previous errors
+error: aborting due to 32 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs
index 36b730505c2..069d2dc414d 100644
--- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs
+++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs
@@ -28,47 +28,61 @@ fn main() {}
 fn _if() {
     if (let 0 = 1) {}
     //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
 
     if (((let 0 = 1))) {}
     //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
 
     if (let 0 = 1) && true {}
     //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
 
     if true && (let 0 = 1) {}
     //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
 
     if (let 0 = 1) && (let 0 = 1) {}
     //~^ ERROR `let` expressions are not supported here
     //~| ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
+    //~| ERROR expected expression, found `let` statement
 
     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
     //~^ ERROR `let` expressions are not supported here
     //~| ERROR `let` expressions are not supported here
     //~| ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
 }
 
 fn _while() {
     while (let 0 = 1) {}
     //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
 
     while (((let 0 = 1))) {}
     //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
 
     while (let 0 = 1) && true {}
     //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
 
     while true && (let 0 = 1) {}
     //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
 
     while (let 0 = 1) && (let 0 = 1) {}
     //~^ ERROR `let` expressions are not supported here
     //~| ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
+    //~| ERROR expected expression, found `let` statement
 
     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
     //~^ ERROR `let` expressions are not supported here
     //~| ERROR `let` expressions are not supported here
     //~| ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
 }
 
 fn _macros() {
@@ -89,39 +103,64 @@ fn _macros() {
 }
 
 fn nested_within_if_expr() {
-    if &let 0 = 0 {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR mismatched types
+    if &let 0 = 0 {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR expected expression, found `let` statement
 
-    if !let 0 = 0 {} //~ ERROR `let` expressions are not supported here
-    if *let 0 = 0 {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR type `bool` cannot be dereferenced
-    if -let 0 = 0 {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR cannot apply unary operator `-` to type `bool`
+    if !let 0 = 0 {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
+    if *let 0 = 0 {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR type `bool` cannot be dereferenced
+    //~| ERROR expected expression, found `let` statement
+    if -let 0 = 0 {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR cannot apply unary operator `-` to type `bool`
+    //~| ERROR expected expression, found `let` statement
 
     fn _check_try_binds_tighter() -> Result<(), ()> {
         if let 0 = 0? {}
         //~^ ERROR the `?` operator can only be applied to values that implement `Try`
         Ok(())
     }
-    if (let 0 = 0)? {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR the `?` operator can only be applied to values that implement `Try`
+    if (let 0 = 0)? {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR the `?` operator can only be applied to values that implement `Try`
     //~| ERROR the `?` operator can only be used in a function that returns `Result`
+    //~| ERROR expected expression, found `let` statement
 
-    if true || let 0 = 0 {} //~ ERROR `let` expressions are not supported here
-    if (true || let 0 = 0) {} //~ ERROR `let` expressions are not supported here
-    if true && (true || let 0 = 0) {} //~ ERROR `let` expressions are not supported here
-    if true || (true && let 0 = 0) {} //~ ERROR `let` expressions are not supported here
+    if true || let 0 = 0 {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
+    if (true || let 0 = 0) {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
+    if true && (true || let 0 = 0) {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
+    if true || (true && let 0 = 0) {}
+    //~^ ERROR `let` expressions are not supported here
 
     let mut x = true;
-    if x = let 0 = 0 {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR mismatched types
+    if x = let 0 = 0 {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR expected expression, found `let` statement
 
-    if true..(let 0 = 0) {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR mismatched types
-    if ..(let 0 = 0) {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR mismatched types
-    if (let 0 = 0).. {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR mismatched types
+    if true..(let 0 = 0) {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR expected expression, found `let` statement
+    if ..(let 0 = 0) {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR expected expression, found `let` statement
+    if (let 0 = 0).. {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR expected expression, found `let` statement
 
     // Binds as `(let ... = true)..true &&/|| false`.
     if let Range { start: _, end: _ } = true..true && false {}
@@ -151,42 +190,68 @@ fn nested_within_if_expr() {
 
     if let true = let true = true {}
     //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
 }
 
 fn nested_within_while_expr() {
-    while &let 0 = 0 {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR mismatched types
+    while &let 0 = 0 {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR expected expression, found `let` statement
 
-    while !let 0 = 0 {} //~ ERROR `let` expressions are not supported here
-    while *let 0 = 0 {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR type `bool` cannot be dereferenced
-    while -let 0 = 0 {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR cannot apply unary operator `-` to type `bool`
+    while !let 0 = 0 {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
+    while *let 0 = 0 {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR type `bool` cannot be dereferenced
+    //~| ERROR expected expression, found `let` statement
+    while -let 0 = 0 {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR cannot apply unary operator `-` to type `bool`
+    //~| ERROR expected expression, found `let` statement
 
     fn _check_try_binds_tighter() -> Result<(), ()> {
         while let 0 = 0? {}
         //~^ ERROR the `?` operator can only be applied to values that implement `Try`
         Ok(())
     }
-    while (let 0 = 0)? {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR the `?` operator can only be applied to values that implement `Try`
+    while (let 0 = 0)? {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR the `?` operator can only be applied to values that implement `Try`
     //~| ERROR the `?` operator can only be used in a function that returns `Result`
+    //~| ERROR expected expression, found `let` statement
 
-    while true || let 0 = 0 {} //~ ERROR `let` expressions are not supported here
-    while (true || let 0 = 0) {} //~ ERROR `let` expressions are not supported here
-    while true && (true || let 0 = 0) {} //~ ERROR `let` expressions are not supported here
-    while true || (true && let 0 = 0) {} //~ ERROR `let` expressions are not supported here
+    while true || let 0 = 0 {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
+    while (true || let 0 = 0) {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
+    while true && (true || let 0 = 0) {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
+    while true || (true && let 0 = 0) {}
+    //~^ ERROR `let` expressions are not supported here
 
     let mut x = true;
-    while x = let 0 = 0 {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR mismatched types
+    while x = let 0 = 0 {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR expected expression, found `let` statement
 
-    while true..(let 0 = 0) {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR mismatched types
-    while ..(let 0 = 0) {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR mismatched types
-    while (let 0 = 0).. {} //~ ERROR `let` expressions are not supported here
-    //~^ ERROR mismatched types
+    while true..(let 0 = 0) {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR expected expression, found `let` statement
+    while ..(let 0 = 0) {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR expected expression, found `let` statement
+    while (let 0 = 0).. {}
+    //~^ ERROR `let` expressions are not supported here
+    //~| ERROR mismatched types
+    //~| ERROR expected expression, found `let` statement
 
     // Binds as `(let ... = true)..true &&/|| false`.
     while let Range { start: _, end: _ } = true..true && false {}
@@ -216,6 +281,7 @@ fn nested_within_while_expr() {
 
     while let true = let true = true {}
     //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
 }
 
 fn not_error_because_clarified_intent() {
@@ -316,15 +382,18 @@ fn inside_const_generic_arguments() {
     impl<const B: bool> A<{B}> { const O: u32 = 5; }
 
     if let A::<{
-        true && let 1 = 1 //~ ERROR `let` expressions are not supported here
+        true && let 1 = 1
+        //~^ ERROR `let` expressions are not supported here
     }>::O = 5 {}
 
     while let A::<{
-        true && let 1 = 1 //~ ERROR `let` expressions are not supported here
+        true && let 1 = 1
+        //~^ ERROR `let` expressions are not supported here
     }>::O = 5 {}
 
     if A::<{
-        true && let 1 = 1 //~ ERROR `let` expressions are not supported here
+        true && let 1 = 1
+        //~^ ERROR `let` expressions are not supported here
     }>::O == 5 {}
 
     // In the cases above we have `ExprKind::Block` to help us out.
@@ -345,14 +414,18 @@ fn with_parenthesis() {
 
     if (let Some(a) = opt && true) {
     //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
     }
 
     if (let Some(a) = opt) && true {
     //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
     }
     if (let Some(a) = opt) && (let Some(b) = a) {
     //~^ ERROR `let` expressions are not supported here
     //~| ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
+    //~| ERROR expected expression, found `let` statement
     }
     if let Some(a) = opt && (true && true) {
     }
@@ -360,13 +433,18 @@ fn with_parenthesis() {
     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
     //~^ ERROR `let` expressions are not supported here
     //~| ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
+    //~| ERROR expected expression, found `let` statement
     }
     if (let Some(a) = opt && (let Some(b) = a)) && true {
     //~^ ERROR `let` expressions are not supported here
     //~| ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
+    //~| ERROR expected expression, found `let` statement
     }
     if (let Some(a) = opt && (true)) && true {
     //~^ ERROR `let` expressions are not supported here
+    //~| ERROR expected expression, found `let` statement
     }
 
     if (true && (true)) && let Some(a) = opt {
diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
index 93a1f691c8e..cca5310ee0f 100644
--- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
+++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
@@ -1,113 +1,353 @@
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:232:6
+  --> $DIR/disallowed-positions.rs:29:9
+   |
+LL |     if (let 0 = 1) {}
+   |         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:33:11
+   |
+LL |     if (((let 0 = 1))) {}
+   |           ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:37:9
+   |
+LL |     if (let 0 = 1) && true {}
+   |         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:41:17
+   |
+LL |     if true && (let 0 = 1) {}
+   |                 ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:45:9
+   |
+LL |     if (let 0 = 1) && (let 0 = 1) {}
+   |         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:45:24
+   |
+LL |     if (let 0 = 1) && (let 0 = 1) {}
+   |                        ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:51:35
+   |
+LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                   ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:59:12
+   |
+LL |     while (let 0 = 1) {}
+   |            ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:63:14
+   |
+LL |     while (((let 0 = 1))) {}
+   |              ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:67:12
+   |
+LL |     while (let 0 = 1) && true {}
+   |            ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:71:20
+   |
+LL |     while true && (let 0 = 1) {}
+   |                    ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:75:12
+   |
+LL |     while (let 0 = 1) && (let 0 = 1) {}
+   |            ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:75:27
+   |
+LL |     while (let 0 = 1) && (let 0 = 1) {}
+   |                           ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:81:38
+   |
+LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
+   |                                      ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:106:9
+   |
+LL |     if &let 0 = 0 {}
+   |         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:111:9
+   |
+LL |     if !let 0 = 0 {}
+   |         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:114:9
+   |
+LL |     if *let 0 = 0 {}
+   |         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:118:9
+   |
+LL |     if -let 0 = 0 {}
+   |         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:128:9
+   |
+LL |     if (let 0 = 0)? {}
+   |         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:134:16
+   |
+LL |     if true || let 0 = 0 {}
+   |                ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:137:17
+   |
+LL |     if (true || let 0 = 0) {}
+   |                 ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:140:25
+   |
+LL |     if true && (true || let 0 = 0) {}
+   |                         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:147:12
+   |
+LL |     if x = let 0 = 0 {}
+   |            ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:152:15
+   |
+LL |     if true..(let 0 = 0) {}
+   |               ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:156:11
+   |
+LL |     if ..(let 0 = 0) {}
+   |           ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:160:9
+   |
+LL |     if (let 0 = 0).. {}
+   |         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:191:19
+   |
+LL |     if let true = let true = true {}
+   |                   ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:197:12
+   |
+LL |     while &let 0 = 0 {}
+   |            ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:202:12
+   |
+LL |     while !let 0 = 0 {}
+   |            ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:205:12
+   |
+LL |     while *let 0 = 0 {}
+   |            ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:209:12
+   |
+LL |     while -let 0 = 0 {}
+   |            ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:219:12
+   |
+LL |     while (let 0 = 0)? {}
+   |            ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:225:19
+   |
+LL |     while true || let 0 = 0 {}
+   |                   ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:228:20
+   |
+LL |     while (true || let 0 = 0) {}
+   |                    ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:231:28
+   |
+LL |     while true && (true || let 0 = 0) {}
+   |                            ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:238:15
+   |
+LL |     while x = let 0 = 0 {}
+   |               ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:243:18
+   |
+LL |     while true..(let 0 = 0) {}
+   |                  ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:247:14
+   |
+LL |     while ..(let 0 = 0) {}
+   |              ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:251:12
+   |
+LL |     while (let 0 = 0).. {}
+   |            ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:282:22
+   |
+LL |     while let true = let true = true {}
+   |                      ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:298:6
    |
 LL |     &let 0 = 0;
    |      ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:236:6
+  --> $DIR/disallowed-positions.rs:302:6
    |
 LL |     !let 0 = 0;
    |      ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:239:6
+  --> $DIR/disallowed-positions.rs:305:6
    |
 LL |     *let 0 = 0;
    |      ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:243:6
+  --> $DIR/disallowed-positions.rs:309:6
    |
 LL |     -let 0 = 0;
    |      ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:253:6
+  --> $DIR/disallowed-positions.rs:319:6
    |
 LL |     (let 0 = 0)?;
    |      ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:259:13
+  --> $DIR/disallowed-positions.rs:325:13
    |
 LL |     true || let 0 = 0;
    |             ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:262:14
+  --> $DIR/disallowed-positions.rs:328:14
    |
 LL |     (true || let 0 = 0);
    |              ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:265:22
+  --> $DIR/disallowed-positions.rs:331:22
    |
 LL |     true && (true || let 0 = 0);
    |                      ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:270:9
+  --> $DIR/disallowed-positions.rs:336:9
    |
 LL |     x = let 0 = 0;
    |         ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:274:12
+  --> $DIR/disallowed-positions.rs:340:12
    |
 LL |     true..(let 0 = 0);
    |            ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:277:8
+  --> $DIR/disallowed-positions.rs:343:8
    |
 LL |     ..(let 0 = 0);
    |        ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:280:6
+  --> $DIR/disallowed-positions.rs:346:6
    |
 LL |     (let 0 = 0)..;
    |      ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:284:6
+  --> $DIR/disallowed-positions.rs:350:6
    |
 LL |     (let Range { start: _, end: _ } = true..true || false);
    |      ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:289:6
+  --> $DIR/disallowed-positions.rs:355:6
    |
 LL |     (let true = let true = true);
    |      ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:289:17
+  --> $DIR/disallowed-positions.rs:355:17
    |
 LL |     (let true = let true = true);
    |                 ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:296:25
+  --> $DIR/disallowed-positions.rs:362:25
    |
 LL |         let x = true && let y = 1;
    |                         ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:302:19
+  --> $DIR/disallowed-positions.rs:368:19
    |
 LL |         [1, 2, 3][let _ = ()]
    |                   ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:307:6
+  --> $DIR/disallowed-positions.rs:373:6
    |
 LL |     &let 0 = 0
    |      ^^^
 
 error: expressions must be enclosed in braces to be used as const generic arguments
-  --> $DIR/disallowed-positions.rs:337:9
+  --> $DIR/disallowed-positions.rs:406:9
    |
 LL |         true && let 1 = 1
    |         ^^^^^^^^^^^^^^^^^
@@ -118,25 +358,79 @@ LL |         { true && let 1 = 1 }
    |         +                   +
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:384:22
+  --> $DIR/disallowed-positions.rs:415:9
+   |
+LL |     if (let Some(a) = opt && true) {
+   |         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:420:9
+   |
+LL |     if (let Some(a) = opt) && true {
+   |         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:424:9
+   |
+LL |     if (let Some(a) = opt) && (let Some(b) = a) {
+   |         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:424:32
+   |
+LL |     if (let Some(a) = opt) && (let Some(b) = a) {
+   |                                ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:433:9
+   |
+LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
+   |         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:433:31
+   |
+LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
+   |                               ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:439:9
+   |
+LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
+   |         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:439:31
+   |
+LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
+   |                               ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:445:9
+   |
+LL |     if (let Some(a) = opt && (true)) && true {
+   |         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/disallowed-positions.rs:462:22
    |
 LL |     let x = (true && let y = 1);
    |                      ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:389:20
+  --> $DIR/disallowed-positions.rs:467:20
    |
 LL |         ([1, 2, 3][let _ = ()])
    |                    ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:81:16
+  --> $DIR/disallowed-positions.rs:95:16
    |
 LL |     use_expr!((let 0 = 1 && 0 == 0));
    |                ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/disallowed-positions.rs:85:16
+  --> $DIR/disallowed-positions.rs:99:16
    |
 LL |     use_expr!((let 0 = 1));
    |                ^^^
@@ -155,280 +449,280 @@ LL |     if (let 0 = 1) {}
    |         ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:32:11
+  --> $DIR/disallowed-positions.rs:33:11
    |
 LL |     if (((let 0 = 1))) {}
    |           ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:32:11
+  --> $DIR/disallowed-positions.rs:33:11
    |
 LL |     if (((let 0 = 1))) {}
    |           ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:35:9
+  --> $DIR/disallowed-positions.rs:37:9
    |
 LL |     if (let 0 = 1) && true {}
    |         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:35:9
+  --> $DIR/disallowed-positions.rs:37:9
    |
 LL |     if (let 0 = 1) && true {}
    |         ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:38:17
+  --> $DIR/disallowed-positions.rs:41:17
    |
 LL |     if true && (let 0 = 1) {}
    |                 ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:38:17
+  --> $DIR/disallowed-positions.rs:41:17
    |
 LL |     if true && (let 0 = 1) {}
    |                 ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:41:9
+  --> $DIR/disallowed-positions.rs:45:9
    |
 LL |     if (let 0 = 1) && (let 0 = 1) {}
    |         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:41:9
+  --> $DIR/disallowed-positions.rs:45:9
    |
 LL |     if (let 0 = 1) && (let 0 = 1) {}
    |         ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:41:24
+  --> $DIR/disallowed-positions.rs:45:24
    |
 LL |     if (let 0 = 1) && (let 0 = 1) {}
    |                        ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:41:24
+  --> $DIR/disallowed-positions.rs:45:24
    |
 LL |     if (let 0 = 1) && (let 0 = 1) {}
    |                        ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:45:35
+  --> $DIR/disallowed-positions.rs:51:35
    |
 LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                   ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:45:35
+  --> $DIR/disallowed-positions.rs:51:35
    |
 LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:45:48
+  --> $DIR/disallowed-positions.rs:51:48
    |
 LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                                ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:45:35
+  --> $DIR/disallowed-positions.rs:51:35
    |
 LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:45:61
+  --> $DIR/disallowed-positions.rs:51:61
    |
 LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                                             ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:45:35
+  --> $DIR/disallowed-positions.rs:51:35
    |
 LL |     if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:52:12
+  --> $DIR/disallowed-positions.rs:59:12
    |
 LL |     while (let 0 = 1) {}
    |            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:52:12
+  --> $DIR/disallowed-positions.rs:59:12
    |
 LL |     while (let 0 = 1) {}
    |            ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:55:14
+  --> $DIR/disallowed-positions.rs:63:14
    |
 LL |     while (((let 0 = 1))) {}
    |              ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:55:14
+  --> $DIR/disallowed-positions.rs:63:14
    |
 LL |     while (((let 0 = 1))) {}
    |              ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:58:12
+  --> $DIR/disallowed-positions.rs:67:12
    |
 LL |     while (let 0 = 1) && true {}
    |            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:58:12
+  --> $DIR/disallowed-positions.rs:67:12
    |
 LL |     while (let 0 = 1) && true {}
    |            ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:61:20
+  --> $DIR/disallowed-positions.rs:71:20
    |
 LL |     while true && (let 0 = 1) {}
    |                    ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:61:20
+  --> $DIR/disallowed-positions.rs:71:20
    |
 LL |     while true && (let 0 = 1) {}
    |                    ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:64:12
+  --> $DIR/disallowed-positions.rs:75:12
    |
 LL |     while (let 0 = 1) && (let 0 = 1) {}
    |            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:64:12
+  --> $DIR/disallowed-positions.rs:75:12
    |
 LL |     while (let 0 = 1) && (let 0 = 1) {}
    |            ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:64:27
+  --> $DIR/disallowed-positions.rs:75:27
    |
 LL |     while (let 0 = 1) && (let 0 = 1) {}
    |                           ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:64:27
+  --> $DIR/disallowed-positions.rs:75:27
    |
 LL |     while (let 0 = 1) && (let 0 = 1) {}
    |                           ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:68:38
+  --> $DIR/disallowed-positions.rs:81:38
    |
 LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                      ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:68:38
+  --> $DIR/disallowed-positions.rs:81:38
    |
 LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:68:51
+  --> $DIR/disallowed-positions.rs:81:51
    |
 LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                                   ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:68:38
+  --> $DIR/disallowed-positions.rs:81:38
    |
 LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:68:64
+  --> $DIR/disallowed-positions.rs:81:64
    |
 LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                                                ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:68:38
+  --> $DIR/disallowed-positions.rs:81:38
    |
 LL |     while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:81:16
+  --> $DIR/disallowed-positions.rs:95:16
    |
 LL |     use_expr!((let 0 = 1 && 0 == 0));
    |                ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:81:16
+  --> $DIR/disallowed-positions.rs:95:16
    |
 LL |     use_expr!((let 0 = 1 && 0 == 0));
    |                ^^^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:81:16
+  --> $DIR/disallowed-positions.rs:95:16
    |
 LL |     use_expr!((let 0 = 1 && 0 == 0));
    |                ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:81:16
+  --> $DIR/disallowed-positions.rs:95:16
    |
 LL |     use_expr!((let 0 = 1 && 0 == 0));
    |                ^^^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:85:16
+  --> $DIR/disallowed-positions.rs:99:16
    |
 LL |     use_expr!((let 0 = 1));
    |                ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:85:16
+  --> $DIR/disallowed-positions.rs:99:16
    |
 LL |     use_expr!((let 0 = 1));
    |                ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:85:16
+  --> $DIR/disallowed-positions.rs:99:16
    |
 LL |     use_expr!((let 0 = 1));
    |                ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:85:16
+  --> $DIR/disallowed-positions.rs:99:16
    |
 LL |     use_expr!((let 0 = 1));
    |                ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:92:9
+  --> $DIR/disallowed-positions.rs:106:9
    |
 LL |     if &let 0 = 0 {}
    |         ^^^^^^^^^
@@ -436,7 +730,7 @@ LL |     if &let 0 = 0 {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:95:9
+  --> $DIR/disallowed-positions.rs:111:9
    |
 LL |     if !let 0 = 0 {}
    |         ^^^^^^^^^
@@ -444,7 +738,7 @@ LL |     if !let 0 = 0 {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:96:9
+  --> $DIR/disallowed-positions.rs:114:9
    |
 LL |     if *let 0 = 0 {}
    |         ^^^^^^^^^
@@ -452,7 +746,7 @@ LL |     if *let 0 = 0 {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:98:9
+  --> $DIR/disallowed-positions.rs:118:9
    |
 LL |     if -let 0 = 0 {}
    |         ^^^^^^^^^
@@ -460,72 +754,72 @@ LL |     if -let 0 = 0 {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:106:9
+  --> $DIR/disallowed-positions.rs:128:9
    |
 LL |     if (let 0 = 0)? {}
    |         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:106:9
+  --> $DIR/disallowed-positions.rs:128:9
    |
 LL |     if (let 0 = 0)? {}
    |         ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:110:16
+  --> $DIR/disallowed-positions.rs:134:16
    |
 LL |     if true || let 0 = 0 {}
    |                ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:110:13
+  --> $DIR/disallowed-positions.rs:134:13
    |
 LL |     if true || let 0 = 0 {}
    |             ^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:111:17
+  --> $DIR/disallowed-positions.rs:137:17
    |
 LL |     if (true || let 0 = 0) {}
    |                 ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:111:14
+  --> $DIR/disallowed-positions.rs:137:14
    |
 LL |     if (true || let 0 = 0) {}
    |              ^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:112:25
+  --> $DIR/disallowed-positions.rs:140:25
    |
 LL |     if true && (true || let 0 = 0) {}
    |                         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:112:22
+  --> $DIR/disallowed-positions.rs:140:22
    |
 LL |     if true && (true || let 0 = 0) {}
    |                      ^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:113:25
+  --> $DIR/disallowed-positions.rs:143:25
    |
 LL |     if true || (true && let 0 = 0) {}
    |                         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:113:17
+  --> $DIR/disallowed-positions.rs:143:17
    |
 LL |     if true || (true && let 0 = 0) {}
    |                 ^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:116:12
+  --> $DIR/disallowed-positions.rs:147:12
    |
 LL |     if x = let 0 = 0 {}
    |            ^^^^^^^^^
@@ -533,46 +827,46 @@ LL |     if x = let 0 = 0 {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:119:15
+  --> $DIR/disallowed-positions.rs:152:15
    |
 LL |     if true..(let 0 = 0) {}
    |               ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:119:15
+  --> $DIR/disallowed-positions.rs:152:15
    |
 LL |     if true..(let 0 = 0) {}
    |               ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:121:11
+  --> $DIR/disallowed-positions.rs:156:11
    |
 LL |     if ..(let 0 = 0) {}
    |           ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:121:11
+  --> $DIR/disallowed-positions.rs:156:11
    |
 LL |     if ..(let 0 = 0) {}
    |           ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:123:9
+  --> $DIR/disallowed-positions.rs:160:9
    |
 LL |     if (let 0 = 0).. {}
    |         ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:123:9
+  --> $DIR/disallowed-positions.rs:160:9
    |
 LL |     if (let 0 = 0).. {}
    |         ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:127:8
+  --> $DIR/disallowed-positions.rs:166:8
    |
 LL |     if let Range { start: _, end: _ } = true..true && false {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -580,7 +874,7 @@ LL |     if let Range { start: _, end: _ } = true..true && false {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:131:8
+  --> $DIR/disallowed-positions.rs:170:8
    |
 LL |     if let Range { start: _, end: _ } = true..true || false {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -588,7 +882,7 @@ LL |     if let Range { start: _, end: _ } = true..true || false {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:138:8
+  --> $DIR/disallowed-positions.rs:177:8
    |
 LL |     if let Range { start: F, end } = F..|| true {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -596,7 +890,7 @@ LL |     if let Range { start: F, end } = F..|| true {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:146:8
+  --> $DIR/disallowed-positions.rs:185:8
    |
 LL |     if let Range { start: true, end } = t..&&false {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -604,7 +898,7 @@ LL |     if let Range { start: true, end } = t..&&false {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:152:19
+  --> $DIR/disallowed-positions.rs:191:19
    |
 LL |     if let true = let true = true {}
    |                   ^^^^^^^^^^^^^^^
@@ -612,7 +906,7 @@ LL |     if let true = let true = true {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:157:12
+  --> $DIR/disallowed-positions.rs:197:12
    |
 LL |     while &let 0 = 0 {}
    |            ^^^^^^^^^
@@ -620,7 +914,7 @@ LL |     while &let 0 = 0 {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:160:12
+  --> $DIR/disallowed-positions.rs:202:12
    |
 LL |     while !let 0 = 0 {}
    |            ^^^^^^^^^
@@ -628,7 +922,7 @@ LL |     while !let 0 = 0 {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:161:12
+  --> $DIR/disallowed-positions.rs:205:12
    |
 LL |     while *let 0 = 0 {}
    |            ^^^^^^^^^
@@ -636,7 +930,7 @@ LL |     while *let 0 = 0 {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:163:12
+  --> $DIR/disallowed-positions.rs:209:12
    |
 LL |     while -let 0 = 0 {}
    |            ^^^^^^^^^
@@ -644,72 +938,72 @@ LL |     while -let 0 = 0 {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:171:12
+  --> $DIR/disallowed-positions.rs:219:12
    |
 LL |     while (let 0 = 0)? {}
    |            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:171:12
+  --> $DIR/disallowed-positions.rs:219:12
    |
 LL |     while (let 0 = 0)? {}
    |            ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:175:19
+  --> $DIR/disallowed-positions.rs:225:19
    |
 LL |     while true || let 0 = 0 {}
    |                   ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:175:16
+  --> $DIR/disallowed-positions.rs:225:16
    |
 LL |     while true || let 0 = 0 {}
    |                ^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:176:20
+  --> $DIR/disallowed-positions.rs:228:20
    |
 LL |     while (true || let 0 = 0) {}
    |                    ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:176:17
+  --> $DIR/disallowed-positions.rs:228:17
    |
 LL |     while (true || let 0 = 0) {}
    |                 ^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:177:28
+  --> $DIR/disallowed-positions.rs:231:28
    |
 LL |     while true && (true || let 0 = 0) {}
    |                            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:177:25
+  --> $DIR/disallowed-positions.rs:231:25
    |
 LL |     while true && (true || let 0 = 0) {}
    |                         ^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:178:28
+  --> $DIR/disallowed-positions.rs:234:28
    |
 LL |     while true || (true && let 0 = 0) {}
    |                            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:178:20
+  --> $DIR/disallowed-positions.rs:234:20
    |
 LL |     while true || (true && let 0 = 0) {}
    |                    ^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:181:15
+  --> $DIR/disallowed-positions.rs:238:15
    |
 LL |     while x = let 0 = 0 {}
    |               ^^^^^^^^^
@@ -717,46 +1011,46 @@ LL |     while x = let 0 = 0 {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:184:18
+  --> $DIR/disallowed-positions.rs:243:18
    |
 LL |     while true..(let 0 = 0) {}
    |                  ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:184:18
+  --> $DIR/disallowed-positions.rs:243:18
    |
 LL |     while true..(let 0 = 0) {}
    |                  ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:186:14
+  --> $DIR/disallowed-positions.rs:247:14
    |
 LL |     while ..(let 0 = 0) {}
    |              ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:186:14
+  --> $DIR/disallowed-positions.rs:247:14
    |
 LL |     while ..(let 0 = 0) {}
    |              ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:188:12
+  --> $DIR/disallowed-positions.rs:251:12
    |
 LL |     while (let 0 = 0).. {}
    |            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:188:12
+  --> $DIR/disallowed-positions.rs:251:12
    |
 LL |     while (let 0 = 0).. {}
    |            ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:192:11
+  --> $DIR/disallowed-positions.rs:257:11
    |
 LL |     while let Range { start: _, end: _ } = true..true && false {}
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -764,7 +1058,7 @@ LL |     while let Range { start: _, end: _ } = true..true && false {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:196:11
+  --> $DIR/disallowed-positions.rs:261:11
    |
 LL |     while let Range { start: _, end: _ } = true..true || false {}
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -772,7 +1066,7 @@ LL |     while let Range { start: _, end: _ } = true..true || false {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:203:11
+  --> $DIR/disallowed-positions.rs:268:11
    |
 LL |     while let Range { start: F, end } = F..|| true {}
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -780,7 +1074,7 @@ LL |     while let Range { start: F, end } = F..|| true {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:211:11
+  --> $DIR/disallowed-positions.rs:276:11
    |
 LL |     while let Range { start: true, end } = t..&&false {}
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -788,7 +1082,7 @@ LL |     while let Range { start: true, end } = t..&&false {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:217:22
+  --> $DIR/disallowed-positions.rs:282:22
    |
 LL |     while let true = let true = true {}
    |                      ^^^^^^^^^^^^^^^
@@ -796,7 +1090,7 @@ LL |     while let true = let true = true {}
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:232:6
+  --> $DIR/disallowed-positions.rs:298:6
    |
 LL |     &let 0 = 0;
    |      ^^^^^^^^^
@@ -804,7 +1098,7 @@ LL |     &let 0 = 0;
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:236:6
+  --> $DIR/disallowed-positions.rs:302:6
    |
 LL |     !let 0 = 0;
    |      ^^^^^^^^^
@@ -812,7 +1106,7 @@ LL |     !let 0 = 0;
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:239:6
+  --> $DIR/disallowed-positions.rs:305:6
    |
 LL |     *let 0 = 0;
    |      ^^^^^^^^^
@@ -820,7 +1114,7 @@ LL |     *let 0 = 0;
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:243:6
+  --> $DIR/disallowed-positions.rs:309:6
    |
 LL |     -let 0 = 0;
    |      ^^^^^^^^^
@@ -828,59 +1122,59 @@ LL |     -let 0 = 0;
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:253:6
+  --> $DIR/disallowed-positions.rs:319:6
    |
 LL |     (let 0 = 0)?;
    |      ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:253:6
+  --> $DIR/disallowed-positions.rs:319:6
    |
 LL |     (let 0 = 0)?;
    |      ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:259:13
+  --> $DIR/disallowed-positions.rs:325:13
    |
 LL |     true || let 0 = 0;
    |             ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:259:10
+  --> $DIR/disallowed-positions.rs:325:10
    |
 LL |     true || let 0 = 0;
    |          ^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:262:14
+  --> $DIR/disallowed-positions.rs:328:14
    |
 LL |     (true || let 0 = 0);
    |              ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:262:11
+  --> $DIR/disallowed-positions.rs:328:11
    |
 LL |     (true || let 0 = 0);
    |           ^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:265:22
+  --> $DIR/disallowed-positions.rs:331:22
    |
 LL |     true && (true || let 0 = 0);
    |                      ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `||` operators are not supported in let chain expressions
-  --> $DIR/disallowed-positions.rs:265:19
+  --> $DIR/disallowed-positions.rs:331:19
    |
 LL |     true && (true || let 0 = 0);
    |                   ^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:270:9
+  --> $DIR/disallowed-positions.rs:336:9
    |
 LL |     x = let 0 = 0;
    |         ^^^^^^^^^
@@ -888,46 +1182,46 @@ LL |     x = let 0 = 0;
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:274:12
+  --> $DIR/disallowed-positions.rs:340:12
    |
 LL |     true..(let 0 = 0);
    |            ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:274:12
+  --> $DIR/disallowed-positions.rs:340:12
    |
 LL |     true..(let 0 = 0);
    |            ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:277:8
+  --> $DIR/disallowed-positions.rs:343:8
    |
 LL |     ..(let 0 = 0);
    |        ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:277:8
+  --> $DIR/disallowed-positions.rs:343:8
    |
 LL |     ..(let 0 = 0);
    |        ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:280:6
+  --> $DIR/disallowed-positions.rs:346:6
    |
 LL |     (let 0 = 0)..;
    |      ^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:280:6
+  --> $DIR/disallowed-positions.rs:346:6
    |
 LL |     (let 0 = 0)..;
    |      ^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:284:6
+  --> $DIR/disallowed-positions.rs:350:6
    |
 LL |     (let Range { start: _, end: _ } = true..true || false);
    |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -935,20 +1229,20 @@ LL |     (let Range { start: _, end: _ } = true..true || false);
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:289:6
+  --> $DIR/disallowed-positions.rs:355:6
    |
 LL |     (let true = let true = true);
    |      ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:289:6
+  --> $DIR/disallowed-positions.rs:355:6
    |
 LL |     (let true = let true = true);
    |      ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:307:6
+  --> $DIR/disallowed-positions.rs:373:6
    |
 LL |     &let 0 = 0
    |      ^^^^^^^^^
@@ -956,7 +1250,7 @@ LL |     &let 0 = 0
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:319:17
+  --> $DIR/disallowed-positions.rs:385:17
    |
 LL |         true && let 1 = 1
    |                 ^^^^^^^^^
@@ -964,7 +1258,7 @@ LL |         true && let 1 = 1
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:323:17
+  --> $DIR/disallowed-positions.rs:390:17
    |
 LL |         true && let 1 = 1
    |                 ^^^^^^^^^
@@ -972,7 +1266,7 @@ LL |         true && let 1 = 1
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:327:17
+  --> $DIR/disallowed-positions.rs:395:17
    |
 LL |         true && let 1 = 1
    |                 ^^^^^^^^^
@@ -980,7 +1274,7 @@ LL |         true && let 1 = 1
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:337:17
+  --> $DIR/disallowed-positions.rs:406:17
    |
 LL |         true && let 1 = 1
    |                 ^^^^^^^^^
@@ -988,124 +1282,124 @@ LL |         true && let 1 = 1
    = note: only supported directly in conditions of `if` and `while` expressions
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:346:9
+  --> $DIR/disallowed-positions.rs:415:9
    |
 LL |     if (let Some(a) = opt && true) {
    |         ^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:346:9
+  --> $DIR/disallowed-positions.rs:415:9
    |
 LL |     if (let Some(a) = opt && true) {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:350:9
+  --> $DIR/disallowed-positions.rs:420:9
    |
 LL |     if (let Some(a) = opt) && true {
    |         ^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:350:9
+  --> $DIR/disallowed-positions.rs:420:9
    |
 LL |     if (let Some(a) = opt) && true {
    |         ^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:353:9
+  --> $DIR/disallowed-positions.rs:424:9
    |
 LL |     if (let Some(a) = opt) && (let Some(b) = a) {
    |         ^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:353:9
+  --> $DIR/disallowed-positions.rs:424:9
    |
 LL |     if (let Some(a) = opt) && (let Some(b) = a) {
    |         ^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:353:32
+  --> $DIR/disallowed-positions.rs:424:32
    |
 LL |     if (let Some(a) = opt) && (let Some(b) = a) {
    |                                ^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:353:32
+  --> $DIR/disallowed-positions.rs:424:32
    |
 LL |     if (let Some(a) = opt) && (let Some(b) = a) {
    |                                ^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:360:9
+  --> $DIR/disallowed-positions.rs:433:9
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
    |         ^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:360:9
+  --> $DIR/disallowed-positions.rs:433:9
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:360:31
+  --> $DIR/disallowed-positions.rs:433:31
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
    |                               ^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:360:31
+  --> $DIR/disallowed-positions.rs:433:31
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && b == 1 {
    |                               ^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:364:9
+  --> $DIR/disallowed-positions.rs:439:9
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
    |         ^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:364:9
+  --> $DIR/disallowed-positions.rs:439:9
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:364:31
+  --> $DIR/disallowed-positions.rs:439:31
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
    |                               ^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:364:31
+  --> $DIR/disallowed-positions.rs:439:31
    |
 LL |     if (let Some(a) = opt && (let Some(b) = a)) && true {
    |                               ^^^^^^^^^^^^^^^
 
 error: `let` expressions are not supported here
-  --> $DIR/disallowed-positions.rs:368:9
+  --> $DIR/disallowed-positions.rs:445:9
    |
 LL |     if (let Some(a) = opt && (true)) && true {
    |         ^^^^^^^^^^^^^^^^^
    |
    = note: only supported directly in conditions of `if` and `while` expressions
 note: `let`s wrapped in parentheses are not supported in a context with let chains
-  --> $DIR/disallowed-positions.rs:368:9
+  --> $DIR/disallowed-positions.rs:445:9
    |
 LL |     if (let Some(a) = opt && (true)) && true {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:92:8
+  --> $DIR/disallowed-positions.rs:106:8
    |
 LL |     if &let 0 = 0 {}
    |        ^^^^^^^^^^ expected `bool`, found `&bool`
@@ -1117,19 +1411,19 @@ LL +     if let 0 = 0 {}
    |
 
 error[E0614]: type `bool` cannot be dereferenced
-  --> $DIR/disallowed-positions.rs:96:8
+  --> $DIR/disallowed-positions.rs:114:8
    |
 LL |     if *let 0 = 0 {}
    |        ^^^^^^^^^^
 
 error[E0600]: cannot apply unary operator `-` to type `bool`
-  --> $DIR/disallowed-positions.rs:98:8
+  --> $DIR/disallowed-positions.rs:118:8
    |
 LL |     if -let 0 = 0 {}
    |        ^^^^^^^^^^ cannot apply unary operator `-`
 
 error[E0277]: the `?` operator can only be applied to values that implement `Try`
-  --> $DIR/disallowed-positions.rs:106:8
+  --> $DIR/disallowed-positions.rs:128:8
    |
 LL |     if (let 0 = 0)? {}
    |        ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool`
@@ -1137,7 +1431,7 @@ LL |     if (let 0 = 0)? {}
    = help: the trait `Try` is not implemented for `bool`
 
 error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
-  --> $DIR/disallowed-positions.rs:106:19
+  --> $DIR/disallowed-positions.rs:128:19
    |
 LL | / fn nested_within_if_expr() {
 LL | |     if &let 0 = 0 {}
@@ -1154,7 +1448,7 @@ LL | | }
    = help: the trait `FromResidual<_>` is not implemented for `()`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:116:8
+  --> $DIR/disallowed-positions.rs:147:8
    |
 LL |     if x = let 0 = 0 {}
    |        ^^^^^^^^^^^^^ expected `bool`, found `()`
@@ -1165,7 +1459,7 @@ LL |     if x == let 0 = 0 {}
    |          ~~
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:119:8
+  --> $DIR/disallowed-positions.rs:152:8
    |
 LL |     if true..(let 0 = 0) {}
    |        ^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
@@ -1174,7 +1468,7 @@ LL |     if true..(let 0 = 0) {}
             found struct `std::ops::Range<bool>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:121:8
+  --> $DIR/disallowed-positions.rs:156:8
    |
 LL |     if ..(let 0 = 0) {}
    |        ^^^^^^^^^^^^^ expected `bool`, found struct `RangeTo`
@@ -1183,7 +1477,7 @@ LL |     if ..(let 0 = 0) {}
             found struct `RangeTo<bool>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:123:8
+  --> $DIR/disallowed-positions.rs:160:8
    |
 LL |     if (let 0 = 0).. {}
    |        ^^^^^^^^^^^^^ expected `bool`, found struct `RangeFrom`
@@ -1192,7 +1486,7 @@ LL |     if (let 0 = 0).. {}
             found struct `RangeFrom<bool>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:127:12
+  --> $DIR/disallowed-positions.rs:166:12
    |
 LL |     if let Range { start: _, end: _ } = true..true && false {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
@@ -1203,7 +1497,7 @@ LL |     if let Range { start: _, end: _ } = true..true && false {}
             found struct `std::ops::Range<_>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:127:8
+  --> $DIR/disallowed-positions.rs:166:8
    |
 LL |     if let Range { start: _, end: _ } = true..true && false {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
@@ -1212,7 +1506,7 @@ LL |     if let Range { start: _, end: _ } = true..true && false {}
             found struct `std::ops::Range<bool>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:131:12
+  --> $DIR/disallowed-positions.rs:170:12
    |
 LL |     if let Range { start: _, end: _ } = true..true || false {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
@@ -1223,7 +1517,7 @@ LL |     if let Range { start: _, end: _ } = true..true || false {}
             found struct `std::ops::Range<_>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:131:8
+  --> $DIR/disallowed-positions.rs:170:8
    |
 LL |     if let Range { start: _, end: _ } = true..true || false {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
@@ -1232,7 +1526,7 @@ LL |     if let Range { start: _, end: _ } = true..true || false {}
             found struct `std::ops::Range<bool>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:138:12
+  --> $DIR/disallowed-positions.rs:177:12
    |
 LL |     if let Range { start: F, end } = F..|| true {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^   - this expression has type `fn() -> bool`
@@ -1243,20 +1537,20 @@ LL |     if let Range { start: F, end } = F..|| true {}
                   found struct `std::ops::Range<_>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:138:41
+  --> $DIR/disallowed-positions.rs:177:41
    |
 LL |     if let Range { start: F, end } = F..|| true {}
    |                                         ^^^^^^^ expected `bool`, found closure
    |
    = note: expected type `bool`
-           found closure `[closure@$DIR/disallowed-positions.rs:138:41: 138:43]`
+           found closure `[closure@$DIR/disallowed-positions.rs:177:41: 177:43]`
 help: use parentheses to call this closure
    |
 LL |     if let Range { start: F, end } = F..(|| true)() {}
    |                                         +       +++
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:138:8
+  --> $DIR/disallowed-positions.rs:177:8
    |
 LL |     if let Range { start: F, end } = F..|| true {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
@@ -1265,7 +1559,7 @@ LL |     if let Range { start: F, end } = F..|| true {}
             found struct `std::ops::Range<bool>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:146:12
+  --> $DIR/disallowed-positions.rs:185:12
    |
 LL |     if let Range { start: true, end } = t..&&false {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^   - this expression has type `&&bool`
@@ -1276,7 +1570,7 @@ LL |     if let Range { start: true, end } = t..&&false {}
             found struct `std::ops::Range<_>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:146:44
+  --> $DIR/disallowed-positions.rs:185:44
    |
 LL |     if let Range { start: true, end } = t..&&false {}
    |                                            ^^^^^^^ expected `bool`, found `&&bool`
@@ -1288,7 +1582,7 @@ LL +     if let Range { start: true, end } = t..false {}
    |
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:146:8
+  --> $DIR/disallowed-positions.rs:185:8
    |
 LL |     if let Range { start: true, end } = t..&&false {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
@@ -1297,7 +1591,7 @@ LL |     if let Range { start: true, end } = t..&&false {}
             found struct `std::ops::Range<bool>`
 
 error[E0277]: the `?` operator can only be applied to values that implement `Try`
-  --> $DIR/disallowed-positions.rs:102:20
+  --> $DIR/disallowed-positions.rs:124:20
    |
 LL |         if let 0 = 0? {}
    |                    ^^ the `?` operator cannot be applied to type `{integer}`
@@ -1305,7 +1599,7 @@ LL |         if let 0 = 0? {}
    = help: the trait `Try` is not implemented for `{integer}`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:157:11
+  --> $DIR/disallowed-positions.rs:197:11
    |
 LL |     while &let 0 = 0 {}
    |           ^^^^^^^^^^ expected `bool`, found `&bool`
@@ -1317,19 +1611,19 @@ LL +     while let 0 = 0 {}
    |
 
 error[E0614]: type `bool` cannot be dereferenced
-  --> $DIR/disallowed-positions.rs:161:11
+  --> $DIR/disallowed-positions.rs:205:11
    |
 LL |     while *let 0 = 0 {}
    |           ^^^^^^^^^^
 
 error[E0600]: cannot apply unary operator `-` to type `bool`
-  --> $DIR/disallowed-positions.rs:163:11
+  --> $DIR/disallowed-positions.rs:209:11
    |
 LL |     while -let 0 = 0 {}
    |           ^^^^^^^^^^ cannot apply unary operator `-`
 
 error[E0277]: the `?` operator can only be applied to values that implement `Try`
-  --> $DIR/disallowed-positions.rs:171:11
+  --> $DIR/disallowed-positions.rs:219:11
    |
 LL |     while (let 0 = 0)? {}
    |           ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool`
@@ -1337,7 +1631,7 @@ LL |     while (let 0 = 0)? {}
    = help: the trait `Try` is not implemented for `bool`
 
 error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
-  --> $DIR/disallowed-positions.rs:171:22
+  --> $DIR/disallowed-positions.rs:219:22
    |
 LL | / fn nested_within_while_expr() {
 LL | |     while &let 0 = 0 {}
@@ -1354,7 +1648,7 @@ LL | | }
    = help: the trait `FromResidual<_>` is not implemented for `()`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:181:11
+  --> $DIR/disallowed-positions.rs:238:11
    |
 LL |     while x = let 0 = 0 {}
    |           ^^^^^^^^^^^^^ expected `bool`, found `()`
@@ -1365,7 +1659,7 @@ LL |     while x == let 0 = 0 {}
    |             ~~
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:184:11
+  --> $DIR/disallowed-positions.rs:243:11
    |
 LL |     while true..(let 0 = 0) {}
    |           ^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
@@ -1374,7 +1668,7 @@ LL |     while true..(let 0 = 0) {}
             found struct `std::ops::Range<bool>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:186:11
+  --> $DIR/disallowed-positions.rs:247:11
    |
 LL |     while ..(let 0 = 0) {}
    |           ^^^^^^^^^^^^^ expected `bool`, found struct `RangeTo`
@@ -1383,7 +1677,7 @@ LL |     while ..(let 0 = 0) {}
             found struct `RangeTo<bool>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:188:11
+  --> $DIR/disallowed-positions.rs:251:11
    |
 LL |     while (let 0 = 0).. {}
    |           ^^^^^^^^^^^^^ expected `bool`, found struct `RangeFrom`
@@ -1392,7 +1686,7 @@ LL |     while (let 0 = 0).. {}
             found struct `RangeFrom<bool>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:192:15
+  --> $DIR/disallowed-positions.rs:257:15
    |
 LL |     while let Range { start: _, end: _ } = true..true && false {}
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
@@ -1403,7 +1697,7 @@ LL |     while let Range { start: _, end: _ } = true..true && false {}
             found struct `std::ops::Range<_>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:192:11
+  --> $DIR/disallowed-positions.rs:257:11
    |
 LL |     while let Range { start: _, end: _ } = true..true && false {}
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
@@ -1412,7 +1706,7 @@ LL |     while let Range { start: _, end: _ } = true..true && false {}
             found struct `std::ops::Range<bool>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:196:15
+  --> $DIR/disallowed-positions.rs:261:15
    |
 LL |     while let Range { start: _, end: _ } = true..true || false {}
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
@@ -1423,7 +1717,7 @@ LL |     while let Range { start: _, end: _ } = true..true || false {}
             found struct `std::ops::Range<_>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:196:11
+  --> $DIR/disallowed-positions.rs:261:11
    |
 LL |     while let Range { start: _, end: _ } = true..true || false {}
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
@@ -1432,7 +1726,7 @@ LL |     while let Range { start: _, end: _ } = true..true || false {}
             found struct `std::ops::Range<bool>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:203:15
+  --> $DIR/disallowed-positions.rs:268:15
    |
 LL |     while let Range { start: F, end } = F..|| true {}
    |               ^^^^^^^^^^^^^^^^^^^^^^^   - this expression has type `fn() -> bool`
@@ -1443,20 +1737,20 @@ LL |     while let Range { start: F, end } = F..|| true {}
                   found struct `std::ops::Range<_>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:203:44
+  --> $DIR/disallowed-positions.rs:268:44
    |
 LL |     while let Range { start: F, end } = F..|| true {}
    |                                            ^^^^^^^ expected `bool`, found closure
    |
    = note: expected type `bool`
-           found closure `[closure@$DIR/disallowed-positions.rs:203:44: 203:46]`
+           found closure `[closure@$DIR/disallowed-positions.rs:268:44: 268:46]`
 help: use parentheses to call this closure
    |
 LL |     while let Range { start: F, end } = F..(|| true)() {}
    |                                            +       +++
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:203:11
+  --> $DIR/disallowed-positions.rs:268:11
    |
 LL |     while let Range { start: F, end } = F..|| true {}
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
@@ -1465,7 +1759,7 @@ LL |     while let Range { start: F, end } = F..|| true {}
             found struct `std::ops::Range<bool>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:211:15
+  --> $DIR/disallowed-positions.rs:276:15
    |
 LL |     while let Range { start: true, end } = t..&&false {}
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^   - this expression has type `&&bool`
@@ -1476,7 +1770,7 @@ LL |     while let Range { start: true, end } = t..&&false {}
             found struct `std::ops::Range<_>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:211:47
+  --> $DIR/disallowed-positions.rs:276:47
    |
 LL |     while let Range { start: true, end } = t..&&false {}
    |                                               ^^^^^^^ expected `bool`, found `&&bool`
@@ -1488,7 +1782,7 @@ LL +     while let Range { start: true, end } = t..false {}
    |
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:211:11
+  --> $DIR/disallowed-positions.rs:276:11
    |
 LL |     while let Range { start: true, end } = t..&&false {}
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
@@ -1497,7 +1791,7 @@ LL |     while let Range { start: true, end } = t..&&false {}
             found struct `std::ops::Range<bool>`
 
 error[E0277]: the `?` operator can only be applied to values that implement `Try`
-  --> $DIR/disallowed-positions.rs:167:23
+  --> $DIR/disallowed-positions.rs:215:23
    |
 LL |         while let 0 = 0? {}
    |                       ^^ the `?` operator cannot be applied to type `{integer}`
@@ -1505,19 +1799,19 @@ LL |         while let 0 = 0? {}
    = help: the trait `Try` is not implemented for `{integer}`
 
 error[E0614]: type `bool` cannot be dereferenced
-  --> $DIR/disallowed-positions.rs:239:5
+  --> $DIR/disallowed-positions.rs:305:5
    |
 LL |     *let 0 = 0;
    |     ^^^^^^^^^^
 
 error[E0600]: cannot apply unary operator `-` to type `bool`
-  --> $DIR/disallowed-positions.rs:243:5
+  --> $DIR/disallowed-positions.rs:309:5
    |
 LL |     -let 0 = 0;
    |     ^^^^^^^^^^ cannot apply unary operator `-`
 
 error[E0277]: the `?` operator can only be applied to values that implement `Try`
-  --> $DIR/disallowed-positions.rs:253:5
+  --> $DIR/disallowed-positions.rs:319:5
    |
 LL |     (let 0 = 0)?;
    |     ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool`
@@ -1525,7 +1819,7 @@ LL |     (let 0 = 0)?;
    = help: the trait `Try` is not implemented for `bool`
 
 error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
-  --> $DIR/disallowed-positions.rs:253:16
+  --> $DIR/disallowed-positions.rs:319:16
    |
 LL | / fn outside_if_and_while_expr() {
 LL | |     &let 0 = 0;
@@ -1542,7 +1836,7 @@ LL | | }
    = help: the trait `FromResidual<_>` is not implemented for `()`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:284:10
+  --> $DIR/disallowed-positions.rs:350:10
    |
 LL |     (let Range { start: _, end: _ } = true..true || false);
    |          ^^^^^^^^^^^^^^^^^^^^^^^^^^   ---- this expression has type `bool`
@@ -1553,7 +1847,7 @@ LL |     (let Range { start: _, end: _ } = true..true || false);
             found struct `std::ops::Range<_>`
 
 error[E0308]: mismatched types
-  --> $DIR/disallowed-positions.rs:307:5
+  --> $DIR/disallowed-positions.rs:373:5
    |
 LL | fn outside_if_and_while_expr() {
    |                                - help: try adding a return type: `-> &bool`
@@ -1562,14 +1856,14 @@ LL |     &let 0 = 0
    |     ^^^^^^^^^^ expected `()`, found `&bool`
 
 error[E0277]: the `?` operator can only be applied to values that implement `Try`
-  --> $DIR/disallowed-positions.rs:249:17
+  --> $DIR/disallowed-positions.rs:315:17
    |
 LL |         let 0 = 0?;
    |                 ^^ the `?` operator cannot be applied to type `{integer}`
    |
    = help: the trait `Try` is not implemented for `{integer}`
 
-error: aborting due to 156 previous errors
+error: aborting due to 205 previous errors
 
 Some errors have detailed explanations: E0277, E0308, E0600, E0614.
 For more information about an error, try `rustc --explain E0277`.
diff --git a/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs b/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs
index 87718211308..2b407ef510c 100644
--- a/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs
+++ b/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs
@@ -19,6 +19,11 @@ fn _if() {
 
     if let Range { start: _, end: _ } = (true..true) && false {}
     //~^ ERROR `let` expressions in this position are unstable [E0658]
+
+    if let 1 = 1 && let true = { true } && false {
+    //~^ ERROR `let` expressions in this position are unstable [E0658]
+    //~| ERROR `let` expressions in this position are unstable [E0658]
+    }
 }
 
 fn _while() {
diff --git a/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr b/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr
index bcea8bbaa73..feea1c254d8 100644
--- a/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr
+++ b/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr
@@ -1,17 +1,17 @@
 error: expected expression, found `let` statement
-  --> $DIR/feature-gate.rs:50:20
+  --> $DIR/feature-gate.rs:55:20
    |
 LL |     #[cfg(FALSE)] (let 0 = 1);
    |                    ^^^
 
 error: expected expression, found `let` statement
-  --> $DIR/feature-gate.rs:40:17
+  --> $DIR/feature-gate.rs:45:17
    |
 LL |     noop_expr!((let 0 = 1));
    |                 ^^^
 
 error: no rules expected the token `let`
-  --> $DIR/feature-gate.rs:53:15
+  --> $DIR/feature-gate.rs:58:15
    |
 LL |     macro_rules! use_expr {
    |     --------------------- when calling this macro
@@ -47,7 +47,25 @@ LL |     if let Range { start: _, end: _ } = (true..true) && false {}
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:27:19
+  --> $DIR/feature-gate.rs:23:8
+   |
+LL |     if let 1 = 1 && let true = { true } && false {
+   |        ^^^^^^^^^
+   |
+   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+   = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+  --> $DIR/feature-gate.rs:23:21
+   |
+LL |     if let 1 = 1 && let true = { true } && false {
+   |                     ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+   = help: add `#![feature(let_chains)]` to the crate attributes to enable
+
+error[E0658]: `let` expressions in this position are unstable
+  --> $DIR/feature-gate.rs:32:19
    |
 LL |     while true && let 0 = 1 {}
    |                   ^^^^^^^^^
@@ -56,7 +74,7 @@ LL |     while true && let 0 = 1 {}
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:30:11
+  --> $DIR/feature-gate.rs:35:11
    |
 LL |     while let 0 = 1 && true {}
    |           ^^^^^^^^^
@@ -65,7 +83,7 @@ LL |     while let 0 = 1 && true {}
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:33:11
+  --> $DIR/feature-gate.rs:38:11
    |
 LL |     while let Range { start: _, end: _ } = (true..true) && false {}
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -74,7 +92,7 @@ LL |     while let Range { start: _, end: _ } = (true..true) && false {}
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:50:20
+  --> $DIR/feature-gate.rs:55:20
    |
 LL |     #[cfg(FALSE)] (let 0 = 1);
    |                    ^^^^^^^^^
@@ -83,7 +101,7 @@ LL |     #[cfg(FALSE)] (let 0 = 1);
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
 error[E0658]: `let` expressions in this position are unstable
-  --> $DIR/feature-gate.rs:40:17
+  --> $DIR/feature-gate.rs:45:17
    |
 LL |     noop_expr!((let 0 = 1));
    |                 ^^^^^^^^^
@@ -91,6 +109,6 @@ LL |     noop_expr!((let 0 = 1));
    = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
    = help: add `#![feature(let_chains)]` to the crate attributes to enable
 
-error: aborting due to 11 previous errors
+error: aborting due to 13 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs b/src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs
index 6cc53a1935b..a942d1f4caf 100644
--- a/src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs
+++ b/src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.rs
@@ -1,6 +1,3 @@
-// check-pass
-// known-bug
-
 #![feature(let_chains)]
 
 fn main() {
@@ -8,10 +5,41 @@ fn main() {
 
     #[cfg(FALSE)]
     {
+        let _ = &&let Some(x) = Some(42);
+        //~^ ERROR expected expression, found `let` statement
+    }
+    #[cfg(FALSE)]
+    {
+        if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] = 1 {
+        //~^ ERROR expected expression, found `let` statement
+        //~| ERROR expected expression, found `let` statement
+            true
+        }
+    }
+
+    #[cfg(FALSE)]
+    {
         if let Some(elem) = _opt && {
             [1, 2, 3][let _ = ()];
+            //~^ ERROR expected expression, found `let` statement
             true
         } {
         }
     }
+
+    #[cfg(FALSE)]
+    {
+        if let Some(elem) = _opt && [1, 2, 3][let _ = ()] = 1 {
+        //~^ ERROR expected expression, found `let` statement
+            true
+        }
+    }
+    #[cfg(FALSE)]
+    {
+        if let a = 1 && {
+            let x = let y = 1;
+            //~^ ERROR expected expression, found `let` statement
+        } {
+        }
+    }
 }
diff --git a/src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr b/src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr
new file mode 100644
index 00000000000..d1ce83c7233
--- /dev/null
+++ b/src/test/ui/rfc-2497-if-let-chains/invalid-let-in-a-valid-let-context.stderr
@@ -0,0 +1,38 @@
+error: expected expression, found `let` statement
+  --> $DIR/invalid-let-in-a-valid-let-context.rs:8:19
+   |
+LL |         let _ = &&let Some(x) = Some(42);
+   |                   ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/invalid-let-in-a-valid-let-context.rs:13:47
+   |
+LL |         if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] = 1 {
+   |                                               ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/invalid-let-in-a-valid-let-context.rs:13:57
+   |
+LL |         if let Some(elem) = _opt && [1, 2, 3][let _ = &&let Some(x) = Some(42)] = 1 {
+   |                                                         ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/invalid-let-in-a-valid-let-context.rs:23:23
+   |
+LL |             [1, 2, 3][let _ = ()];
+   |                       ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/invalid-let-in-a-valid-let-context.rs:32:47
+   |
+LL |         if let Some(elem) = _opt && [1, 2, 3][let _ = ()] = 1 {
+   |                                               ^^^
+
+error: expected expression, found `let` statement
+  --> $DIR/invalid-let-in-a-valid-let-context.rs:40:21
+   |
+LL |             let x = let y = 1;
+   |                     ^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/test/ui/suggestions/auxiliary/meow.rs b/src/test/ui/suggestions/auxiliary/meow.rs
new file mode 100644
index 00000000000..115df70a690
--- /dev/null
+++ b/src/test/ui/suggestions/auxiliary/meow.rs
@@ -0,0 +1,11 @@
+pub trait Meow {
+    fn meow(&self) {}
+}
+
+pub struct GlobalMeow;
+
+impl Meow for GlobalMeow {}
+
+pub(crate) struct PrivateMeow;
+
+impl Meow for PrivateMeow {}
diff --git a/src/test/ui/suggestions/issue-99080.rs b/src/test/ui/suggestions/issue-99080.rs
new file mode 100644
index 00000000000..91f574f35b8
--- /dev/null
+++ b/src/test/ui/suggestions/issue-99080.rs
@@ -0,0 +1,16 @@
+// aux-build:meow.rs
+
+extern crate meow;
+
+use meow::Meow;
+
+fn needs_meow<T: Meow>(t: T) {}
+
+fn main() {
+    needs_meow(1usize);
+    //~^ ERROR the trait bound `usize: Meow` is not satisfied
+}
+
+struct LocalMeow;
+
+impl Meow for LocalMeow {}
diff --git a/src/test/ui/suggestions/issue-99080.stderr b/src/test/ui/suggestions/issue-99080.stderr
new file mode 100644
index 00000000000..d1908dd9d0d
--- /dev/null
+++ b/src/test/ui/suggestions/issue-99080.stderr
@@ -0,0 +1,20 @@
+error[E0277]: the trait bound `usize: Meow` is not satisfied
+  --> $DIR/issue-99080.rs:10:16
+   |
+LL |     needs_meow(1usize);
+   |     ---------- ^^^^^^ the trait `Meow` is not implemented for `usize`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the following other types implement trait `Meow`:
+             GlobalMeow
+             LocalMeow
+note: required by a bound in `needs_meow`
+  --> $DIR/issue-99080.rs:7:18
+   |
+LL | fn needs_meow<T: Meow>(t: T) {}
+   |                  ^^^^ required by this bound in `needs_meow`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/traits/bound/assoc-fn-bound-root-obligation.stderr b/src/test/ui/traits/bound/assoc-fn-bound-root-obligation.stderr
index 115539a6dc2..6ce57b6263e 100644
--- a/src/test/ui/traits/bound/assoc-fn-bound-root-obligation.stderr
+++ b/src/test/ui/traits/bound/assoc-fn-bound-root-obligation.stderr
@@ -15,7 +15,6 @@ LL |     s.strip_suffix(b'\n').unwrap_or(s)
              &'c &'b str
              [char; N]
              char
-             pattern::MultiCharEqPattern<C>
    = note: required because of the requirements on the impl of `Pattern<'_>` for `u8`
 
 error: aborting due to previous error
diff --git a/src/test/ui/traits/issue-59029-1.stderr b/src/test/ui/traits/issue-59029-1.stderr
index 53cdb8b1baf..203a8928530 100644
--- a/src/test/ui/traits/issue-59029-1.stderr
+++ b/src/test/ui/traits/issue-59029-1.stderr
@@ -2,13 +2,13 @@ error[E0220]: associated type `Res` not found for `Self`
   --> $DIR/issue-59029-1.rs:5:52
    |
 LL | trait MkSvc<Target, Req> = Svc<Target> where Self::Res: Svc<Req>;
-   |                                                    ^^^ associated type `Res` not found
+   |                                                    ^^^ there is a similarly named associated type `Res` in the trait `Svc`
 
 error[E0220]: associated type `Res` not found for `Self`
   --> $DIR/issue-59029-1.rs:5:52
    |
 LL | trait MkSvc<Target, Req> = Svc<Target> where Self::Res: Svc<Req>;
-   |                                                    ^^^ associated type `Res` not found
+   |                                                    ^^^ there is a similarly named associated type `Res` in the trait `Svc`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/traits/issue-77982.stderr b/src/test/ui/traits/issue-77982.stderr
index a2d23c4e9df..2b832e27c52 100644
--- a/src/test/ui/traits/issue-77982.stderr
+++ b/src/test/ui/traits/issue-77982.stderr
@@ -13,10 +13,6 @@ note: required by a bound in `HashMap::<K, V, S>::get`
    |
 LL |         K: Borrow<Q>,
    |            ^^^^^^^^^ required by this bound in `HashMap::<K, V, S>::get`
-help: consider specifying the generic argument
-   |
-LL |     opts.get::<Q>(opt.as_ref());
-   |             +++++
 help: consider specifying the type argument in the function call
    |
 LL |     opts.get::<Q>(opt.as_ref());
diff --git a/src/test/ui/traits/multidispatch-convert-ambig-dest.stderr b/src/test/ui/traits/multidispatch-convert-ambig-dest.stderr
index 25f8d538377..cbec3593421 100644
--- a/src/test/ui/traits/multidispatch-convert-ambig-dest.stderr
+++ b/src/test/ui/traits/multidispatch-convert-ambig-dest.stderr
@@ -30,10 +30,6 @@ LL | fn test<T,U>(_: T, _: U)
    |    ---- required by a bound in this
 LL | where T : Convert<U>
    |           ^^^^^^^^^^ required by this bound in `test`
-help: consider specifying the generic arguments
-   |
-LL |     test::<i32, U>(22, std::default::Default::default());
-   |         ++++++++++
 help: consider specifying the type arguments in the function call
    |
 LL |     test::<T, U>(22, std::default::Default::default());
diff --git a/src/test/ui/type-alias-impl-trait/not_well_formed.stderr b/src/test/ui/type-alias-impl-trait/not_well_formed.stderr
index 91c1d031e4e..c36b95f47e8 100644
--- a/src/test/ui/type-alias-impl-trait/not_well_formed.stderr
+++ b/src/test/ui/type-alias-impl-trait/not_well_formed.stderr
@@ -2,7 +2,7 @@ error[E0220]: associated type `Assoc` not found for `V`
   --> $DIR/not_well_formed.rs:9:29
    |
 LL | type Foo<V> = impl Trait<V::Assoc>;
-   |                             ^^^^^ associated type `Assoc` not found
+   |                             ^^^^^ there is a similarly named associated type `Assoc` in the trait `TraitWithAssoc`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/type/type-annotation-needed.stderr b/src/test/ui/type/type-annotation-needed.stderr
index 7d7890c8b80..4af4c22f751 100644
--- a/src/test/ui/type/type-annotation-needed.stderr
+++ b/src/test/ui/type/type-annotation-needed.stderr
@@ -10,10 +10,6 @@ note: required by a bound in `foo`
    |
 LL | fn foo<T: Into<String>>(x: i32) {}
    |           ^^^^^^^^^^^^ required by this bound in `foo`
-help: consider specifying the generic argument
-   |
-LL |     foo::<T>(42);
-   |        +++++
 help: consider specifying the type argument in the function call
    |
 LL |     foo::<T>(42);
diff --git a/src/test/ui/typeck/do-not-suggest-adding-missing-zero-to-floating-point-number.rs b/src/test/ui/typeck/do-not-suggest-adding-missing-zero-to-floating-point-number.rs
new file mode 100644
index 00000000000..501f4b6ef9e
--- /dev/null
+++ b/src/test/ui/typeck/do-not-suggest-adding-missing-zero-to-floating-point-number.rs
@@ -0,0 +1,21 @@
+macro_rules! num { () => { 1 } }
+
+fn main() {
+    let x = 1i32;
+    x.e10; //~ERROR `i32` is a primitive type and therefore doesn't have fields
+
+    let y = 1;
+    y.e10; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+
+    2u32.e10; //~ERROR `u32` is a primitive type and therefore doesn't have fields
+
+    num!().e10; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+
+    2.e10foo; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+
+    42._;
+    //~^ERROR expected identifier, found reserved identifier `_`
+    //~|ERROR `{integer}` is a primitive type and therefore doesn't have fields
+
+    42.a; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+}
diff --git a/src/test/ui/typeck/do-not-suggest-adding-missing-zero-to-floating-point-number.stderr b/src/test/ui/typeck/do-not-suggest-adding-missing-zero-to-floating-point-number.stderr
new file mode 100644
index 00000000000..1ef1d4c28e4
--- /dev/null
+++ b/src/test/ui/typeck/do-not-suggest-adding-missing-zero-to-floating-point-number.stderr
@@ -0,0 +1,51 @@
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:16:8
+   |
+LL |     42._;
+   |        ^ expected identifier, found reserved identifier
+
+error[E0610]: `i32` is a primitive type and therefore doesn't have fields
+  --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:5:7
+   |
+LL |     x.e10;
+   |       ^^^
+
+error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
+  --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:8:7
+   |
+LL |     y.e10;
+   |       ^^^
+
+error[E0610]: `u32` is a primitive type and therefore doesn't have fields
+  --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:10:10
+   |
+LL |     2u32.e10;
+   |          ^^^
+
+error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
+  --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:12:12
+   |
+LL |     num!().e10;
+   |            ^^^
+
+error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
+  --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:14:7
+   |
+LL |     2.e10foo;
+   |       ^^^^^^
+
+error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
+  --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:16:8
+   |
+LL |     42._;
+   |        ^
+
+error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
+  --> $DIR/do-not-suggest-adding-missing-zero-to-floating-point-number.rs:20:8
+   |
+LL |     42.a;
+   |        ^
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0610`.
diff --git a/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.fixed b/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.fixed
new file mode 100644
index 00000000000..ba83e79005b
--- /dev/null
+++ b/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.fixed
@@ -0,0 +1,11 @@
+// run-rustfix
+
+fn main() {
+    2.0e1; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+    2.0E1; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+    2.0f32; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+    2.0f64; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+    2.0e+12; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+    2.0e-12; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+    2.0e1f32; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+}
diff --git a/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.rs b/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.rs
new file mode 100644
index 00000000000..c102447f602
--- /dev/null
+++ b/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.rs
@@ -0,0 +1,11 @@
+// run-rustfix
+
+fn main() {
+    2.e1; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+    2.E1; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+    2.f32; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+    2.f64; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+    2.e+12; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+    2.e-12; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+    2.e1f32; //~ERROR `{integer}` is a primitive type and therefore doesn't have fields
+}
diff --git a/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.stderr b/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.stderr
new file mode 100644
index 00000000000..e8e069708a8
--- /dev/null
+++ b/src/test/ui/typeck/suggest-adding-missing-zero-to-floating-point-number.stderr
@@ -0,0 +1,80 @@
+error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
+  --> $DIR/suggest-adding-missing-zero-to-floating-point-number.rs:4:7
+   |
+LL |     2.e1;
+   |       ^^
+   |
+help: If the number is meant to be a floating point number, consider adding a `0` after the period
+   |
+LL |     2.0e1;
+   |       +
+
+error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
+  --> $DIR/suggest-adding-missing-zero-to-floating-point-number.rs:5:7
+   |
+LL |     2.E1;
+   |       ^^
+   |
+help: If the number is meant to be a floating point number, consider adding a `0` after the period
+   |
+LL |     2.0E1;
+   |       +
+
+error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
+  --> $DIR/suggest-adding-missing-zero-to-floating-point-number.rs:6:7
+   |
+LL |     2.f32;
+   |       ^^^
+   |
+help: If the number is meant to be a floating point number, consider adding a `0` after the period
+   |
+LL |     2.0f32;
+   |       +
+
+error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
+  --> $DIR/suggest-adding-missing-zero-to-floating-point-number.rs:7:7
+   |
+LL |     2.f64;
+   |       ^^^
+   |
+help: If the number is meant to be a floating point number, consider adding a `0` after the period
+   |
+LL |     2.0f64;
+   |       +
+
+error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
+  --> $DIR/suggest-adding-missing-zero-to-floating-point-number.rs:8:7
+   |
+LL |     2.e+12;
+   |       ^
+   |
+help: If the number is meant to be a floating point number, consider adding a `0` after the period
+   |
+LL |     2.0e+12;
+   |       +
+
+error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
+  --> $DIR/suggest-adding-missing-zero-to-floating-point-number.rs:9:7
+   |
+LL |     2.e-12;
+   |       ^
+   |
+help: If the number is meant to be a floating point number, consider adding a `0` after the period
+   |
+LL |     2.0e-12;
+   |       +
+
+error[E0610]: `{integer}` is a primitive type and therefore doesn't have fields
+  --> $DIR/suggest-adding-missing-zero-to-floating-point-number.rs:10:7
+   |
+LL |     2.e1f32;
+   |       ^^^^^
+   |
+help: If the number is meant to be a floating point number, consider adding a `0` after the period
+   |
+LL |     2.0e1f32;
+   |       +
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0610`.
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 7cf4a88c470..17f2b77dab0 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -60,6 +60,8 @@ impl EarlyProps {
 pub struct TestProps {
     // Lines that should be expected, in order, on standard out
     pub error_patterns: Vec<String>,
+    // Regexes that should be expected, in order, on standard out
+    pub regex_error_patterns: Vec<String>,
     // Extra flags to pass to the compiler
     pub compile_flags: Vec<String>,
     // Extra flags to pass when the compiled code is run (such as --bench)
@@ -163,6 +165,7 @@ pub struct TestProps {
 
 mod directives {
     pub const ERROR_PATTERN: &'static str = "error-pattern";
+    pub const REGEX_ERROR_PATTERN: &'static str = "regex-error-pattern";
     pub const COMPILE_FLAGS: &'static str = "compile-flags";
     pub const RUN_FLAGS: &'static str = "run-flags";
     pub const SHOULD_ICE: &'static str = "should-ice";
@@ -200,6 +203,7 @@ impl TestProps {
     pub fn new() -> Self {
         TestProps {
             error_patterns: vec![],
+            regex_error_patterns: vec![],
             compile_flags: vec![],
             run_flags: None,
             pp_exact: None,
@@ -285,6 +289,12 @@ impl TestProps {
                     &mut self.error_patterns,
                     |r| r,
                 );
+                config.push_name_value_directive(
+                    ln,
+                    REGEX_ERROR_PATTERN,
+                    &mut self.regex_error_patterns,
+                    |r| r,
+                );
 
                 if let Some(flags) = config.parse_name_value_directive(ln, COMPILE_FLAGS) {
                     self.compile_flags.extend(flags.split_whitespace().map(|s| s.to_owned()));
@@ -294,7 +304,7 @@ impl TestProps {
                 }
 
                 if let Some(edition) = config.parse_edition(ln) {
-                    self.compile_flags.push(format!("--edition={}", edition));
+                    self.compile_flags.push(format!("--edition={}", edition.trim()));
                     has_edition = true;
                 }
 
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index dd9e2a6687e..5517b5a12c3 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -323,12 +323,13 @@ impl<'test> TestCx<'test> {
         let output_to_check = self.get_output(&proc_res);
         let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
         if !expected_errors.is_empty() {
-            if !self.props.error_patterns.is_empty() {
+            if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty()
+            {
                 self.fatal("both error pattern and expected errors specified");
             }
             self.check_expected_errors(expected_errors, &proc_res);
         } else {
-            self.check_error_patterns(&output_to_check, &proc_res, pm);
+            self.check_all_error_patterns(&output_to_check, &proc_res, pm);
         }
         if self.props.should_ice {
             match proc_res.status.code() {
@@ -363,7 +364,7 @@ impl<'test> TestCx<'test> {
 
         let output_to_check = self.get_output(&proc_res);
         self.check_correct_failure_status(&proc_res);
-        self.check_error_patterns(&output_to_check, &proc_res, pm);
+        self.check_all_error_patterns(&output_to_check, &proc_res, pm);
     }
 
     fn get_output(&self, proc_res: &ProcRes) -> String {
@@ -1222,14 +1223,13 @@ impl<'test> TestCx<'test> {
         }
     }
 
-    fn check_error_patterns(
+    fn check_all_error_patterns(
         &self,
         output_to_check: &str,
         proc_res: &ProcRes,
         pm: Option<PassMode>,
     ) {
-        debug!("check_error_patterns");
-        if self.props.error_patterns.is_empty() {
+        if self.props.error_patterns.is_empty() && self.props.regex_error_patterns.is_empty() {
             if pm.is_some() {
                 // FIXME(#65865)
                 return;
@@ -1243,13 +1243,8 @@ impl<'test> TestCx<'test> {
 
         let mut missing_patterns: Vec<String> = Vec::new();
 
-        for pattern in &self.props.error_patterns {
-            if output_to_check.contains(pattern.trim()) {
-                debug!("found error pattern {}", pattern);
-            } else {
-                missing_patterns.push(pattern.to_string());
-            }
-        }
+        self.check_error_patterns(output_to_check, &mut missing_patterns);
+        self.check_regex_error_patterns(output_to_check, proc_res, &mut missing_patterns);
 
         if missing_patterns.is_empty() {
             return;
@@ -1268,6 +1263,44 @@ impl<'test> TestCx<'test> {
         }
     }
 
+    fn check_error_patterns(&self, output_to_check: &str, missing_patterns: &mut Vec<String>) {
+        debug!("check_error_patterns");
+        for pattern in &self.props.error_patterns {
+            if output_to_check.contains(pattern.trim()) {
+                debug!("found error pattern {}", pattern);
+            } else {
+                missing_patterns.push(pattern.to_string());
+            }
+        }
+    }
+
+    fn check_regex_error_patterns(
+        &self,
+        output_to_check: &str,
+        proc_res: &ProcRes,
+        missing_patterns: &mut Vec<String>,
+    ) {
+        debug!("check_regex_error_patterns");
+
+        for pattern in &self.props.regex_error_patterns {
+            let pattern = pattern.trim();
+            let re = match Regex::new(pattern) {
+                Ok(re) => re,
+                Err(err) => {
+                    self.fatal_proc_rec(
+                        &format!("invalid regex error pattern '{}': {:?}", pattern, err),
+                        proc_res,
+                    );
+                }
+            };
+            if re.is_match(output_to_check) {
+                debug!("found regex error pattern {}", pattern);
+            } else {
+                missing_patterns.push(pattern.to_string());
+            }
+        }
+    }
+
     fn check_no_compiler_crash(&self, proc_res: &ProcRes, should_ice: bool) {
         match proc_res.status.code() {
             Some(101) if !should_ice => {
@@ -1892,7 +1925,9 @@ impl<'test> TestCx<'test> {
                 // If we are extracting and matching errors in the new
                 // fashion, then you want JSON mode. Old-skool error
                 // patterns still match the raw compiler output.
-                if self.props.error_patterns.is_empty() {
+                if self.props.error_patterns.is_empty()
+                    && self.props.regex_error_patterns.is_empty()
+                {
                     rustc.args(&["--error-format", "json"]);
                     rustc.args(&["--json", "future-incompat"]);
                 }
@@ -3268,10 +3303,11 @@ impl<'test> TestCx<'test> {
                 self.fatal_proc_rec("test run succeeded!", &proc_res);
             }
 
-            if !self.props.error_patterns.is_empty() {
+            if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty()
+            {
                 // "// error-pattern" comments
                 let output_to_check = self.get_output(&proc_res);
-                self.check_error_patterns(&output_to_check, &proc_res, pm);
+                self.check_all_error_patterns(&output_to_check, &proc_res, pm);
             }
         }
 
@@ -3285,15 +3321,16 @@ impl<'test> TestCx<'test> {
             self.props.error_patterns
         );
         if !explicit && self.config.compare_mode.is_none() {
-            let check_patterns =
-                should_run == WillExecute::No && !self.props.error_patterns.is_empty();
+            let check_patterns = should_run == WillExecute::No
+                && (!self.props.error_patterns.is_empty()
+                    || !self.props.regex_error_patterns.is_empty());
 
             let check_annotations = !check_patterns || !expected_errors.is_empty();
 
             if check_patterns {
                 // "// error-pattern" comments
                 let output_to_check = self.get_output(&proc_res);
-                self.check_error_patterns(&output_to_check, &proc_res, pm);
+                self.check_all_error_patterns(&output_to_check, &proc_res, pm);
             }
 
             if check_annotations {