about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-01-24 08:28:35 +0000
committerbors <bors@rust-lang.org>2025-01-24 08:28:35 +0000
commit061ee95ce197dc9b276fc5363eddbfc0ecc08584 (patch)
tree5c9d741564f0447833d21666ee2c3c685d4cc5f6
parent48ef38d3503a04e5e18157e664e3e65dc7eca1a5 (diff)
parent109def514987ab6a1885f6f3d2165e7bf03dbee8 (diff)
downloadrust-061ee95ce197dc9b276fc5363eddbfc0ecc08584.tar.gz
rust-061ee95ce197dc9b276fc5363eddbfc0ecc08584.zip
Auto merge of #135978 - matthiaskrgr:rollup-ni16gqr, r=matthiaskrgr
Rollup of 8 pull requests

Successful merges:

 - #133605 (Add extensive set of drop order tests)
 - #135489 (remove pointless allowed_through_unstable_modules on TryFromSliceError)
 - #135757 (Add NuttX support for AArch64 and ARMv7-A targets)
 - #135799 (rustdoc-json: Rename `Path::name` to `path`, and give it the path again.)
 - #135865 (For E0223, suggest associated functions that are similar to the path, even if the base type has multiple inherent impl blocks.)
 - #135890 (Implement `VecDeque::pop_front_if` & `VecDeque::pop_back_if`)
 - #135914 (Remove usages of `QueryNormalizer` in the compiler)
 - #135936 (fix reify-intrinsic test)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs20
-rw-r--r--compiler/rustc_target/src/spec/mod.rs5
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs46
-rw-r--r--compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs41
-rw-r--r--compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs41
-rw-r--r--compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs33
-rw-r--r--compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs37
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs24
-rw-r--r--compiler/rustc_traits/src/type_op.rs8
-rw-r--r--library/alloc/src/collections/vec_deque/mod.rs46
-rw-r--r--library/alloc/tests/lib.rs1
-rw-r--r--library/alloc/tests/vec_deque.rs39
-rw-r--r--library/core/src/array/mod.rs1
-rw-r--r--src/doc/rustc-dev-guide/src/stability.md3
-rw-r--r--src/doc/rustc/src/platform-support.md5
-rw-r--r--src/doc/rustc/src/platform-support/nuttx.md9
-rw-r--r--src/librustdoc/json/conversions.rs2
-rw-r--r--src/rustdoc-json-types/lib.rs20
-rw-r--r--src/tools/jsondoclint/src/validator/tests.rs4
-rw-r--r--src/tools/tidy/src/issues.txt1
-rw-r--r--tests/assembly/targets/targets-elf.rs15
-rw-r--r--tests/rustdoc-json/auxiliary/defines_and_reexports.rs10
-rw-r--r--tests/rustdoc-json/blanket_impls.rs2
-rw-r--r--tests/rustdoc-json/fns/async_return.rs4
-rw-r--r--tests/rustdoc-json/impl-trait-in-assoc-type.rs2
-rw-r--r--tests/rustdoc-json/path_name.rs83
-rw-r--r--tests/rustdoc-json/return_private.rs4
-rw-r--r--tests/rustdoc-json/type/dyn.rs14
-rw-r--r--tests/rustdoc-json/type/generic_default.rs4
-rw-r--r--tests/rustdoc-json/type/hrtb.rs2
-rw-r--r--tests/ui/drop/drop-order-comparisons.e2021.fixed575
-rw-r--r--tests/ui/drop/drop-order-comparisons.e2021.stderr477
-rw-r--r--tests/ui/drop/drop-order-comparisons.rs575
-rw-r--r--tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs8
-rw-r--r--tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr32
-rw-r--r--tests/ui/intrinsics/reify-intrinsic.rs6
-rw-r--r--tests/ui/intrinsics/reify-intrinsic.stderr16
-rw-r--r--tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.rs51
-rw-r--r--tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.stderr135
-rw-r--r--tests/ui/suggestions/issue-109195.rs20
-rw-r--r--tests/ui/suggestions/issue-109195.stderr69
41 files changed, 2317 insertions, 173 deletions
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 5d751a25080..36b214b6ae7 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -9,6 +9,7 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_middle::bug;
+use rustc_middle::ty::fast_reject::{TreatParams, simplify_type};
 use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _};
 use rustc_middle::ty::{
     self, AdtDef, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt,
@@ -998,12 +999,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     )),
                 ..
             }) = node
-            && let Some(adt_def) = qself_ty.ty_adt_def()
-            && let [inherent_impl] = tcx.inherent_impls(adt_def.did())
-            && let name = format!("{ident2}_{ident3}")
-            && let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = tcx
-                .associated_items(inherent_impl)
-                .filter_by_name_unhygienic(Symbol::intern(&name))
+            && let Some(inherent_impls) = qself_ty
+                .ty_adt_def()
+                .map(|adt_def| tcx.inherent_impls(adt_def.did()))
+                .or_else(|| {
+                    simplify_type(tcx, qself_ty, TreatParams::InstantiateWithInfer)
+                        .map(|simple_ty| tcx.incoherent_impls(simple_ty))
+                })
+            && let name = Symbol::intern(&format!("{ident2}_{ident3}"))
+            && let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = inherent_impls
+                .iter()
+                .flat_map(|inherent_impl| {
+                    tcx.associated_items(inherent_impl).filter_by_name_unhygienic(name)
+                })
                 .next()
         {
             Err(struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated type")
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 1f2df7f0168..37564ab38fc 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1853,6 +1853,8 @@ supported_targets! {
 
     ("armv7a-none-eabi", armv7a_none_eabi),
     ("armv7a-none-eabihf", armv7a_none_eabihf),
+    ("armv7a-nuttx-eabi", armv7a_nuttx_eabi),
+    ("armv7a-nuttx-eabihf", armv7a_nuttx_eabihf),
 
     ("msp430-none-elf", msp430_none_elf),
 
@@ -1896,6 +1898,7 @@ supported_targets! {
 
     ("aarch64-unknown-none", aarch64_unknown_none),
     ("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat),
+    ("aarch64-unknown-nuttx", aarch64_unknown_nuttx),
 
     ("x86_64-fortanix-unknown-sgx", x86_64_fortanix_unknown_sgx),
 
@@ -1971,6 +1974,8 @@ supported_targets! {
     ("x86_64-unknown-linux-none", x86_64_unknown_linux_none),
 
     ("thumbv6m-nuttx-eabi", thumbv6m_nuttx_eabi),
+    ("thumbv7a-nuttx-eabi", thumbv7a_nuttx_eabi),
+    ("thumbv7a-nuttx-eabihf", thumbv7a_nuttx_eabihf),
     ("thumbv7m-nuttx-eabi", thumbv7m_nuttx_eabi),
     ("thumbv7em-nuttx-eabi", thumbv7em_nuttx_eabi),
     ("thumbv7em-nuttx-eabihf", thumbv7em_nuttx_eabihf),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs
new file mode 100644
index 00000000000..04fd3ec1c26
--- /dev/null
+++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs
@@ -0,0 +1,46 @@
+// Generic AArch64 target for NuttX OS
+//
+// Can be used in conjunction with the `target-feature` and
+// `target-cpu` compiler flags to opt-in more hardware-specific
+// features.
+//
+// For example, `-C target-cpu=cortex-a53`.
+
+use crate::spec::{
+    Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target,
+    TargetOptions, cvs,
+};
+
+pub(crate) fn target() -> Target {
+    let opts = TargetOptions {
+        linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
+        linker: Some("rust-lld".into()),
+        // Enable the Cortex-A53 errata 843419 mitigation by default
+        pre_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &[
+            "--fix-cortex-a53-843419",
+        ]),
+        features: "+v8a,+strict-align,+neon,+fp-armv8".into(),
+        supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS,
+        relocation_model: RelocModel::Static,
+        disable_redzone: true,
+        max_atomic_width: Some(128),
+        stack_probes: StackProbeType::Inline,
+        panic_strategy: PanicStrategy::Abort,
+        families: cvs!["unix"],
+        os: "nuttx".into(),
+        ..Default::default()
+    };
+    Target {
+        llvm_target: "aarch64-unknown-none".into(),
+        metadata: crate::spec::TargetMetadata {
+            description: Some("AArch64 NuttX".into()),
+            tier: Some(3),
+            host_tools: Some(false),
+            std: Some(false),
+        },
+        pointer_width: 64,
+        data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        arch: "aarch64".into(),
+        options: opts,
+    }
+}
diff --git a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs
new file mode 100644
index 00000000000..138716e8f14
--- /dev/null
+++ b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs
@@ -0,0 +1,41 @@
+// Targets Cortex-A7/A8/A9 processors (ARMv7-A) running NuttX
+//
+// This target assumes that the device does NOT have a FPU (Floating Point Unit)
+// and will use software floating point operations. This matches the NuttX EABI
+// configuration without hardware floating point support.
+
+use crate::spec::{
+    Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions, cvs,
+};
+
+pub(crate) fn target() -> Target {
+    let opts = TargetOptions {
+        abi: "eabi".into(),
+        llvm_floatabi: Some(FloatAbi::Soft),
+        linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
+        linker: Some("rust-lld".into()),
+        features: "+v7,+thumb2,+soft-float,-neon,+strict-align".into(),
+        relocation_model: RelocModel::Static,
+        disable_redzone: true,
+        max_atomic_width: Some(64),
+        panic_strategy: PanicStrategy::Abort,
+        emit_debug_gdb_scripts: false,
+        c_enum_min_bits: Some(8),
+        families: cvs!["unix"],
+        os: "nuttx".into(),
+        ..Default::default()
+    };
+    Target {
+        llvm_target: "armv7a-none-eabi".into(),
+        metadata: crate::spec::TargetMetadata {
+            description: Some("ARMv7-A Cortex-A with NuttX".into()),
+            tier: Some(3),
+            host_tools: Some(false),
+            std: Some(false),
+        },
+        pointer_width: 32,
+        data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+        arch: "arm".into(),
+        options: opts,
+    }
+}
diff --git a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs
new file mode 100644
index 00000000000..40391c9f48e
--- /dev/null
+++ b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs
@@ -0,0 +1,41 @@
+// Targets Cortex-A7/A8/A9 processors (ARMv7-A) running NuttX with hardware floating point
+//
+// This target assumes that the device has a FPU (Floating Point Unit)
+// and will use hardware floating point operations. This matches the NuttX EABI
+// configuration with hardware floating point support.
+
+use crate::spec::{
+    Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions, cvs,
+};
+
+pub(crate) fn target() -> Target {
+    let opts = TargetOptions {
+        abi: "eabihf".into(),
+        llvm_floatabi: Some(FloatAbi::Hard),
+        linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
+        linker: Some("rust-lld".into()),
+        features: "+v7,+thumb2,+vfp3,+neon,+strict-align".into(),
+        relocation_model: RelocModel::Static,
+        disable_redzone: true,
+        max_atomic_width: Some(64),
+        panic_strategy: PanicStrategy::Abort,
+        emit_debug_gdb_scripts: false,
+        c_enum_min_bits: Some(8),
+        families: cvs!["unix"],
+        os: "nuttx".into(),
+        ..Default::default()
+    };
+    Target {
+        llvm_target: "armv7a-none-eabihf".into(),
+        metadata: crate::spec::TargetMetadata {
+            description: Some("ARMv7-A Cortex-A with NuttX (hard float)".into()),
+            tier: Some(3),
+            host_tools: Some(false),
+            std: Some(false),
+        },
+        pointer_width: 32,
+        data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+        arch: "arm".into(),
+        options: opts,
+    }
+}
diff --git a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs
new file mode 100644
index 00000000000..7fd22602e56
--- /dev/null
+++ b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs
@@ -0,0 +1,33 @@
+// Targets Cortex-A7/A8/A9 processors (ARMv7-A)
+//
+// This target assumes that the device does NOT have a FPU (Floating Point Unit)
+// and will use software floating point operations. This matches the NuttX EABI
+// configuration without hardware floating point support.
+
+use crate::spec::{FloatAbi, Target, TargetOptions, base, cvs};
+
+pub(crate) fn target() -> Target {
+    Target {
+        llvm_target: "thumbv7a-none-eabi".into(),
+        metadata: crate::spec::TargetMetadata {
+            description: None,
+            tier: None,
+            host_tools: None,
+            std: None,
+        },
+        pointer_width: 32,
+        data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+        arch: "arm".into(),
+
+        options: TargetOptions {
+            families: cvs!["unix"],
+            os: "nuttx".into(),
+            abi: "eabi".into(),
+            llvm_floatabi: Some(FloatAbi::Soft),
+            // Cortex-A7/A8/A9 with software floating point
+            features: "+soft-float,-neon".into(),
+            max_atomic_width: Some(64),
+            ..base::thumb::opts()
+        },
+    }
+}
diff --git a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs
new file mode 100644
index 00000000000..d3148c53a82
--- /dev/null
+++ b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs
@@ -0,0 +1,37 @@
+// Targets Cortex-A7/A8/A9 processors (ARMv7-A)
+//
+// This target assumes that the device has a FPU (Floating Point Unit) and lowers all (single
+// precision) floating point operations to hardware instructions. Cortex-A7/A8/A9 processors
+// support VFPv3-D32 or VFPv4-D32 floating point units with optional double-precision support.
+//
+// This target uses the "hard" floating convention (ABI) where floating point values
+// are passed to/from subroutines via FPU registers (S0, S1, D0, D1, etc.).
+
+use crate::spec::{FloatAbi, Target, TargetOptions, base, cvs};
+
+pub(crate) fn target() -> Target {
+    Target {
+        llvm_target: "thumbv7a-none-eabihf".into(),
+        metadata: crate::spec::TargetMetadata {
+            description: None,
+            tier: None,
+            host_tools: None,
+            std: None,
+        },
+        pointer_width: 32,
+        data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
+        arch: "arm".into(),
+
+        options: TargetOptions {
+            families: cvs!["unix"],
+            os: "nuttx".into(),
+            abi: "eabihf".into(),
+            llvm_floatabi: Some(FloatAbi::Hard),
+            // Cortex-A7/A8/A9 support VFPv3-D32/VFPv4-D32 with optional double-precision
+            // and NEON SIMD instructions
+            features: "+vfp3,+neon".into(),
+            max_atomic_width: Some(64),
+            ..base::thumb::opts()
+        },
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
index 4004e354dc1..26ba1511b54 100644
--- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
@@ -6,8 +6,7 @@ use rustc_span::{DUMMY_SP, Span};
 use tracing::{debug, instrument};
 
 use crate::traits::query::NoSolution;
-use crate::traits::query::normalize::QueryNormalizeExt;
-use crate::traits::{Normalized, ObligationCause, ObligationCtxt};
+use crate::traits::{ObligationCause, ObligationCtxt};
 
 /// This returns true if the type `ty` is "trivial" for
 /// dropck-outlives -- that is, if it doesn't require any types to
@@ -172,13 +171,18 @@ pub fn compute_dropck_outlives_inner<'tcx>(
         // do not themselves define a destructor", more or less. We have
         // to push them onto the stack to be expanded.
         for ty in constraints.dtorck_types.drain(..) {
-            let Normalized { value: ty, obligations } =
-                ocx.infcx.at(&cause, param_env).query_normalize(ty)?;
-            ocx.register_obligations(obligations);
+            let normalized_ty = ocx.normalize(&cause, param_env, ty);
 
-            debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
+            let errors = ocx.select_where_possible();
+            if !errors.is_empty() {
+                debug!("failed to normalize dtorck type: {ty} ~> {errors:#?}");
+                return Err(NoSolution);
+            }
+
+            let normalized_ty = ocx.infcx.resolve_vars_if_possible(normalized_ty);
+            debug!("dropck_outlives: ty from dtorck_types = {:?}", normalized_ty);
 
-            match ty.kind() {
+            match normalized_ty.kind() {
                 // All parameters live for the duration of the
                 // function.
                 ty::Param(..) => {}
@@ -186,12 +190,12 @@ pub fn compute_dropck_outlives_inner<'tcx>(
                 // A projection that we couldn't resolve - it
                 // might have a destructor.
                 ty::Alias(..) => {
-                    result.kinds.push(ty.into());
+                    result.kinds.push(normalized_ty.into());
                 }
 
                 _ => {
-                    if ty_set.insert(ty) {
-                        ty_stack.push((ty, depth + 1));
+                    if ty_set.insert(normalized_ty) {
+                        ty_stack.push((normalized_ty, depth + 1));
                     }
                 }
             }
diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs
index 71088a598bb..5d041c2623a 100644
--- a/compiler/rustc_traits/src/type_op.rs
+++ b/compiler/rustc_traits/src/type_op.rs
@@ -6,13 +6,12 @@ use rustc_middle::query::Providers;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::ty::{Clause, FnSig, ParamEnvAnd, PolyFnSig, Ty, TyCtxt, TypeFoldable};
 use rustc_trait_selection::infer::InferCtxtBuilderExt;
-use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
 use rustc_trait_selection::traits::query::type_op::ascribe_user_type::{
     AscribeUserType, type_op_ascribe_user_type_with_span,
 };
 use rustc_trait_selection::traits::query::type_op::normalize::Normalize;
 use rustc_trait_selection::traits::query::type_op::prove_predicate::ProvePredicate;
-use rustc_trait_selection::traits::{Normalized, Obligation, ObligationCause, ObligationCtxt};
+use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt};
 
 pub(crate) fn provide(p: &mut Providers) {
     *p = Providers {
@@ -43,10 +42,7 @@ where
     T: fmt::Debug + TypeFoldable<TyCtxt<'tcx>>,
 {
     let (param_env, Normalize { value }) = key.into_parts();
-    let Normalized { value, obligations } =
-        ocx.infcx.at(&ObligationCause::dummy(), param_env).query_normalize(value)?;
-    ocx.register_obligations(obligations);
-    Ok(value)
+    Ok(ocx.normalize(&ObligationCause::dummy(), param_env, value))
 }
 
 fn type_op_normalize_ty<'tcx>(
diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs
index 0b6a55297e1..1c33f8f60d8 100644
--- a/library/alloc/src/collections/vec_deque/mod.rs
+++ b/library/alloc/src/collections/vec_deque/mod.rs
@@ -1735,6 +1735,52 @@ impl<T, A: Allocator> VecDeque<T, A> {
         }
     }
 
+    /// Removes and returns the first element from the deque if the predicate
+    /// returns `true`, or [`None`] if the predicate returns false or the deque
+    /// is empty (the predicate will not be called in that case).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(vec_deque_pop_if)]
+    /// use std::collections::VecDeque;
+    ///
+    /// let mut deque: VecDeque<i32> = vec![0, 1, 2, 3, 4].into();
+    /// let pred = |x: &mut i32| *x % 2 == 0;
+    ///
+    /// assert_eq!(deque.pop_front_if(pred), Some(0));
+    /// assert_eq!(deque, [1, 2, 3, 4]);
+    /// assert_eq!(deque.pop_front_if(pred), None);
+    /// ```
+    #[unstable(feature = "vec_deque_pop_if", issue = "135889")]
+    pub fn pop_front_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option<T> {
+        let first = self.front_mut()?;
+        if predicate(first) { self.pop_front() } else { None }
+    }
+
+    /// Removes and returns the last element from the deque if the predicate
+    /// returns `true`, or [`None`] if the predicate returns false or the deque
+    /// is empty (the predicate will not be called in that case).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(vec_deque_pop_if)]
+    /// use std::collections::VecDeque;
+    ///
+    /// let mut deque: VecDeque<i32> = vec![0, 1, 2, 3, 4].into();
+    /// let pred = |x: &mut i32| *x % 2 == 0;
+    ///
+    /// assert_eq!(deque.pop_back_if(pred), Some(4));
+    /// assert_eq!(deque, [0, 1, 2, 3]);
+    /// assert_eq!(deque.pop_back_if(pred), None);
+    /// ```
+    #[unstable(feature = "vec_deque_pop_if", issue = "135889")]
+    pub fn pop_back_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option<T> {
+        let first = self.back_mut()?;
+        if predicate(first) { self.pop_back() } else { None }
+    }
+
     /// Prepends an element to the deque.
     ///
     /// # Examples
diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs
index 393bdfe48b7..d8364d750fa 100644
--- a/library/alloc/tests/lib.rs
+++ b/library/alloc/tests/lib.rs
@@ -38,6 +38,7 @@
 #![feature(str_as_str)]
 #![feature(strict_provenance_lints)]
 #![feature(vec_pop_if)]
+#![feature(vec_deque_pop_if)]
 #![feature(unique_rc_arc)]
 #![feature(macro_metavar_expr_concat)]
 #![allow(internal_features)]
diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs
index 4b8d3c735f7..1b03c29e5bd 100644
--- a/library/alloc/tests/vec_deque.rs
+++ b/library/alloc/tests/vec_deque.rs
@@ -81,6 +81,45 @@ fn test_parameterized<T: Clone + PartialEq + Debug>(a: T, b: T, c: T, d: T) {
 }
 
 #[test]
+fn test_pop_if() {
+    let mut deq: VecDeque<_> = vec![0, 1, 2, 3, 4].into();
+    let pred = |x: &mut i32| *x % 2 == 0;
+
+    assert_eq!(deq.pop_front_if(pred), Some(0));
+    assert_eq!(deq, [1, 2, 3, 4]);
+
+    assert_eq!(deq.pop_front_if(pred), None);
+    assert_eq!(deq, [1, 2, 3, 4]);
+
+    assert_eq!(deq.pop_back_if(pred), Some(4));
+    assert_eq!(deq, [1, 2, 3]);
+
+    assert_eq!(deq.pop_back_if(pred), None);
+    assert_eq!(deq, [1, 2, 3]);
+}
+
+#[test]
+fn test_pop_if_empty() {
+    let mut deq = VecDeque::<i32>::new();
+    assert_eq!(deq.pop_front_if(|_| true), None);
+    assert_eq!(deq.pop_back_if(|_| true), None);
+    assert!(deq.is_empty());
+}
+
+#[test]
+fn test_pop_if_mutates() {
+    let mut v: VecDeque<_> = vec![-1, 1].into();
+    let pred = |x: &mut i32| {
+        *x *= 2;
+        false
+    };
+    assert_eq!(v.pop_front_if(pred), None);
+    assert_eq!(v, [-2, 1]);
+    assert_eq!(v.pop_back_if(pred), None);
+    assert_eq!(v, [-2, 2]);
+}
+
+#[test]
 fn test_push_front_grow() {
     let mut deq = VecDeque::new();
     for i in 0..66 {
diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs
index ba61679564a..28329bb0908 100644
--- a/library/core/src/array/mod.rs
+++ b/library/core/src/array/mod.rs
@@ -156,7 +156,6 @@ pub const fn from_mut<T>(s: &mut T) -> &mut [T; 1] {
 
 /// The error type returned when a conversion from a slice to an array fails.
 #[stable(feature = "try_from", since = "1.34.0")]
-#[rustc_allowed_through_unstable_modules]
 #[derive(Debug, Copy, Clone)]
 pub struct TryFromSliceError(());
 
diff --git a/src/doc/rustc-dev-guide/src/stability.md b/src/doc/rustc-dev-guide/src/stability.md
index 1bfe911c900..230925252ba 100644
--- a/src/doc/rustc-dev-guide/src/stability.md
+++ b/src/doc/rustc-dev-guide/src/stability.md
@@ -34,7 +34,8 @@ Previously, due to a [rustc bug], stable items inside unstable modules were
 available to stable code in that location.
 As of <!-- date-check --> September 2024, items with [accidentally stabilized
 paths] are marked with the `#[rustc_allowed_through_unstable_modules]` attribute
-to prevent code dependent on those paths from breaking.
+to prevent code dependent on those paths from breaking. Do *not* add this attribute
+to any more items unless that is needed to avoid breaking changes.
 
 The `unstable` attribute may also have the `soft` value, which makes it a
 future-incompatible deny-by-default lint instead of a hard error. This is used
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index deeabd810d3..a706926f743 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -260,6 +260,7 @@ target | std | host | notes
 [`aarch64-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD
 [`aarch64-unknown-nto-qnx700`](platform-support/nto-qnx.md) | ? |  | ARM64 QNX Neutrino 7.0 RTOS |
 [`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ |  | ARM64 QNX Neutrino 7.1 RTOS |
+[`aarch64-unknown-nuttx`](platform-support/nuttx.md) | * |  | ARM64 with NuttX
 [`aarch64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | ARM64 OpenBSD
 [`aarch64-unknown-redox`](platform-support/redox.md) | ✓ |  | ARM64 Redox OS
 [`aarch64-unknown-teeos`](platform-support/aarch64-unknown-teeos.md) | ? |  | ARM64 TEEOS |
@@ -295,6 +296,8 @@ target | std | host | notes
 [`armv7k-apple-watchos`](platform-support/apple-watchos.md) | ✓ |  | Armv7-A Apple WatchOS
 [`armv7s-apple-ios`](platform-support/apple-ios.md) | ✓ |  | Armv7-A Apple-A6 Apple iOS
 [`armv8r-none-eabihf`](platform-support/armv8r-none-eabihf.md) | * |  | Bare Armv8-R, hardfloat
+[`armv7a-nuttx-eabi`](platform-support/nuttx.md) | * |  | ARMv7-A with NuttX
+[`armv7a-nuttx-eabihf`](platform-support/nuttx.md) | * |  | ARMv7-A with NuttX, hardfloat
 `avr-unknown-gnu-atmega328` | * |  | AVR. Requires `-Z build-std=core`
 `bpfeb-unknown-none` | * |  | BPF (big endian)
 `bpfel-unknown-none` | * |  | BPF (little endian)
@@ -389,6 +392,8 @@ target | std | host | notes
 [`thumbv6m-nuttx-eabi`](platform-support/nuttx.md) | * |  | ARMv6M with NuttX
 `thumbv7a-pc-windows-msvc` |  |  |
 [`thumbv7a-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) |  |  |
+[`thumbv7a-nuttx-eabi`](platform-support/nuttx.md) | * |  | ARMv7-A with NuttX
+[`thumbv7a-nuttx-eabihf`](platform-support/nuttx.md) | * |  | ARMv7-A with NuttX, hardfloat
 [`thumbv7em-nuttx-eabi`](platform-support/nuttx.md) | * |  | ARMv7EM with NuttX
 [`thumbv7em-nuttx-eabihf`](platform-support/nuttx.md) | * |  | ARMv7EM with NuttX, hardfloat
 [`thumbv7m-nuttx-eabi`](platform-support/nuttx.md) | * |  | ARMv7M with NuttX
diff --git a/src/doc/rustc/src/platform-support/nuttx.md b/src/doc/rustc/src/platform-support/nuttx.md
index 433a092aab2..f76fe0887b5 100644
--- a/src/doc/rustc/src/platform-support/nuttx.md
+++ b/src/doc/rustc/src/platform-support/nuttx.md
@@ -20,8 +20,13 @@ The target name follow this format: `ARCH[-VENDOR]-nuttx-ABI`, where `ARCH` is t
 
 The following target names are defined:
 
-- `thumbv6m-nuttx-eal`
-- `thumbv7m-nuttx-eal`
+- `aarch64-unknown-nuttx`
+- `armv7a-nuttx-eabi`
+- `armv7a-nuttx-eabihf`
+- `thumbv6m-nuttx-eabi`
+- `thumbv7a-nuttx-eabi`
+- `thumbv7a-nuttx-eabihf`
+- `thumbv7m-nuttx-eabi`
 - `thumbv7em-nuttx-eabi`
 - `thumbv7em-nuttx-eabihf`
 - `thumbv8m.base-nuttx-eabi`
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 33166a39574..afe81937495 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -622,7 +622,7 @@ impl FromClean<clean::Type> for Type {
 impl FromClean<clean::Path> for Path {
     fn from_clean(path: clean::Path, renderer: &JsonRenderer<'_>) -> Path {
         Path {
-            name: path.last_opt().map_or(String::from(""), |s| String::from(s.as_str())),
+            path: path.whole_name(),
             id: renderer.id_from_item_default(path.def_id().into()),
             args: path.segments.last().map(|args| Box::new(args.clone().args.into_json(renderer))),
         }
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 916f0ab3cc8..e67e17a73cb 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -30,7 +30,7 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
 /// This integer is incremented with every breaking change to the API,
 /// and is returned along with the JSON blob as [`Crate::format_version`].
 /// Consuming code should assert that this value matches the format version(s) that it supports.
-pub const FORMAT_VERSION: u32 = 38;
+pub const FORMAT_VERSION: u32 = 39;
 
 /// The root of the emitted JSON blob.
 ///
@@ -1036,16 +1036,20 @@ pub enum Type {
 /// A type that has a simple path to it. This is the kind of type of structs, unions, enums, etc.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Path {
-    /// The name of the type as declared, e.g. in
+    /// The path of the type.
+    ///
+    /// This will be the path that is *used* (not where it is defined), so
+    /// multiple `Path`s may have different values for this field even if
+    /// they all refer to the same item. e.g.
     ///
     /// ```rust
-    /// mod foo {
-    ///     struct Bar;
-    /// }
+    /// pub type Vec1 = std::vec::Vec<i32>; // path: "std::vec::Vec"
+    /// pub type Vec2 = Vec<i32>; // path: "Vec"
+    /// pub type Vec3 = std::prelude::v1::Vec<i32>; // path: "std::prelude::v1::Vec"
     /// ```
-    ///
-    /// for `foo::Bar`, this field will be `Bar`.
-    pub name: String,
+    //
+    // Example tested in ./tests/rustdoc-json/path_name.rs
+    pub path: String,
     /// The ID of the type.
     pub id: Id,
     /// Generic arguments to the type.
diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs
index 7fcb8ffd1f9..a37e6c2eb5c 100644
--- a/src/tools/jsondoclint/src/validator/tests.rs
+++ b/src/tools/jsondoclint/src/validator/tests.rs
@@ -163,7 +163,7 @@ fn errors_on_missing_path() {
                     sig: FunctionSignature {
                         inputs: vec![],
                         output: Some(Type::ResolvedPath(Path {
-                            name: "Bar".to_owned(),
+                            path: "Bar".to_owned(),
                             id: Id(1),
                             args: None,
                         })),
@@ -191,7 +191,7 @@ fn errors_on_missing_path() {
 
     check(&krate, &[Error {
         kind: ErrorKind::Custom(
-            r#"No entry in '$.paths' for Path { name: "Bar", id: Id(1), args: None }"#.to_owned(),
+            r#"No entry in '$.paths' for Path { path: "Bar", id: Id(1), args: None }"#.to_owned(),
         ),
         id: Id(1),
     }]);
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index de3380502bf..5878cce5ffb 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -3857,7 +3857,6 @@ ui/suggestions/issue-106443-sugg-clone-for-arg.rs
 ui/suggestions/issue-106443-sugg-clone-for-bound.rs
 ui/suggestions/issue-107860.rs
 ui/suggestions/issue-108470.rs
-ui/suggestions/issue-109195.rs
 ui/suggestions/issue-109291.rs
 ui/suggestions/issue-109396.rs
 ui/suggestions/issue-109436.rs
diff --git a/tests/assembly/targets/targets-elf.rs b/tests/assembly/targets/targets-elf.rs
index acfa868c2df..6bb3389c409 100644
--- a/tests/assembly/targets/targets-elf.rs
+++ b/tests/assembly/targets/targets-elf.rs
@@ -66,6 +66,9 @@
 //@ revisions: aarch64_unknown_teeos
 //@ [aarch64_unknown_teeos] compile-flags: --target aarch64-unknown-teeos
 //@ [aarch64_unknown_teeos] needs-llvm-components: aarch64
+//@ revisions: aarch64_unknown_nuttx
+//@ [aarch64_unknown_nuttx] compile-flags: --target aarch64-unknown-nuttx
+//@ [aarch64_unknown_nuttx] needs-llvm-components: aarch64
 //@ revisions: aarch64_unknown_trusty
 //@ [aarch64_unknown_trusty] compile-flags: --target aarch64-unknown-trusty
 //@ [aarch64_unknown_trusty] needs-llvm-components: aarch64
@@ -177,6 +180,12 @@
 //@ revisions: armv7a_none_eabihf
 //@ [armv7a_none_eabihf] compile-flags: --target armv7a-none-eabihf
 //@ [armv7a_none_eabihf] needs-llvm-components: arm
+//@ revisions: armv7a_nuttx_eabi
+//@ [armv7a_nuttx_eabi] compile-flags: --target armv7a-nuttx-eabi
+//@ [armv7a_nuttx_eabi] needs-llvm-components: arm
+//@ revisions: armv7a_nuttx_eabihf
+//@ [armv7a_nuttx_eabihf] compile-flags: --target armv7a-nuttx-eabihf
+//@ [armv7a_nuttx_eabihf] needs-llvm-components: arm
 //@ revisions: armv7r_none_eabi
 //@ [armv7r_none_eabi] compile-flags: --target armv7r-none-eabi
 //@ [armv7r_none_eabi] needs-llvm-components: arm
@@ -621,6 +630,12 @@
 //@ revisions: thumbv6m_nuttx_eabi
 //@ [thumbv6m_nuttx_eabi] compile-flags: --target thumbv6m-nuttx-eabi
 //@ [thumbv6m_nuttx_eabi] needs-llvm-components: arm
+//@ revisions: thumbv7a_nuttx_eabi
+//@ [thumbv7a_nuttx_eabi] compile-flags: --target thumbv7a-nuttx-eabi
+//@ [thumbv7a_nuttx_eabi] needs-llvm-components: arm
+//@ revisions: thumbv7a_nuttx_eabihf
+//@ [thumbv7a_nuttx_eabihf] compile-flags: --target thumbv7a-nuttx-eabihf
+//@ [thumbv7a_nuttx_eabihf] needs-llvm-components: arm
 //@ revisions: thumbv7m_nuttx_eabi
 //@ [thumbv7m_nuttx_eabi] compile-flags: --target thumbv7m-nuttx-eabi
 //@ [thumbv7m_nuttx_eabi] needs-llvm-components: arm
diff --git a/tests/rustdoc-json/auxiliary/defines_and_reexports.rs b/tests/rustdoc-json/auxiliary/defines_and_reexports.rs
new file mode 100644
index 00000000000..72434ef152f
--- /dev/null
+++ b/tests/rustdoc-json/auxiliary/defines_and_reexports.rs
@@ -0,0 +1,10 @@
+pub mod m1 {
+    pub struct InPubMod;
+}
+
+mod m2 {
+    pub struct InPrivMod;
+}
+
+pub use m1::{InPubMod, InPubMod as InPubMod2};
+pub use m2::{InPrivMod, InPrivMod as InPrivMod2};
diff --git a/tests/rustdoc-json/blanket_impls.rs b/tests/rustdoc-json/blanket_impls.rs
index f2acabbe372..bf0983e66a1 100644
--- a/tests/rustdoc-json/blanket_impls.rs
+++ b/tests/rustdoc-json/blanket_impls.rs
@@ -4,5 +4,5 @@
 
 //@ has "$.index[*][?(@.name=='Error')].inner.assoc_type"
 //@ has "$.index[*][?(@.name=='Error')].inner.assoc_type.type.resolved_path"
-//@ has "$.index[*][?(@.name=='Error')].inner.assoc_type.type.resolved_path.name" \"Infallible\"
+//@ has "$.index[*][?(@.name=='Error')].inner.assoc_type.type.resolved_path.path" \"Infallible\"
 pub struct ForBlanketTryFromImpl;
diff --git a/tests/rustdoc-json/fns/async_return.rs b/tests/rustdoc-json/fns/async_return.rs
index 18a8a586e76..ff88fa99c61 100644
--- a/tests/rustdoc-json/fns/async_return.rs
+++ b/tests/rustdoc-json/fns/async_return.rs
@@ -17,7 +17,7 @@ pub async fn get_int_async() -> i32 {
     42
 }
 
-//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.name" '"Future"'
+//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.path" '"Future"'
 //@ is "$.index[*][?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].name" '"Output"'
 //@ is "$.index[*][?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive"  \"i32\"
 //@ is "$.index[*][?(@.name=='get_int_future')].inner.function.header.is_async" false
@@ -25,7 +25,7 @@ pub fn get_int_future() -> impl Future<Output = i32> {
     async { 42 }
 }
 
-//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.name" '"Future"'
+//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.path" '"Future"'
 //@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].name" '"Output"'
 //@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive" \"i32\"
 //@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.header.is_async" true
diff --git a/tests/rustdoc-json/impl-trait-in-assoc-type.rs b/tests/rustdoc-json/impl-trait-in-assoc-type.rs
index 907a0f6c603..14ea2950769 100644
--- a/tests/rustdoc-json/impl-trait-in-assoc-type.rs
+++ b/tests/rustdoc-json/impl-trait-in-assoc-type.rs
@@ -10,7 +10,7 @@ impl IntoIterator for AlwaysTrue {
     type Item = bool;
 
     //@ count '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[*]' 1
-    //@ is    '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.name' '"Iterator"'
+    //@ is    '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.path' '"Iterator"'
     //@ count '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[*]' 1
     //@ is    '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].name' '"Item"'
     //@ is    '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive' '"bool"'
diff --git a/tests/rustdoc-json/path_name.rs b/tests/rustdoc-json/path_name.rs
new file mode 100644
index 00000000000..67843dfc8ff
--- /dev/null
+++ b/tests/rustdoc-json/path_name.rs
@@ -0,0 +1,83 @@
+// Test for the Path::name field within a single crate.
+//
+// See https://github.com/rust-lang/rust/issues/135600
+// and https://github.com/rust-lang/rust/pull/134880#issuecomment-2596386111
+//
+// ignore-tidy-linelength
+//@ aux-build: defines_and_reexports.rs
+extern crate defines_and_reexports;
+
+mod priv_mod {
+    pub struct InPrivMod;
+}
+
+pub mod pub_mod {
+    pub struct InPubMod;
+}
+
+use priv_mod::InPrivMod as InPrivMod3;
+pub use priv_mod::{InPrivMod, InPrivMod as InPrivMod2};
+use pub_mod::InPubMod as InPubMod3;
+pub use pub_mod::{InPubMod, InPubMod as InPubMod2};
+
+//@ is "$.index[*][?(@.name=='T0')].inner.type_alias.type.resolved_path.path" '"priv_mod::InPrivMod"'
+pub type T0 = priv_mod::InPrivMod;
+//@ is "$.index[*][?(@.name=='T1')].inner.type_alias.type.resolved_path.path" '"InPrivMod"'
+pub type T1 = InPrivMod;
+//@ is "$.index[*][?(@.name=='T2')].inner.type_alias.type.resolved_path.path" '"InPrivMod2"'
+pub type T2 = InPrivMod2;
+//@ is "$.index[*][?(@.name=='T3')].inner.type_alias.type.resolved_path.path" '"priv_mod::InPrivMod"'
+pub type T3 = InPrivMod3;
+
+//@ is "$.index[*][?(@.name=='U0')].inner.type_alias.type.resolved_path.path" '"pub_mod::InPubMod"'
+pub type U0 = pub_mod::InPubMod;
+//@ is "$.index[*][?(@.name=='U1')].inner.type_alias.type.resolved_path.path" '"InPubMod"'
+pub type U1 = InPubMod;
+//@ is "$.index[*][?(@.name=='U2')].inner.type_alias.type.resolved_path.path" '"InPubMod2"'
+pub type U2 = InPubMod2;
+//@ is "$.index[*][?(@.name=='U3')].inner.type_alias.type.resolved_path.path" '"pub_mod::InPubMod"'
+pub type U3 = InPubMod3;
+
+// Check we only have paths for structs at their original path
+//@ ismany "$.paths[*][?(@.crate_id==0 && @.kind=='struct')].path" '["path_name", "priv_mod", "InPrivMod"]' '["path_name", "pub_mod", "InPubMod"]'
+
+pub use defines_and_reexports::{InPrivMod as XPrivMod, InPubMod as XPubMod};
+use defines_and_reexports::{InPrivMod as XPrivMod2, InPubMod as XPubMod2};
+
+//@ is "$.index[*][?(@.name=='X0')].inner.type_alias.type.resolved_path.path" '"defines_and_reexports::m1::InPubMod"'
+pub type X0 = defines_and_reexports::m1::InPubMod;
+//@ is "$.index[*][?(@.name=='X1')].inner.type_alias.type.resolved_path.path" '"defines_and_reexports::InPubMod"'
+pub type X1 = defines_and_reexports::InPubMod;
+//@ is "$.index[*][?(@.name=='X2')].inner.type_alias.type.resolved_path.path" '"defines_and_reexports::InPubMod2"'
+pub type X2 = defines_and_reexports::InPubMod2;
+//@ is "$.index[*][?(@.name=='X3')].inner.type_alias.type.resolved_path.path" '"XPubMod"'
+pub type X3 = XPubMod;
+// N.B. This isn't the path as used *or* the original path!
+//@ is "$.index[*][?(@.name=='X4')].inner.type_alias.type.resolved_path.path" '"defines_and_reexports::InPubMod"'
+pub type X4 = XPubMod2;
+
+//@ is "$.index[*][?(@.name=='Y1')].inner.type_alias.type.resolved_path.path" '"defines_and_reexports::InPrivMod"'
+pub type Y1 = defines_and_reexports::InPrivMod;
+//@ is "$.index[*][?(@.name=='Y2')].inner.type_alias.type.resolved_path.path" '"defines_and_reexports::InPrivMod2"'
+pub type Y2 = defines_and_reexports::InPrivMod2;
+//@ is "$.index[*][?(@.name=='Y3')].inner.type_alias.type.resolved_path.path" '"XPrivMod"'
+pub type Y3 = XPrivMod;
+//@ is "$.index[*][?(@.name=='Y4')].inner.type_alias.type.resolved_path.path" '"defines_and_reexports::InPrivMod"'
+pub type Y4 = XPrivMod2;
+
+// For foreign items, $.paths contains the *origional* path, even if it's not publicly
+// assessable. This should probably be changed.
+
+//@ has "$.paths[*].path" '["defines_and_reexports", "m1", "InPubMod"]'
+//@ has "$.paths[*].path" '["defines_and_reexports", "m2", "InPrivMod"]'
+//@ !has "$.paths[*].path" '["defines_and_reexports", "InPubMod"]'
+//@ !has "$.paths[*].path" '["defines_and_reexports", "InPrivMod"]'
+
+// Tests for the example in the docs of Path::name.
+// If these change, chage the docs.
+//@ is "$.index[*][?(@.name=='Vec1')].inner.type_alias.type.resolved_path.path" '"std::vec::Vec"'
+pub type Vec1 = std::vec::Vec<i32>;
+//@ is "$.index[*][?(@.name=='Vec2')].inner.type_alias.type.resolved_path.path" '"Vec"'
+pub type Vec2 = Vec<i32>;
+//@ is "$.index[*][?(@.name=='Vec3')].inner.type_alias.type.resolved_path.path" '"std::prelude::v1::Vec"'
+pub type Vec3 = std::prelude::v1::Vec<i32>;
diff --git a/tests/rustdoc-json/return_private.rs b/tests/rustdoc-json/return_private.rs
index c238a536e0d..bfcbed89040 100644
--- a/tests/rustdoc-json/return_private.rs
+++ b/tests/rustdoc-json/return_private.rs
@@ -2,11 +2,13 @@
 // ignore-tidy-linelength
 
 mod secret {
+    //@ set struct_secret = "$.index[*][?(@.name == 'Secret' && @.inner.struct)].id"
     pub struct Secret;
 }
 
 //@ has "$.index[*][?(@.name=='get_secret')].inner.function"
-//@ is "$.index[*][?(@.name=='get_secret')].inner.function.sig.output.resolved_path.name" \"Secret\"
+//@ is "$.index[*][?(@.name=='get_secret')].inner.function.sig.output.resolved_path.path" '"secret::Secret"'
+//@ is "$.index[*][?(@.name=='get_secret')].inner.function.sig.output.resolved_path.id" $struct_secret
 pub fn get_secret() -> secret::Secret {
     secret::Secret
 }
diff --git a/tests/rustdoc-json/type/dyn.rs b/tests/rustdoc-json/type/dyn.rs
index 97c8689a7c8..f990a2cb53a 100644
--- a/tests/rustdoc-json/type/dyn.rs
+++ b/tests/rustdoc-json/type/dyn.rs
@@ -10,7 +10,7 @@ use std::fmt::Debug;
 //@ has    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias"
 //@ is    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.generics" '{"params": [], "where_predicates": []}'
 //@ has    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path"
-//@ is    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.name" \"Box\"
+//@ is    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.path" \"Box\"
 //@ is    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.constraints" []
 //@ count "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args" 1
 //@ has    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait"
@@ -19,9 +19,9 @@ use std::fmt::Debug;
 //@ is    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].generic_params" []
 //@ is    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[1].generic_params" []
 //@ is    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[2].generic_params" []
-//@ is    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].trait.name" '"Fn"'
-//@ is    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[1].trait.name" '"Send"'
-//@ is    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[2].trait.name" '"Sync"'
+//@ is    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].trait.path" '"Fn"'
+//@ is    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[1].trait.path" '"Send"'
+//@ is    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[2].trait.path" '"Sync"'
 //@ is    "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].trait.args" '{"parenthesized": {"inputs": [],"output": {"primitive": "i32"}}}'
 pub type SyncIntGen = Box<dyn Fn() -> i32 + Send + Sync + 'static>;
 
@@ -34,13 +34,13 @@ pub type SyncIntGen = Box<dyn Fn() -> i32 + Send + Sync + 'static>;
 //@ is "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.lifetime" null
 //@ count "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[*]" 1
 //@ is "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[0].generic_params" '[{"kind": {"lifetime": {"outlives": []}},"name": "'\''b"}]'
-//@ is "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[0].trait.name" '"Fn"'
+//@ is "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[0].trait.path" '"Fn"'
 //@ has "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[0].trait.args.parenthesized.inputs[0].borrowed_ref"
 //@ is "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[0].trait.args.parenthesized.inputs[0].borrowed_ref.lifetime" "\"'b\""
 //@ has "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[0].trait.args.parenthesized.output.borrowed_ref"
 //@ is "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[0].trait.args.parenthesized.output.borrowed_ref.lifetime" "\"'b\""
 pub type RefFn<'a> = &'a dyn for<'b> Fn(&'b i32) -> &'b i32;
 
-//@ is    "$.index[*][?(@.name=='WeirdOrder')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].trait.name" '"Send"'
-//@ is    "$.index[*][?(@.name=='WeirdOrder')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[1].trait.name" '"Debug"'
+//@ is    "$.index[*][?(@.name=='WeirdOrder')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].trait.path" '"Send"'
+//@ is    "$.index[*][?(@.name=='WeirdOrder')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[1].trait.path" '"Debug"'
 pub type WeirdOrder = Box<dyn Send + Debug>;
diff --git a/tests/rustdoc-json/type/generic_default.rs b/tests/rustdoc-json/type/generic_default.rs
index c1a05805014..7eaa299af5c 100644
--- a/tests/rustdoc-json/type/generic_default.rs
+++ b/tests/rustdoc-json/type/generic_default.rs
@@ -21,10 +21,10 @@ pub struct MyError {}
 //@ is    "$.index[*][?(@.name=='MyResult')].inner.type_alias.generics.params[0].kind.type.default" null
 //@ has    "$.index[*][?(@.name=='MyResult')].inner.type_alias.generics.params[1].kind.type.default.resolved_path"
 //@ is    "$.index[*][?(@.name=='MyResult')].inner.type_alias.generics.params[1].kind.type.default.resolved_path.id" $my_error
-//@ is    "$.index[*][?(@.name=='MyResult')].inner.type_alias.generics.params[1].kind.type.default.resolved_path.name" \"MyError\"
+//@ is    "$.index[*][?(@.name=='MyResult')].inner.type_alias.generics.params[1].kind.type.default.resolved_path.path" \"MyError\"
 //@ has    "$.index[*][?(@.name=='MyResult')].inner.type_alias.type.resolved_path"
 //@ is    "$.index[*][?(@.name=='MyResult')].inner.type_alias.type.resolved_path.id" $result
-//@ is    "$.index[*][?(@.name=='MyResult')].inner.type_alias.type.resolved_path.name" \"Result\"
+//@ is    "$.index[*][?(@.name=='MyResult')].inner.type_alias.type.resolved_path.path" \"Result\"
 //@ is    "$.index[*][?(@.name=='MyResult')].inner.type_alias.type.resolved_path.args.angle_bracketed.constraints" []
 //@ has    "$.index[*][?(@.name=='MyResult')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.generic"
 //@ has    "$.index[*][?(@.name=='MyResult')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[1].type.generic"
diff --git a/tests/rustdoc-json/type/hrtb.rs b/tests/rustdoc-json/type/hrtb.rs
index 825720e9198..e71d9fc1e1e 100644
--- a/tests/rustdoc-json/type/hrtb.rs
+++ b/tests/rustdoc-json/type/hrtb.rs
@@ -15,7 +15,7 @@ where
 //@ is "$.index[*][?(@.name=='dynfn')].inner.function.sig.inputs[0][1].borrowed_ref.type.dyn_trait.lifetime" null
 //@ count "$.index[*][?(@.name=='dynfn')].inner.function.sig.inputs[0][1].borrowed_ref.type.dyn_trait.traits[*]" 1
 //@ is "$.index[*][?(@.name=='dynfn')].inner.function.sig.inputs[0][1].borrowed_ref.type.dyn_trait.traits[0].generic_params" '[{"kind": {"lifetime": {"outlives": []}},"name": "'\''a"},{"kind": {"lifetime": {"outlives": []}},"name": "'\''b"}]'
-//@ is "$.index[*][?(@.name=='dynfn')].inner.function.sig.inputs[0][1].borrowed_ref.type.dyn_trait.traits[0].trait.name" '"Fn"'
+//@ is "$.index[*][?(@.name=='dynfn')].inner.function.sig.inputs[0][1].borrowed_ref.type.dyn_trait.traits[0].trait.path" '"Fn"'
 pub fn dynfn(f: &dyn for<'a, 'b> Fn(&'a i32, &'b i32)) {
     let zero = 0;
     f(&zero, &zero);
diff --git a/tests/ui/drop/drop-order-comparisons.e2021.fixed b/tests/ui/drop/drop-order-comparisons.e2021.fixed
new file mode 100644
index 00000000000..78cf421cfbf
--- /dev/null
+++ b/tests/ui/drop/drop-order-comparisons.e2021.fixed
@@ -0,0 +1,575 @@
+// This tests various aspects of the drop order with a focus on:
+//
+// - The lifetime of temporaries with the `if let` construct (and with
+// various similar constructs) and how these lifetimes were shortened
+// for `if let` in Rust 2024.
+//
+// - The shortening of the lifetimes of temporaries in tail
+// expressions in Rust 2024.
+//
+// - The behavior of `let` chains and how this behavior compares to
+// nested `if let` expressions and chained `let .. else` statements.
+//
+// In the tests below, `Events` tracks a sequence of numbered events.
+// Calling `e.mark(..)` logs a numbered event immediately.  Calling
+// `e.ok(..)` or `e.err(..)` returns an `Ok(_)` or `Err(_)` value,
+// respectively, and logs the numbered event when that value is
+// dropped.  Calling `e.assert()` verifies that the correct number of
+// events were logged and that they were logged in the correct order.
+
+//@ revisions: e2021 e2024
+//@ [e2021] edition: 2021
+//@ [e2021] run-rustfix
+//@ [e2021] rustfix-only-machine-applicable
+//@ [e2024] edition: 2024
+//@ run-pass
+
+#![feature(let_chains)]
+#![cfg_attr(e2021, warn(rust_2024_compatibility))]
+
+fn t_bindings() {
+    let e = Events::new();
+    _ = {
+        e.mark(1);
+        let _v = e.ok(8);
+        let _v = e.ok(2).is_ok();
+        let _ = e.ok(3);
+        let Ok(_) = e.ok(4) else { unreachable!() };
+        let Ok(_) = e.ok(5).as_ref() else { unreachable!() };
+        let _v = e.ok(7);
+        e.mark(6);
+    };
+    e.assert(8);
+}
+
+fn t_tuples() {
+    let e = Events::new();
+    _ = (e.ok(1), e.ok(4).is_ok(), e.ok(2), e.ok(3).is_ok());
+    e.assert(4);
+}
+
+fn t_arrays() {
+    let e = Events::new();
+    trait Tr {}
+    impl<T> Tr for T {}
+    fn b<'a, T: 'a>(x: T) -> Box<dyn Tr + 'a> {
+        Box::new(x)
+    }
+    _ = [b(e.ok(1)), b(e.ok(4).is_ok()), b(e.ok(2)), b(e.ok(3).is_ok())];
+    e.assert(4);
+}
+
+fn t_fncalls() {
+    let e = Events::new();
+    let f = |_, _, _, _| {};
+    _ = f(e.ok(2), e.ok(4).is_ok(), e.ok(1), e.ok(3).is_ok());
+    e.assert(4);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_tailexpr_bindings() {
+    let e = Events::new();
+    _ = ({
+        let _v = e.ok(2);
+        let _v = e.ok(1);
+        e.ok(5).is_ok()
+        //[e2021]~^ WARN relative drop order changing in Rust 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+    }, e.mark(3), e.ok(4));
+    e.assert(5);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_tailexpr_bindings() {
+    let e = Events::new();
+    _ = ({
+        let _v = e.ok(3);
+        let _v = e.ok(2);
+        e.ok(1).is_ok()
+    }, e.mark(4), e.ok(5));
+    e.assert(5);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_tailexpr_tuples() {
+    let e = Events::new();
+    _ = ({
+        (e.ok(2), e.ok(6).is_ok(), e.ok(3), e.ok(5).is_ok())
+        //[e2021]~^ WARN relative drop order changing in Rust 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+        //[e2021]~| WARN relative drop order changing in Rust 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+    }, e.mark(1), e.ok(4));
+    e.assert(6);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_tailexpr_tuples() {
+    let e = Events::new();
+    _ = ({
+        (e.ok(4), e.ok(2).is_ok(), e.ok(5), e.ok(1).is_ok())
+    }, e.mark(3), e.ok(6));
+    e.assert(6);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_then() {
+    let e = Events::new();
+    _ = (match e.ok(4).as_ref() { Ok(_) => {
+            //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+            //[e2021]~| WARN this changes meaning in Rust 2024
+            e.mark(1);
+    } _ => {}}, e.mark(2), e.ok(3));
+    e.assert(4);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_then() {
+    let e = Events::new();
+    _ = (if let Ok(_) = e.ok(2).as_ref() {
+            e.mark(1);
+    }, e.mark(3), e.ok(4));
+    e.assert(4);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_else() {
+    let e = Events::new();
+    _ = (match e.err(4).as_ref() { Ok(_) => {} _ => {
+            //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+            //[e2021]~| WARN this changes meaning in Rust 2024
+            e.mark(1);
+    }}, e.mark(2), e.ok(3));
+    e.assert(4);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_else() {
+    let e = Events::new();
+    _ = (if let Ok(_) = e.err(1).as_ref() {} else {
+            e.mark(2);
+    }, e.mark(3), e.ok(4));
+    e.assert(4);
+}
+
+#[rustfmt::skip]
+fn t_match_then() {
+    let e = Events::new();
+    _ = (match e.ok(4).as_ref() {
+            Ok(_) => e.mark(1),
+            _ => unreachable!(),
+    }, e.mark(2), e.ok(3));
+    e.assert(4);
+}
+
+#[rustfmt::skip]
+fn t_match_else() {
+    let e = Events::new();
+    _ = (match e.err(4).as_ref() {
+            Ok(_) => unreachable!(),
+            _ => e.mark(1),
+    }, e.mark(2), e.ok(3));
+    e.assert(4);
+}
+
+#[rustfmt::skip]
+fn t_let_else_then() {
+    let e = Events::new();
+    _ = ('top: {
+         'chain: {
+            let Ok(_) = e.ok(1).as_ref() else { break 'chain };
+            // The "then" branch:
+            e.mark(2);
+            break 'top;
+        }
+        // The "else" branch:
+        unreachable!()
+    }, e.mark(3), e.ok(4));
+    e.assert(4);
+}
+
+#[rustfmt::skip]
+fn t_let_else_else() {
+    let e = Events::new();
+    _ = ('top: {
+         'chain: {
+            let Ok(_) = e.err(1).as_ref() else { break 'chain };
+            // The "then" branch:
+            unreachable!();
+            #[allow(unreachable_code)]
+            break 'top;
+        }
+        // The "else" branch:
+        e.mark(2);
+    }, e.mark(3), e.ok(4));
+    e.assert(4);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_then_tailexpr() {
+    let e = Events::new();
+    _ = ({
+        if let Ok(_) = e.ok(4).as_ref() {
+            //[e2021]~^ WARN relative drop order changing in Rust 2024
+            //[e2021]~| WARN this changes meaning in Rust 2024
+            e.mark(1);
+        }
+    }, e.mark(2), e.ok(3));
+    e.assert(4);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_then_tailexpr() {
+    let e = Events::new();
+    _ = ({
+        if let Ok(_) = e.ok(2).as_ref() {
+            e.mark(1);
+        }
+    }, e.mark(3), e.ok(4));
+    e.assert(4);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_else_tailexpr() {
+    let e = Events::new();
+    _ = ({
+        match e.err(4).as_ref() { Ok(_) => {} _ => {
+            //[e2021]~^ WARN relative drop order changing in Rust 2024
+            //[e2021]~| WARN this changes meaning in Rust 2024
+            //[e2021]~| WARN if let` assigns a shorter lifetime since Edition 2024
+            //[e2021]~| WARN this changes meaning in Rust 2024
+            e.mark(1);
+        }}
+    }, e.mark(2), e.ok(3));
+    e.assert(4);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_else_tailexpr() {
+    let e = Events::new();
+    _ = ({
+        if let Ok(_) = e.err(1).as_ref() {} else {
+            e.mark(2);
+        }
+    }, e.mark(3), e.ok(4));
+    e.assert(4);
+}
+
+#[rustfmt::skip]
+fn t_if_let_nested_then() {
+    let e = Events::new();
+    _ = {
+        // The unusual formatting, here and below, is to make the
+        // comparison with `let` chains more direct.
+        if e.ok(1).is_ok() {
+        if let true = e.ok(9).is_ok() {
+        if let Ok(_v) = e.ok(8) {
+        if let Ok(_) = e.ok(7) {
+        if let Ok(_) = e.ok(6).as_ref() {
+        if e.ok(2).is_ok() {
+        if let Ok(_v) = e.ok(5) {
+        if let Ok(_) = e.ok(4).as_ref() {
+            e.mark(3);
+        }}}}}}}}
+    };
+    e.assert(9);
+}
+
+#[rustfmt::skip]
+fn t_let_else_chained_then() {
+    let e = Events::new();
+    _ = 'top: {
+        'chain: {
+            if e.ok(1).is_ok() {} else { break 'chain };
+            let true = e.ok(2).is_ok() else { break 'chain };
+            let Ok(_v) = e.ok(9) else { break 'chain };
+            let Ok(_) = e.ok(3) else { break 'chain };
+            let Ok(_) = e.ok(4).as_ref() else { break 'chain };
+            if e.ok(5).is_ok() {} else { break 'chain };
+            let Ok(_v) = e.ok(8) else { break 'chain };
+            let Ok(_) = e.ok(6).as_ref() else { break 'chain };
+            // The "then" branch:
+            e.mark(7);
+            break 'top;
+        }
+        // The "else" branch:
+        unreachable!()
+    };
+    e.assert(9);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_chains_then() {
+    let e = Events::new();
+    _ = if e.ok(1).is_ok()
+        && let true = e.ok(9).is_ok()
+        && let Ok(_v) = e.ok(5)
+        && let Ok(_) = e.ok(8)
+        && let Ok(_) = e.ok(7).as_ref()
+        && e.ok(2).is_ok()
+        && let Ok(_v) = e.ok(4)
+        && let Ok(_) = e.ok(6).as_ref() {
+            e.mark(3);
+        };
+    e.assert(9);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_chains_then() {
+    let e = Events::new();
+    _ = if e.ok(1).is_ok()
+        && let true = e.ok(9).is_ok()
+        && let Ok(_v) = e.ok(8)
+        && let Ok(_) = e.ok(7)
+        && let Ok(_) = e.ok(6).as_ref()
+        && e.ok(2).is_ok()
+        && let Ok(_v) = e.ok(5)
+        && let Ok(_) = e.ok(4).as_ref() {
+            e.mark(3);
+        };
+    e.assert(9);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_nested_else() {
+    let e = Events::new();
+    _ = if e.err(1).is_ok() {} else {
+        match e.err(9).is_ok() { true => {} _ => {
+        //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+        match e.err(8) { Ok(_v) => {} _ => {
+        //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+        match e.err(7) { Ok(_) => {} _ => {
+        //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+        match e.err(6).as_ref() { Ok(_) => {} _ => {
+        //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+        if e.err(2).is_ok() {} else {
+        match e.err(5) { Ok(_v) => {} _ => {
+        //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+        match e.err(4) { Ok(_) => {} _ => {
+            //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+            //[e2021]~| WARN this changes meaning in Rust 2024
+            e.mark(3);
+        }}}}}}}}}}}}}};
+    e.assert(9);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_nested_else() {
+    let e = Events::new();
+    _ = if e.err(1).is_ok() {} else {
+        if let true = e.err(2).is_ok() {} else {
+        if let Ok(_v) = e.err(3) {} else {
+        if let Ok(_) = e.err(4) {} else {
+        if let Ok(_) = e.err(5).as_ref() {} else {
+        if e.err(6).is_ok() {} else {
+        if let Ok(_v) = e.err(7) {} else {
+        if let Ok(_) = e.err(8) {} else {
+            e.mark(9);
+        }}}}}}}};
+    e.assert(9);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_nested_then_else() {
+    let e = Events::new();
+    _ = if e.ok(1).is_ok() {
+        if let true = e.ok(9).is_ok() {
+        if let Ok(_v) = e.ok(8) {
+        if let Ok(_) = e.ok(7) {
+        if let Ok(_) = e.ok(6).as_ref() {
+        if e.ok(2).is_ok() {
+        if let Ok(_v) = e.ok(5) {
+        match e.err(4).as_ref() { Ok(_) => {} _ => {
+            //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+            //[e2021]~| WARN this changes meaning in Rust 2024
+            e.mark(3);
+        }}}}}}}}};
+    e.assert(9);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_nested_then_else() {
+    let e = Events::new();
+    _ = if e.ok(1).is_ok() {
+        if let true = e.ok(9).is_ok() {
+        if let Ok(_v) = e.ok(8) {
+        if let Ok(_) = e.ok(7) {
+        if let Ok(_) = e.ok(6).as_ref() {
+        if e.ok(2).is_ok() {
+        if let Ok(_v) = e.ok(5) {
+        if let Ok(_) = e.err(3).as_ref() {} else {
+            e.mark(4);
+        }}}}}}}};
+    e.assert(9);
+}
+
+#[rustfmt::skip]
+fn t_let_else_chained_then_else() {
+    let e = Events::new();
+    _ = 'top: {
+        'chain: {
+            if e.ok(1).is_ok() {} else { break 'chain };
+            let true = e.ok(2).is_ok() else { break 'chain };
+            let Ok(_v) = e.ok(8) else { break 'chain };
+            let Ok(_) = e.ok(3) else { break 'chain };
+            let Ok(_) = e.ok(4).as_ref() else { break 'chain };
+            if e.ok(5).is_ok() {} else { break 'chain };
+            let Ok(_v) = e.ok(7) else { break 'chain };
+            let Ok(_) = e.err(6).as_ref() else { break 'chain };
+            // The "then" branch:
+            unreachable!();
+            #[allow(unreachable_code)]
+            break 'top;
+        }
+        // The "else" branch:
+        e.mark(9);
+    };
+    e.assert(9);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_chains_then_else() {
+    let e = Events::new();
+    _ = if e.ok(1).is_ok()
+        && let true = e.ok(9).is_ok()
+        && let Ok(_v) = e.ok(4)
+        && let Ok(_) = e.ok(8)
+        && let Ok(_) = e.ok(7).as_ref()
+        && e.ok(2).is_ok()
+        && let Ok(_v) = e.ok(3)
+        && let Ok(_) = e.err(6) {} else {
+            e.mark(5);
+        };
+    e.assert(9);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_chains_then_else() {
+    let e = Events::new();
+    _ = if e.ok(1).is_ok()
+        && let true = e.ok(8).is_ok()
+        && let Ok(_v) = e.ok(7)
+        && let Ok(_) = e.ok(6)
+        && let Ok(_) = e.ok(5).as_ref()
+        && e.ok(2).is_ok()
+        && let Ok(_v) = e.ok(4)
+        && let Ok(_) = e.err(3) {} else {
+            e.mark(9);
+        };
+    e.assert(9);
+}
+
+fn main() {
+    t_bindings();
+    t_tuples();
+    t_arrays();
+    t_fncalls();
+    t_tailexpr_bindings();
+    t_tailexpr_tuples();
+    t_if_let_then();
+    t_if_let_else();
+    t_match_then();
+    t_match_else();
+    t_let_else_then();
+    t_let_else_else();
+    t_if_let_then_tailexpr();
+    t_if_let_else_tailexpr();
+    t_if_let_nested_then();
+    t_let_else_chained_then();
+    t_if_let_chains_then();
+    t_if_let_nested_else();
+    t_if_let_nested_then_else();
+    t_let_else_chained_then_else();
+    t_if_let_chains_then_else();
+}
+
+// # Test scaffolding
+
+use core::cell::RefCell;
+use std::collections::HashSet;
+
+/// A buffer to track the order of events.
+///
+/// First, numbered events are logged into this buffer.
+///
+/// Then, `assert` is called to verify that the correct number of
+/// events were logged, and that they were logged in the expected
+/// order.
+struct Events(RefCell<Option<Vec<u64>>>);
+
+impl Events {
+    const fn new() -> Self {
+        Self(RefCell::new(Some(Vec::new())))
+    }
+    #[track_caller]
+    fn assert(&self, max: u64) {
+        let buf = &self.0;
+        let v1 = buf.borrow().as_ref().unwrap().clone();
+        let mut v2 = buf.borrow().as_ref().unwrap().clone();
+        *buf.borrow_mut() = None;
+        v2.sort();
+        let uniq_len = v2.iter().collect::<HashSet<_>>().len();
+        // Check that the sequence is sorted.
+        assert_eq!(v1, v2);
+        // Check that there are no duplicates.
+        assert_eq!(v2.len(), uniq_len);
+        // Check that the length is the expected one.
+        assert_eq!(max, uniq_len as u64);
+        // Check that the last marker is the expected one.
+        assert_eq!(v2.last().unwrap(), &max);
+    }
+    /// Return an `Ok` value that logs its drop.
+    fn ok(&self, m: u64) -> Result<LogDrop<'_>, LogDrop<'_>> {
+        Ok(LogDrop(self, m))
+    }
+    /// Return an `Err` value that logs its drop.
+    fn err(&self, m: u64) -> Result<LogDrop, LogDrop> {
+        Err(LogDrop(self, m))
+    }
+    /// Log an event.
+    fn mark(&self, m: u64) {
+        self.0.borrow_mut().as_mut().unwrap().push(m);
+    }
+}
+
+impl Drop for Events {
+    fn drop(&mut self) {
+        if self.0.borrow().is_some() {
+            panic!("failed to call `Events::assert()`");
+        }
+    }
+}
+
+/// A type that logs its drop events.
+struct LogDrop<'b>(&'b Events, u64);
+
+impl<'b> Drop for LogDrop<'b> {
+    fn drop(&mut self) {
+        self.0.mark(self.1);
+    }
+}
diff --git a/tests/ui/drop/drop-order-comparisons.e2021.stderr b/tests/ui/drop/drop-order-comparisons.e2021.stderr
new file mode 100644
index 00000000000..158d18f6882
--- /dev/null
+++ b/tests/ui/drop/drop-order-comparisons.e2021.stderr
@@ -0,0 +1,477 @@
+warning: relative drop order changing in Rust 2024
+  --> $DIR/drop-order-comparisons.rs:76:9
+   |
+LL |       _ = ({
+   |  _________-
+LL | |         let _v = e.ok(2);
+   | |             --
+   | |             |
+   | |             `_v` calls a custom destructor
+   | |             `_v` will be dropped later as of Edition 2024
+LL | |         let _v = e.ok(1);
+   | |             --
+   | |             |
+   | |             this value will be stored in a temporary; let us call it `#2`
+   | |             `#2` will be dropped later as of Edition 2024
+LL | |         e.ok(5).is_ok()
+   | |         ^^^^^^^
+   | |         |
+   | |         this value will be stored in a temporary; let us call it `#3`
+   | |         up until Edition 2021 `#3` is dropped last but will be dropped earlier in Edition 2024
+...  |
+LL | |     }, e.mark(3), e.ok(4));
+   | |                          -
+   | |                          |
+   | |                          now the temporary value is dropped here, before the local variables in the block or statement
+   | |__________________________this value will be stored in a temporary; let us call it `#1`
+   |                            `#1` will be dropped later as of Edition 2024
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
+note: `#3` invokes this custom destructor
+  --> $DIR/drop-order-comparisons.rs:571:1
+   |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | |     fn drop(&mut self) {
+LL | |         self.0.mark(self.1);
+LL | |     }
+LL | | }
+   | |_^
+note: `#1` invokes this custom destructor
+  --> $DIR/drop-order-comparisons.rs:571:1
+   |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | |     fn drop(&mut self) {
+LL | |         self.0.mark(self.1);
+LL | |     }
+LL | | }
+   | |_^
+note: `_v` invokes this custom destructor
+  --> $DIR/drop-order-comparisons.rs:571:1
+   |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | |     fn drop(&mut self) {
+LL | |         self.0.mark(self.1);
+LL | |     }
+LL | | }
+   | |_^
+note: `#2` invokes this custom destructor
+  --> $DIR/drop-order-comparisons.rs:571:1
+   |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | |     fn drop(&mut self) {
+LL | |         self.0.mark(self.1);
+LL | |     }
+LL | | }
+   | |_^
+   = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
+note: the lint level is defined here
+  --> $DIR/drop-order-comparisons.rs:28:25
+   |
+LL | #![cfg_attr(e2021, warn(rust_2024_compatibility))]
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `#[warn(tail_expr_drop_order)]` implied by `#[warn(rust_2024_compatibility)]`
+
+warning: relative drop order changing in Rust 2024
+  --> $DIR/drop-order-comparisons.rs:100:45
+   |
+LL |       _ = ({
+   |  _________-
+LL | |         (e.ok(2), e.ok(6).is_ok(), e.ok(3), e.ok(5).is_ok())
+   | |                                             ^^^^^^^
+   | |                                             |
+   | |                                             this value will be stored in a temporary; let us call it `#2`
+   | |                                             up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024
+...  |
+LL | |     }, e.mark(1), e.ok(4));
+   | |                          -
+   | |                          |
+   | |                          now the temporary value is dropped here, before the local variables in the block or statement
+   | |__________________________this value will be stored in a temporary; let us call it `#1`
+   |                            `#1` will be dropped later as of Edition 2024
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
+note: `#2` invokes this custom destructor
+  --> $DIR/drop-order-comparisons.rs:571:1
+   |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | |     fn drop(&mut self) {
+LL | |         self.0.mark(self.1);
+LL | |     }
+LL | | }
+   | |_^
+note: `#1` invokes this custom destructor
+  --> $DIR/drop-order-comparisons.rs:571:1
+   |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | |     fn drop(&mut self) {
+LL | |         self.0.mark(self.1);
+LL | |     }
+LL | | }
+   | |_^
+   = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
+
+warning: relative drop order changing in Rust 2024
+  --> $DIR/drop-order-comparisons.rs:100:19
+   |
+LL |       _ = ({
+   |  _________-
+LL | |         (e.ok(2), e.ok(6).is_ok(), e.ok(3), e.ok(5).is_ok())
+   | |                   ^^^^^^^
+   | |                   |
+   | |                   this value will be stored in a temporary; let us call it `#2`
+   | |                   up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024
+...  |
+LL | |     }, e.mark(1), e.ok(4));
+   | |                          -
+   | |                          |
+   | |                          now the temporary value is dropped here, before the local variables in the block or statement
+   | |__________________________this value will be stored in a temporary; let us call it `#1`
+   |                            `#1` will be dropped later as of Edition 2024
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
+note: `#2` invokes this custom destructor
+  --> $DIR/drop-order-comparisons.rs:571:1
+   |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | |     fn drop(&mut self) {
+LL | |         self.0.mark(self.1);
+LL | |     }
+LL | | }
+   | |_^
+note: `#1` invokes this custom destructor
+  --> $DIR/drop-order-comparisons.rs:571:1
+   |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | |     fn drop(&mut self) {
+LL | |         self.0.mark(self.1);
+LL | |     }
+LL | | }
+   | |_^
+   = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
+
+warning: relative drop order changing in Rust 2024
+  --> $DIR/drop-order-comparisons.rs:221:24
+   |
+LL |       _ = ({
+   |  _________-
+LL | |         if let Ok(_) = e.ok(4).as_ref() {
+   | |                        ^^^^^^^
+   | |                        |
+   | |                        this value will be stored in a temporary; let us call it `#2`
+   | |                        up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024
+...  |
+LL | |     }, e.mark(2), e.ok(3));
+   | |                          -
+   | |                          |
+   | |                          now the temporary value is dropped here, before the local variables in the block or statement
+   | |__________________________this value will be stored in a temporary; let us call it `#1`
+   |                            `#1` will be dropped later as of Edition 2024
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
+note: `#2` invokes this custom destructor
+  --> $DIR/drop-order-comparisons.rs:571:1
+   |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | |     fn drop(&mut self) {
+LL | |         self.0.mark(self.1);
+LL | |     }
+LL | | }
+   | |_^
+note: `#1` invokes this custom destructor
+  --> $DIR/drop-order-comparisons.rs:571:1
+   |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | |     fn drop(&mut self) {
+LL | |         self.0.mark(self.1);
+LL | |     }
+LL | | }
+   | |_^
+   = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
+
+warning: relative drop order changing in Rust 2024
+  --> $DIR/drop-order-comparisons.rs:247:24
+   |
+LL |       _ = ({
+   |  _________-
+LL | |         if let Ok(_) = e.err(4).as_ref() {} else {
+   | |                        ^^^^^^^^
+   | |                        |
+   | |                        this value will be stored in a temporary; let us call it `#2`
+   | |                        up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024
+...  |
+LL | |     }, e.mark(2), e.ok(3));
+   | |                          -
+   | |                          |
+   | |                          now the temporary value is dropped here, before the local variables in the block or statement
+   | |__________________________this value will be stored in a temporary; let us call it `#1`
+   |                            `#1` will be dropped later as of Edition 2024
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
+note: `#2` invokes this custom destructor
+  --> $DIR/drop-order-comparisons.rs:571:1
+   |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | |     fn drop(&mut self) {
+LL | |         self.0.mark(self.1);
+LL | |     }
+LL | | }
+   | |_^
+note: `#1` invokes this custom destructor
+  --> $DIR/drop-order-comparisons.rs:571:1
+   |
+LL | / impl<'b> Drop for LogDrop<'b> {
+LL | |     fn drop(&mut self) {
+LL | |         self.0.mark(self.1);
+LL | |     }
+LL | | }
+   | |_^
+   = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
+
+warning: `if let` assigns a shorter lifetime since Edition 2024
+  --> $DIR/drop-order-comparisons.rs:123:13
+   |
+LL |     _ = (if let Ok(_) = e.ok(4).as_ref() {
+   |             ^^^^^^^^^^^^-------^^^^^^^^^
+   |                         |
+   |                         this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>
+help: the value is now dropped here in Edition 2024
+  --> $DIR/drop-order-comparisons.rs:127:5
+   |
+LL |     }, e.mark(2), e.ok(3));
+   |     ^
+   = note: `#[warn(if_let_rescope)]` implied by `#[warn(rust_2024_compatibility)]`
+help: a `match` with a single arm can preserve the drop order up to Edition 2021
+   |
+LL ~     _ = (match e.ok(4).as_ref() { Ok(_) => {
+LL |
+LL |
+LL |             e.mark(1);
+LL ~     } _ => {}}, e.mark(2), e.ok(3));
+   |
+
+warning: `if let` assigns a shorter lifetime since Edition 2024
+  --> $DIR/drop-order-comparisons.rs:145:13
+   |
+LL |     _ = (if let Ok(_) = e.err(4).as_ref() {} else {
+   |             ^^^^^^^^^^^^--------^^^^^^^^^
+   |                         |
+   |                         this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>
+help: the value is now dropped here in Edition 2024
+  --> $DIR/drop-order-comparisons.rs:145:44
+   |
+LL |     _ = (if let Ok(_) = e.err(4).as_ref() {} else {
+   |                                            ^
+help: a `match` with a single arm can preserve the drop order up to Edition 2021
+   |
+LL ~     _ = (match e.err(4).as_ref() { Ok(_) => {} _ => {
+LL |
+LL |
+LL |             e.mark(1);
+LL ~     }}, e.mark(2), e.ok(3));
+   |
+
+warning: `if let` assigns a shorter lifetime since Edition 2024
+  --> $DIR/drop-order-comparisons.rs:247:12
+   |
+LL |         if let Ok(_) = e.err(4).as_ref() {} else {
+   |            ^^^^^^^^^^^^--------^^^^^^^^^
+   |                        |
+   |                        this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>
+help: the value is now dropped here in Edition 2024
+  --> $DIR/drop-order-comparisons.rs:247:43
+   |
+LL |         if let Ok(_) = e.err(4).as_ref() {} else {
+   |                                           ^
+help: a `match` with a single arm can preserve the drop order up to Edition 2021
+   |
+LL ~         match e.err(4).as_ref() { Ok(_) => {} _ => {
+LL |
+...
+LL |             e.mark(1);
+LL ~         }}
+   |
+
+warning: `if let` assigns a shorter lifetime since Edition 2024
+  --> $DIR/drop-order-comparisons.rs:352:12
+   |
+LL |         if let true = e.err(9).is_ok() {} else {
+   |            ^^^^^^^^^^^--------^^^^^^^^
+   |                       |
+   |                       this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>
+help: the value is now dropped here in Edition 2024
+  --> $DIR/drop-order-comparisons.rs:352:41
+   |
+LL |         if let true = e.err(9).is_ok() {} else {
+   |                                         ^
+help: a `match` with a single arm can preserve the drop order up to Edition 2021
+   |
+LL ~         match e.err(9).is_ok() { true => {} _ => {
+LL |
+...
+LL |             e.mark(3);
+LL ~         }}}}}}}}};
+   |
+
+warning: `if let` assigns a shorter lifetime since Edition 2024
+  --> $DIR/drop-order-comparisons.rs:355:12
+   |
+LL |         if let Ok(_v) = e.err(8) {} else {
+   |            ^^^^^^^^^^^^^--------
+   |                         |
+   |                         this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>
+help: the value is now dropped here in Edition 2024
+  --> $DIR/drop-order-comparisons.rs:355:35
+   |
+LL |         if let Ok(_v) = e.err(8) {} else {
+   |                                   ^
+help: a `match` with a single arm can preserve the drop order up to Edition 2021
+   |
+LL ~         match e.err(8) { Ok(_v) => {} _ => {
+LL |
+...
+LL |             e.mark(3);
+LL ~         }}}}}}}}};
+   |
+
+warning: `if let` assigns a shorter lifetime since Edition 2024
+  --> $DIR/drop-order-comparisons.rs:358:12
+   |
+LL |         if let Ok(_) = e.err(7) {} else {
+   |            ^^^^^^^^^^^^--------
+   |                        |
+   |                        this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>
+help: the value is now dropped here in Edition 2024
+  --> $DIR/drop-order-comparisons.rs:358:34
+   |
+LL |         if let Ok(_) = e.err(7) {} else {
+   |                                  ^
+help: a `match` with a single arm can preserve the drop order up to Edition 2021
+   |
+LL ~         match e.err(7) { Ok(_) => {} _ => {
+LL |
+...
+LL |             e.mark(3);
+LL ~         }}}}}}}}};
+   |
+
+warning: `if let` assigns a shorter lifetime since Edition 2024
+  --> $DIR/drop-order-comparisons.rs:361:12
+   |
+LL |         if let Ok(_) = e.err(6).as_ref() {} else {
+   |            ^^^^^^^^^^^^--------^^^^^^^^^
+   |                        |
+   |                        this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>
+help: the value is now dropped here in Edition 2024
+  --> $DIR/drop-order-comparisons.rs:361:43
+   |
+LL |         if let Ok(_) = e.err(6).as_ref() {} else {
+   |                                           ^
+help: a `match` with a single arm can preserve the drop order up to Edition 2021
+   |
+LL ~         match e.err(6).as_ref() { Ok(_) => {} _ => {
+LL |
+...
+LL |             e.mark(3);
+LL ~         }}}}}}}}};
+   |
+
+warning: `if let` assigns a shorter lifetime since Edition 2024
+  --> $DIR/drop-order-comparisons.rs:365:12
+   |
+LL |         if let Ok(_v) = e.err(5) {} else {
+   |            ^^^^^^^^^^^^^--------
+   |                         |
+   |                         this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>
+help: the value is now dropped here in Edition 2024
+  --> $DIR/drop-order-comparisons.rs:365:35
+   |
+LL |         if let Ok(_v) = e.err(5) {} else {
+   |                                   ^
+help: a `match` with a single arm can preserve the drop order up to Edition 2021
+   |
+LL ~         match e.err(5) { Ok(_v) => {} _ => {
+LL |
+...
+LL |             e.mark(3);
+LL ~         }}}}}}}}};
+   |
+
+warning: `if let` assigns a shorter lifetime since Edition 2024
+  --> $DIR/drop-order-comparisons.rs:368:12
+   |
+LL |         if let Ok(_) = e.err(4) {} else {
+   |            ^^^^^^^^^^^^--------
+   |                        |
+   |                        this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>
+help: the value is now dropped here in Edition 2024
+  --> $DIR/drop-order-comparisons.rs:368:34
+   |
+LL |         if let Ok(_) = e.err(4) {} else {
+   |                                  ^
+help: a `match` with a single arm can preserve the drop order up to Edition 2021
+   |
+LL ~         match e.err(4) { Ok(_) => {} _ => {
+LL |
+LL |
+LL |             e.mark(3);
+LL ~         }}}}}}}}};
+   |
+
+warning: `if let` assigns a shorter lifetime since Edition 2024
+  --> $DIR/drop-order-comparisons.rs:404:12
+   |
+LL |         if let Ok(_) = e.err(4).as_ref() {} else {
+   |            ^^^^^^^^^^^^--------^^^^^^^^^
+   |                        |
+   |                        this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>
+help: the value is now dropped here in Edition 2024
+  --> $DIR/drop-order-comparisons.rs:404:43
+   |
+LL |         if let Ok(_) = e.err(4).as_ref() {} else {
+   |                                           ^
+help: a `match` with a single arm can preserve the drop order up to Edition 2021
+   |
+LL ~         match e.err(4).as_ref() { Ok(_) => {} _ => {
+LL |
+LL |
+LL |             e.mark(3);
+LL ~         }}}}}}}}};
+   |
+
+warning: 15 warnings emitted
+
diff --git a/tests/ui/drop/drop-order-comparisons.rs b/tests/ui/drop/drop-order-comparisons.rs
new file mode 100644
index 00000000000..78c75a9449f
--- /dev/null
+++ b/tests/ui/drop/drop-order-comparisons.rs
@@ -0,0 +1,575 @@
+// This tests various aspects of the drop order with a focus on:
+//
+// - The lifetime of temporaries with the `if let` construct (and with
+// various similar constructs) and how these lifetimes were shortened
+// for `if let` in Rust 2024.
+//
+// - The shortening of the lifetimes of temporaries in tail
+// expressions in Rust 2024.
+//
+// - The behavior of `let` chains and how this behavior compares to
+// nested `if let` expressions and chained `let .. else` statements.
+//
+// In the tests below, `Events` tracks a sequence of numbered events.
+// Calling `e.mark(..)` logs a numbered event immediately.  Calling
+// `e.ok(..)` or `e.err(..)` returns an `Ok(_)` or `Err(_)` value,
+// respectively, and logs the numbered event when that value is
+// dropped.  Calling `e.assert()` verifies that the correct number of
+// events were logged and that they were logged in the correct order.
+
+//@ revisions: e2021 e2024
+//@ [e2021] edition: 2021
+//@ [e2021] run-rustfix
+//@ [e2021] rustfix-only-machine-applicable
+//@ [e2024] edition: 2024
+//@ run-pass
+
+#![feature(let_chains)]
+#![cfg_attr(e2021, warn(rust_2024_compatibility))]
+
+fn t_bindings() {
+    let e = Events::new();
+    _ = {
+        e.mark(1);
+        let _v = e.ok(8);
+        let _v = e.ok(2).is_ok();
+        let _ = e.ok(3);
+        let Ok(_) = e.ok(4) else { unreachable!() };
+        let Ok(_) = e.ok(5).as_ref() else { unreachable!() };
+        let _v = e.ok(7);
+        e.mark(6);
+    };
+    e.assert(8);
+}
+
+fn t_tuples() {
+    let e = Events::new();
+    _ = (e.ok(1), e.ok(4).is_ok(), e.ok(2), e.ok(3).is_ok());
+    e.assert(4);
+}
+
+fn t_arrays() {
+    let e = Events::new();
+    trait Tr {}
+    impl<T> Tr for T {}
+    fn b<'a, T: 'a>(x: T) -> Box<dyn Tr + 'a> {
+        Box::new(x)
+    }
+    _ = [b(e.ok(1)), b(e.ok(4).is_ok()), b(e.ok(2)), b(e.ok(3).is_ok())];
+    e.assert(4);
+}
+
+fn t_fncalls() {
+    let e = Events::new();
+    let f = |_, _, _, _| {};
+    _ = f(e.ok(2), e.ok(4).is_ok(), e.ok(1), e.ok(3).is_ok());
+    e.assert(4);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_tailexpr_bindings() {
+    let e = Events::new();
+    _ = ({
+        let _v = e.ok(2);
+        let _v = e.ok(1);
+        e.ok(5).is_ok()
+        //[e2021]~^ WARN relative drop order changing in Rust 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+    }, e.mark(3), e.ok(4));
+    e.assert(5);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_tailexpr_bindings() {
+    let e = Events::new();
+    _ = ({
+        let _v = e.ok(3);
+        let _v = e.ok(2);
+        e.ok(1).is_ok()
+    }, e.mark(4), e.ok(5));
+    e.assert(5);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_tailexpr_tuples() {
+    let e = Events::new();
+    _ = ({
+        (e.ok(2), e.ok(6).is_ok(), e.ok(3), e.ok(5).is_ok())
+        //[e2021]~^ WARN relative drop order changing in Rust 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+        //[e2021]~| WARN relative drop order changing in Rust 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+    }, e.mark(1), e.ok(4));
+    e.assert(6);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_tailexpr_tuples() {
+    let e = Events::new();
+    _ = ({
+        (e.ok(4), e.ok(2).is_ok(), e.ok(5), e.ok(1).is_ok())
+    }, e.mark(3), e.ok(6));
+    e.assert(6);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_then() {
+    let e = Events::new();
+    _ = (if let Ok(_) = e.ok(4).as_ref() {
+            //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+            //[e2021]~| WARN this changes meaning in Rust 2024
+            e.mark(1);
+    }, e.mark(2), e.ok(3));
+    e.assert(4);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_then() {
+    let e = Events::new();
+    _ = (if let Ok(_) = e.ok(2).as_ref() {
+            e.mark(1);
+    }, e.mark(3), e.ok(4));
+    e.assert(4);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_else() {
+    let e = Events::new();
+    _ = (if let Ok(_) = e.err(4).as_ref() {} else {
+            //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+            //[e2021]~| WARN this changes meaning in Rust 2024
+            e.mark(1);
+    }, e.mark(2), e.ok(3));
+    e.assert(4);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_else() {
+    let e = Events::new();
+    _ = (if let Ok(_) = e.err(1).as_ref() {} else {
+            e.mark(2);
+    }, e.mark(3), e.ok(4));
+    e.assert(4);
+}
+
+#[rustfmt::skip]
+fn t_match_then() {
+    let e = Events::new();
+    _ = (match e.ok(4).as_ref() {
+            Ok(_) => e.mark(1),
+            _ => unreachable!(),
+    }, e.mark(2), e.ok(3));
+    e.assert(4);
+}
+
+#[rustfmt::skip]
+fn t_match_else() {
+    let e = Events::new();
+    _ = (match e.err(4).as_ref() {
+            Ok(_) => unreachable!(),
+            _ => e.mark(1),
+    }, e.mark(2), e.ok(3));
+    e.assert(4);
+}
+
+#[rustfmt::skip]
+fn t_let_else_then() {
+    let e = Events::new();
+    _ = ('top: {
+         'chain: {
+            let Ok(_) = e.ok(1).as_ref() else { break 'chain };
+            // The "then" branch:
+            e.mark(2);
+            break 'top;
+        }
+        // The "else" branch:
+        unreachable!()
+    }, e.mark(3), e.ok(4));
+    e.assert(4);
+}
+
+#[rustfmt::skip]
+fn t_let_else_else() {
+    let e = Events::new();
+    _ = ('top: {
+         'chain: {
+            let Ok(_) = e.err(1).as_ref() else { break 'chain };
+            // The "then" branch:
+            unreachable!();
+            #[allow(unreachable_code)]
+            break 'top;
+        }
+        // The "else" branch:
+        e.mark(2);
+    }, e.mark(3), e.ok(4));
+    e.assert(4);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_then_tailexpr() {
+    let e = Events::new();
+    _ = ({
+        if let Ok(_) = e.ok(4).as_ref() {
+            //[e2021]~^ WARN relative drop order changing in Rust 2024
+            //[e2021]~| WARN this changes meaning in Rust 2024
+            e.mark(1);
+        }
+    }, e.mark(2), e.ok(3));
+    e.assert(4);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_then_tailexpr() {
+    let e = Events::new();
+    _ = ({
+        if let Ok(_) = e.ok(2).as_ref() {
+            e.mark(1);
+        }
+    }, e.mark(3), e.ok(4));
+    e.assert(4);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_else_tailexpr() {
+    let e = Events::new();
+    _ = ({
+        if let Ok(_) = e.err(4).as_ref() {} else {
+            //[e2021]~^ WARN relative drop order changing in Rust 2024
+            //[e2021]~| WARN this changes meaning in Rust 2024
+            //[e2021]~| WARN if let` assigns a shorter lifetime since Edition 2024
+            //[e2021]~| WARN this changes meaning in Rust 2024
+            e.mark(1);
+        }
+    }, e.mark(2), e.ok(3));
+    e.assert(4);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_else_tailexpr() {
+    let e = Events::new();
+    _ = ({
+        if let Ok(_) = e.err(1).as_ref() {} else {
+            e.mark(2);
+        }
+    }, e.mark(3), e.ok(4));
+    e.assert(4);
+}
+
+#[rustfmt::skip]
+fn t_if_let_nested_then() {
+    let e = Events::new();
+    _ = {
+        // The unusual formatting, here and below, is to make the
+        // comparison with `let` chains more direct.
+        if e.ok(1).is_ok() {
+        if let true = e.ok(9).is_ok() {
+        if let Ok(_v) = e.ok(8) {
+        if let Ok(_) = e.ok(7) {
+        if let Ok(_) = e.ok(6).as_ref() {
+        if e.ok(2).is_ok() {
+        if let Ok(_v) = e.ok(5) {
+        if let Ok(_) = e.ok(4).as_ref() {
+            e.mark(3);
+        }}}}}}}}
+    };
+    e.assert(9);
+}
+
+#[rustfmt::skip]
+fn t_let_else_chained_then() {
+    let e = Events::new();
+    _ = 'top: {
+        'chain: {
+            if e.ok(1).is_ok() {} else { break 'chain };
+            let true = e.ok(2).is_ok() else { break 'chain };
+            let Ok(_v) = e.ok(9) else { break 'chain };
+            let Ok(_) = e.ok(3) else { break 'chain };
+            let Ok(_) = e.ok(4).as_ref() else { break 'chain };
+            if e.ok(5).is_ok() {} else { break 'chain };
+            let Ok(_v) = e.ok(8) else { break 'chain };
+            let Ok(_) = e.ok(6).as_ref() else { break 'chain };
+            // The "then" branch:
+            e.mark(7);
+            break 'top;
+        }
+        // The "else" branch:
+        unreachable!()
+    };
+    e.assert(9);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_chains_then() {
+    let e = Events::new();
+    _ = if e.ok(1).is_ok()
+        && let true = e.ok(9).is_ok()
+        && let Ok(_v) = e.ok(5)
+        && let Ok(_) = e.ok(8)
+        && let Ok(_) = e.ok(7).as_ref()
+        && e.ok(2).is_ok()
+        && let Ok(_v) = e.ok(4)
+        && let Ok(_) = e.ok(6).as_ref() {
+            e.mark(3);
+        };
+    e.assert(9);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_chains_then() {
+    let e = Events::new();
+    _ = if e.ok(1).is_ok()
+        && let true = e.ok(9).is_ok()
+        && let Ok(_v) = e.ok(8)
+        && let Ok(_) = e.ok(7)
+        && let Ok(_) = e.ok(6).as_ref()
+        && e.ok(2).is_ok()
+        && let Ok(_v) = e.ok(5)
+        && let Ok(_) = e.ok(4).as_ref() {
+            e.mark(3);
+        };
+    e.assert(9);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_nested_else() {
+    let e = Events::new();
+    _ = if e.err(1).is_ok() {} else {
+        if let true = e.err(9).is_ok() {} else {
+        //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+        if let Ok(_v) = e.err(8) {} else {
+        //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+        if let Ok(_) = e.err(7) {} else {
+        //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+        if let Ok(_) = e.err(6).as_ref() {} else {
+        //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+        if e.err(2).is_ok() {} else {
+        if let Ok(_v) = e.err(5) {} else {
+        //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+        //[e2021]~| WARN this changes meaning in Rust 2024
+        if let Ok(_) = e.err(4) {} else {
+            //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+            //[e2021]~| WARN this changes meaning in Rust 2024
+            e.mark(3);
+        }}}}}}}};
+    e.assert(9);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_nested_else() {
+    let e = Events::new();
+    _ = if e.err(1).is_ok() {} else {
+        if let true = e.err(2).is_ok() {} else {
+        if let Ok(_v) = e.err(3) {} else {
+        if let Ok(_) = e.err(4) {} else {
+        if let Ok(_) = e.err(5).as_ref() {} else {
+        if e.err(6).is_ok() {} else {
+        if let Ok(_v) = e.err(7) {} else {
+        if let Ok(_) = e.err(8) {} else {
+            e.mark(9);
+        }}}}}}}};
+    e.assert(9);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_nested_then_else() {
+    let e = Events::new();
+    _ = if e.ok(1).is_ok() {
+        if let true = e.ok(9).is_ok() {
+        if let Ok(_v) = e.ok(8) {
+        if let Ok(_) = e.ok(7) {
+        if let Ok(_) = e.ok(6).as_ref() {
+        if e.ok(2).is_ok() {
+        if let Ok(_v) = e.ok(5) {
+        if let Ok(_) = e.err(4).as_ref() {} else {
+            //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024
+            //[e2021]~| WARN this changes meaning in Rust 2024
+            e.mark(3);
+        }}}}}}}};
+    e.assert(9);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_nested_then_else() {
+    let e = Events::new();
+    _ = if e.ok(1).is_ok() {
+        if let true = e.ok(9).is_ok() {
+        if let Ok(_v) = e.ok(8) {
+        if let Ok(_) = e.ok(7) {
+        if let Ok(_) = e.ok(6).as_ref() {
+        if e.ok(2).is_ok() {
+        if let Ok(_v) = e.ok(5) {
+        if let Ok(_) = e.err(3).as_ref() {} else {
+            e.mark(4);
+        }}}}}}}};
+    e.assert(9);
+}
+
+#[rustfmt::skip]
+fn t_let_else_chained_then_else() {
+    let e = Events::new();
+    _ = 'top: {
+        'chain: {
+            if e.ok(1).is_ok() {} else { break 'chain };
+            let true = e.ok(2).is_ok() else { break 'chain };
+            let Ok(_v) = e.ok(8) else { break 'chain };
+            let Ok(_) = e.ok(3) else { break 'chain };
+            let Ok(_) = e.ok(4).as_ref() else { break 'chain };
+            if e.ok(5).is_ok() {} else { break 'chain };
+            let Ok(_v) = e.ok(7) else { break 'chain };
+            let Ok(_) = e.err(6).as_ref() else { break 'chain };
+            // The "then" branch:
+            unreachable!();
+            #[allow(unreachable_code)]
+            break 'top;
+        }
+        // The "else" branch:
+        e.mark(9);
+    };
+    e.assert(9);
+}
+
+#[cfg(e2021)]
+#[rustfmt::skip]
+fn t_if_let_chains_then_else() {
+    let e = Events::new();
+    _ = if e.ok(1).is_ok()
+        && let true = e.ok(9).is_ok()
+        && let Ok(_v) = e.ok(4)
+        && let Ok(_) = e.ok(8)
+        && let Ok(_) = e.ok(7).as_ref()
+        && e.ok(2).is_ok()
+        && let Ok(_v) = e.ok(3)
+        && let Ok(_) = e.err(6) {} else {
+            e.mark(5);
+        };
+    e.assert(9);
+}
+
+#[cfg(e2024)]
+#[rustfmt::skip]
+fn t_if_let_chains_then_else() {
+    let e = Events::new();
+    _ = if e.ok(1).is_ok()
+        && let true = e.ok(8).is_ok()
+        && let Ok(_v) = e.ok(7)
+        && let Ok(_) = e.ok(6)
+        && let Ok(_) = e.ok(5).as_ref()
+        && e.ok(2).is_ok()
+        && let Ok(_v) = e.ok(4)
+        && let Ok(_) = e.err(3) {} else {
+            e.mark(9);
+        };
+    e.assert(9);
+}
+
+fn main() {
+    t_bindings();
+    t_tuples();
+    t_arrays();
+    t_fncalls();
+    t_tailexpr_bindings();
+    t_tailexpr_tuples();
+    t_if_let_then();
+    t_if_let_else();
+    t_match_then();
+    t_match_else();
+    t_let_else_then();
+    t_let_else_else();
+    t_if_let_then_tailexpr();
+    t_if_let_else_tailexpr();
+    t_if_let_nested_then();
+    t_let_else_chained_then();
+    t_if_let_chains_then();
+    t_if_let_nested_else();
+    t_if_let_nested_then_else();
+    t_let_else_chained_then_else();
+    t_if_let_chains_then_else();
+}
+
+// # Test scaffolding
+
+use core::cell::RefCell;
+use std::collections::HashSet;
+
+/// A buffer to track the order of events.
+///
+/// First, numbered events are logged into this buffer.
+///
+/// Then, `assert` is called to verify that the correct number of
+/// events were logged, and that they were logged in the expected
+/// order.
+struct Events(RefCell<Option<Vec<u64>>>);
+
+impl Events {
+    const fn new() -> Self {
+        Self(RefCell::new(Some(Vec::new())))
+    }
+    #[track_caller]
+    fn assert(&self, max: u64) {
+        let buf = &self.0;
+        let v1 = buf.borrow().as_ref().unwrap().clone();
+        let mut v2 = buf.borrow().as_ref().unwrap().clone();
+        *buf.borrow_mut() = None;
+        v2.sort();
+        let uniq_len = v2.iter().collect::<HashSet<_>>().len();
+        // Check that the sequence is sorted.
+        assert_eq!(v1, v2);
+        // Check that there are no duplicates.
+        assert_eq!(v2.len(), uniq_len);
+        // Check that the length is the expected one.
+        assert_eq!(max, uniq_len as u64);
+        // Check that the last marker is the expected one.
+        assert_eq!(v2.last().unwrap(), &max);
+    }
+    /// Return an `Ok` value that logs its drop.
+    fn ok(&self, m: u64) -> Result<LogDrop<'_>, LogDrop<'_>> {
+        Ok(LogDrop(self, m))
+    }
+    /// Return an `Err` value that logs its drop.
+    fn err(&self, m: u64) -> Result<LogDrop, LogDrop> {
+        Err(LogDrop(self, m))
+    }
+    /// Log an event.
+    fn mark(&self, m: u64) {
+        self.0.borrow_mut().as_mut().unwrap().push(m);
+    }
+}
+
+impl Drop for Events {
+    fn drop(&mut self) {
+        if self.0.borrow().is_some() {
+            panic!("failed to call `Events::assert()`");
+        }
+    }
+}
+
+/// A type that logs its drop events.
+struct LogDrop<'b>(&'b Events, u64);
+
+impl<'b> Drop for LogDrop<'b> {
+    fn drop(&mut self) {
+        self.0.mark(self.1);
+    }
+}
diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs
index a44ed9e5ef5..34548e2487e 100644
--- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs
+++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs
@@ -43,9 +43,9 @@ fn main() {
     }
 
     foo(bar, "string", |s| s.len() == 5);
-    //~^ ERROR implementation of `FnOnce` is not general enough
-    //~| ERROR implementation of `FnOnce` is not general enough
+    //~^ ERROR implementation of `Parser` is not general enough
+    //~| ERROR implementation of `Parser` is not general enough
     foo(baz, "string", |s| s.0.len() == 5);
-    //~^ ERROR implementation of `FnOnce` is not general enough
-    //~| ERROR implementation of `FnOnce` is not general enough
+    //~^ ERROR implementation of `Parser` is not general enough
+    //~| ERROR implementation of `Parser` is not general enough
 }
diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr
index b2bb417a8f0..23fc6e2f7f4 100644
--- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr
+++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr
@@ -1,39 +1,39 @@
-error: implementation of `FnOnce` is not general enough
+error: implementation of `Parser` is not general enough
   --> $DIR/issue-71955.rs:45:5
    |
 LL |     foo(bar, "string", |s| s.len() == 5);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough
    |
-   = note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`...
-   = note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2`
+   = note: `for<'a> fn(&'a str) -> (&'a str, &'a str) {bar}` must implement `Parser<'0>`, for any lifetime `'0`...
+   = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1`
 
-error: implementation of `FnOnce` is not general enough
+error: implementation of `Parser` is not general enough
   --> $DIR/issue-71955.rs:45:5
    |
 LL |     foo(bar, "string", |s| s.len() == 5);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough
    |
-   = note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`...
-   = note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2`
+   = note: `for<'a> fn(&'a str) -> (&'a str, &'a str) {bar}` must implement `Parser<'0>`, for any lifetime `'0`...
+   = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1`
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
-error: implementation of `FnOnce` is not general enough
+error: implementation of `Parser` is not general enough
   --> $DIR/issue-71955.rs:48:5
    |
 LL |     foo(baz, "string", |s| s.0.len() == 5);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough
    |
-   = note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`...
-   = note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2`
+   = note: `for<'a> fn(&'a str) -> (&'a str, Wrapper<'a>) {baz}` must implement `Parser<'0>`, for any lifetime `'0`...
+   = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1`
 
-error: implementation of `FnOnce` is not general enough
+error: implementation of `Parser` is not general enough
   --> $DIR/issue-71955.rs:48:5
    |
 LL |     foo(baz, "string", |s| s.0.len() == 5);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough
    |
-   = note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`...
-   = note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2`
+   = note: `for<'a> fn(&'a str) -> (&'a str, Wrapper<'a>) {baz}` must implement `Parser<'0>`, for any lifetime `'0`...
+   = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1`
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: aborting due to 4 previous errors
diff --git a/tests/ui/intrinsics/reify-intrinsic.rs b/tests/ui/intrinsics/reify-intrinsic.rs
index 0d047ccf4a3..5b2324235c1 100644
--- a/tests/ui/intrinsics/reify-intrinsic.rs
+++ b/tests/ui/intrinsics/reify-intrinsic.rs
@@ -3,17 +3,17 @@
 #![feature(core_intrinsics, intrinsics)]
 
 fn a() {
-    let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::transmute;
+    let _: unsafe fn(isize) -> usize = std::mem::transmute;
     //~^ ERROR cannot coerce
 }
 
 fn b() {
-    let _ = std::mem::transmute as unsafe extern "rust-intrinsic" fn(isize) -> usize;
+    let _ = std::mem::transmute as unsafe fn(isize) -> usize;
     //~^ ERROR casting
 }
 
 fn c() {
-    let _: [unsafe extern "rust-intrinsic" fn(f32) -> f32; 2] = [
+    let _: [unsafe fn(f32) -> f32; 2] = [
         std::intrinsics::floorf32, //~ ERROR cannot coerce
         std::intrinsics::log2f32,
     ];
diff --git a/tests/ui/intrinsics/reify-intrinsic.stderr b/tests/ui/intrinsics/reify-intrinsic.stderr
index aea6d263a72..aea6f838e0d 100644
--- a/tests/ui/intrinsics/reify-intrinsic.stderr
+++ b/tests/ui/intrinsics/reify-intrinsic.stderr
@@ -1,19 +1,19 @@
 error[E0308]: cannot coerce intrinsics to function pointers
-  --> $DIR/reify-intrinsic.rs:6:64
+  --> $DIR/reify-intrinsic.rs:6:40
    |
-LL |     let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::transmute;
-   |            -------------------------------------------------   ^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers
+LL |     let _: unsafe fn(isize) -> usize = std::mem::transmute;
+   |            -------------------------   ^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers
    |            |
    |            expected due to this
    |
-   = note: expected fn pointer `unsafe extern "rust-intrinsic" fn(isize) -> usize`
+   = note: expected fn pointer `unsafe fn(isize) -> usize`
                  found fn item `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}`
 
-error[E0606]: casting `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid
+error[E0606]: casting `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` as `unsafe fn(isize) -> usize` is invalid
   --> $DIR/reify-intrinsic.rs:11:13
    |
-LL |     let _ = std::mem::transmute as unsafe extern "rust-intrinsic" fn(isize) -> usize;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let _ = std::mem::transmute as unsafe fn(isize) -> usize;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error[E0308]: cannot coerce intrinsics to function pointers
   --> $DIR/reify-intrinsic.rs:17:9
@@ -21,7 +21,7 @@ error[E0308]: cannot coerce intrinsics to function pointers
 LL |         std::intrinsics::floorf32,
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers
    |
-   = note: expected fn pointer `unsafe extern "rust-intrinsic" fn(_) -> _`
+   = note: expected fn pointer `unsafe fn(_) -> _`
                  found fn item `unsafe fn(_) -> _ {floorf32}`
 
 error: aborting due to 3 previous errors
diff --git a/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.rs b/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.rs
new file mode 100644
index 00000000000..a9c2c20ef37
--- /dev/null
+++ b/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.rs
@@ -0,0 +1,51 @@
+// https://github.com/rust-lang/rust/issues/109195
+struct Foo;
+
+impl Foo {
+    fn bar_baz() {}
+}
+
+impl Foo {
+    fn bar_quux() {}
+}
+
+fn main() {
+    String::from::utf8;
+    //~^ ERROR ambiguous associated type [E0223]
+    //~| HELP there is an associated function with a similar name: `from_utf8`
+    String::from::utf8();
+    //~^ ERROR ambiguous associated type [E0223]
+    //~| HELP there is an associated function with a similar name: `from_utf8`
+    String::from::utf16();
+    //~^ ERROR ambiguous associated type [E0223]
+    //~| HELP there is an associated function with a similar name: `from_utf16`
+    String::from::method_that_doesnt_exist();
+    //~^ ERROR ambiguous associated type [E0223]
+    //~| HELP if there were a trait named `Example` with associated type `from`
+    str::into::string();
+    //~^ ERROR ambiguous associated type [E0223]
+    //~| HELP there is an associated function with a similar name: `into_string`
+    str::char::indices();
+    //~^ ERROR ambiguous associated type [E0223]
+    //~| HELP there is an associated function with a similar name: `char_indices`
+    Foo::bar::baz;
+    //~^ ERROR ambiguous associated type [E0223]
+    //~| HELP there is an associated function with a similar name: `bar_baz`
+    Foo::bar::quux;
+    //~^ ERROR ambiguous associated type [E0223]
+    //~| HELP there is an associated function with a similar name: `bar_quux`
+    Foo::bar::fizz;
+    //~^ ERROR ambiguous associated type [E0223]
+    //~| HELP if there were a trait named `Example` with associated type `bar`
+    i32::wrapping::add;
+    //~^ ERROR ambiguous associated type [E0223]
+    //~| HELP there is an associated function with a similar name: `wrapping_add`
+    i32::wrapping::method_that_doesnt_exist;
+    //~^ ERROR ambiguous associated type [E0223]
+    //~| HELP if there were a trait named `Example` with associated type `wrapping`
+
+    // this one ideally should suggest `downcast_mut_unchecked`
+    <dyn std::any::Any>::downcast::mut_unchecked;
+    //~^ ERROR ambiguous associated type [E0223]
+    //~| HELP if there were a trait named `Example` with associated type `downcast`
+}
diff --git a/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.stderr b/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.stderr
new file mode 100644
index 00000000000..5863aa28f41
--- /dev/null
+++ b/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.stderr
@@ -0,0 +1,135 @@
+error[E0223]: ambiguous associated type
+  --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:13:5
+   |
+LL |     String::from::utf8;
+   |     ^^^^^^^^^^^^
+   |
+help: there is an associated function with a similar name: `from_utf8`
+   |
+LL |     String::from_utf8;
+   |             ~~~~~~~~~
+
+error[E0223]: ambiguous associated type
+  --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:16:5
+   |
+LL |     String::from::utf8();
+   |     ^^^^^^^^^^^^
+   |
+help: there is an associated function with a similar name: `from_utf8`
+   |
+LL |     String::from_utf8();
+   |             ~~~~~~~~~
+
+error[E0223]: ambiguous associated type
+  --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:19:5
+   |
+LL |     String::from::utf16();
+   |     ^^^^^^^^^^^^
+   |
+help: there is an associated function with a similar name: `from_utf16`
+   |
+LL |     String::from_utf16();
+   |             ~~~~~~~~~~
+
+error[E0223]: ambiguous associated type
+  --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:22:5
+   |
+LL |     String::from::method_that_doesnt_exist();
+   |     ^^^^^^^^^^^^
+   |
+help: if there were a trait named `Example` with associated type `from` implemented for `String`, you could use the fully-qualified path
+   |
+LL |     <String as Example>::from::method_that_doesnt_exist();
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error[E0223]: ambiguous associated type
+  --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:25:5
+   |
+LL |     str::into::string();
+   |     ^^^^^^^^^
+   |
+help: there is an associated function with a similar name: `into_string`
+   |
+LL |     str::into_string();
+   |          ~~~~~~~~~~~
+
+error[E0223]: ambiguous associated type
+  --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:28:5
+   |
+LL |     str::char::indices();
+   |     ^^^^^^^^^
+   |
+help: there is an associated function with a similar name: `char_indices`
+   |
+LL |     str::char_indices();
+   |          ~~~~~~~~~~~~
+
+error[E0223]: ambiguous associated type
+  --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:31:5
+   |
+LL |     Foo::bar::baz;
+   |     ^^^^^^^^
+   |
+help: there is an associated function with a similar name: `bar_baz`
+   |
+LL |     Foo::bar_baz;
+   |          ~~~~~~~
+
+error[E0223]: ambiguous associated type
+  --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:34:5
+   |
+LL |     Foo::bar::quux;
+   |     ^^^^^^^^
+   |
+help: there is an associated function with a similar name: `bar_quux`
+   |
+LL |     Foo::bar_quux;
+   |          ~~~~~~~~
+
+error[E0223]: ambiguous associated type
+  --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:37:5
+   |
+LL |     Foo::bar::fizz;
+   |     ^^^^^^^^
+   |
+help: if there were a trait named `Example` with associated type `bar` implemented for `Foo`, you could use the fully-qualified path
+   |
+LL |     <Foo as Example>::bar::fizz;
+   |     ~~~~~~~~~~~~~~~~~~~~~
+
+error[E0223]: ambiguous associated type
+  --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:40:5
+   |
+LL |     i32::wrapping::add;
+   |     ^^^^^^^^^^^^^
+   |
+help: there is an associated function with a similar name: `wrapping_add`
+   |
+LL |     i32::wrapping_add;
+   |          ~~~~~~~~~~~~
+
+error[E0223]: ambiguous associated type
+  --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:43:5
+   |
+LL |     i32::wrapping::method_that_doesnt_exist;
+   |     ^^^^^^^^^^^^^
+   |
+help: if there were a trait named `Example` with associated type `wrapping` implemented for `i32`, you could use the fully-qualified path
+   |
+LL |     <i32 as Example>::wrapping::method_that_doesnt_exist;
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error[E0223]: ambiguous associated type
+  --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:48:5
+   |
+LL |     <dyn std::any::Any>::downcast::mut_unchecked;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: if there were a trait named `Example` with associated type `downcast` implemented for `(dyn Any + 'static)`, you could use the fully-qualified path
+   |
+LL |     <(dyn Any + 'static) as Example>::downcast::mut_unchecked;
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 12 previous errors
+
+For more information about this error, try `rustc --explain E0223`.
diff --git a/tests/ui/suggestions/issue-109195.rs b/tests/ui/suggestions/issue-109195.rs
deleted file mode 100644
index cc499b0d776..00000000000
--- a/tests/ui/suggestions/issue-109195.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-fn main() {
-    String::from::utf8;
-    //~^ ERROR ambiguous associated type [E0223]
-    //~| HELP there is an associated function with a similar name: `from_utf8`
-    String::from::utf8();
-    //~^ ERROR ambiguous associated type [E0223]
-    //~| HELP there is an associated function with a similar name: `from_utf8`
-    String::from::utf16();
-    //~^ ERROR ambiguous associated type [E0223]
-    //~| HELP there is an associated function with a similar name: `from_utf16`
-    String::from::method_that_doesnt_exist();
-    //~^ ERROR ambiguous associated type [E0223]
-    //~| HELP if there were a trait named `Example` with associated type `from`
-    str::from::utf8();
-    //~^ ERROR ambiguous associated type [E0223]
-    //~| HELP if there were a trait named `Example` with associated type `from`
-    str::from::utf8_mut();
-    //~^ ERROR ambiguous associated type [E0223]
-    //~| HELP if there were a trait named `Example` with associated type `from`
-}
diff --git a/tests/ui/suggestions/issue-109195.stderr b/tests/ui/suggestions/issue-109195.stderr
deleted file mode 100644
index 10cf9cfd28c..00000000000
--- a/tests/ui/suggestions/issue-109195.stderr
+++ /dev/null
@@ -1,69 +0,0 @@
-error[E0223]: ambiguous associated type
-  --> $DIR/issue-109195.rs:2:5
-   |
-LL |     String::from::utf8;
-   |     ^^^^^^^^^^^^
-   |
-help: there is an associated function with a similar name: `from_utf8`
-   |
-LL |     String::from_utf8;
-   |             ~~~~~~~~~
-
-error[E0223]: ambiguous associated type
-  --> $DIR/issue-109195.rs:5:5
-   |
-LL |     String::from::utf8();
-   |     ^^^^^^^^^^^^
-   |
-help: there is an associated function with a similar name: `from_utf8`
-   |
-LL |     String::from_utf8();
-   |             ~~~~~~~~~
-
-error[E0223]: ambiguous associated type
-  --> $DIR/issue-109195.rs:8:5
-   |
-LL |     String::from::utf16();
-   |     ^^^^^^^^^^^^
-   |
-help: there is an associated function with a similar name: `from_utf16`
-   |
-LL |     String::from_utf16();
-   |             ~~~~~~~~~~
-
-error[E0223]: ambiguous associated type
-  --> $DIR/issue-109195.rs:11:5
-   |
-LL |     String::from::method_that_doesnt_exist();
-   |     ^^^^^^^^^^^^
-   |
-help: if there were a trait named `Example` with associated type `from` implemented for `String`, you could use the fully-qualified path
-   |
-LL |     <String as Example>::from::method_that_doesnt_exist();
-   |     ~~~~~~~~~~~~~~~~~~~~~~~~~
-
-error[E0223]: ambiguous associated type
-  --> $DIR/issue-109195.rs:14:5
-   |
-LL |     str::from::utf8();
-   |     ^^^^^^^^^
-   |
-help: if there were a trait named `Example` with associated type `from` implemented for `str`, you could use the fully-qualified path
-   |
-LL |     <str as Example>::from::utf8();
-   |     ~~~~~~~~~~~~~~~~~~~~~~
-
-error[E0223]: ambiguous associated type
-  --> $DIR/issue-109195.rs:17:5
-   |
-LL |     str::from::utf8_mut();
-   |     ^^^^^^^^^
-   |
-help: if there were a trait named `Example` with associated type `from` implemented for `str`, you could use the fully-qualified path
-   |
-LL |     <str as Example>::from::utf8_mut();
-   |     ~~~~~~~~~~~~~~~~~~~~~~
-
-error: aborting due to 6 previous errors
-
-For more information about this error, try `rustc --explain E0223`.