about summary refs log tree commit diff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-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
3 files changed, 1627 insertions, 0 deletions
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);
+    }
+}