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/or-pattern-drop-order.rs109
-rw-r--r--tests/ui/dropck/eager-by-ref-binding-for-guards.rs31
-rw-r--r--tests/ui/dropck/eager-by-ref-binding-for-guards.stderr28
-rw-r--r--tests/ui/dropck/let-else-more-permissive.rs30
-rw-r--r--tests/ui/dropck/let-else-more-permissive.stderr35
5 files changed, 233 insertions, 0 deletions
diff --git a/tests/ui/drop/or-pattern-drop-order.rs b/tests/ui/drop/or-pattern-drop-order.rs
new file mode 100644
index 00000000000..fdc28225c35
--- /dev/null
+++ b/tests/ui/drop/or-pattern-drop-order.rs
@@ -0,0 +1,109 @@
+//@ run-pass
+//! Test drop order for different ways of declaring pattern bindings involving or-patterns.
+//! Currently, it's inconsistent between language constructs (#142163).
+
+use std::cell::RefCell;
+use std::ops::Drop;
+
+// For more informative failures, we collect drops in a `Vec` before checking their order.
+struct DropOrder(RefCell<Vec<u32>>);
+struct LogDrop<'o>(&'o DropOrder, u32);
+
+impl<'o> Drop for LogDrop<'o> {
+    fn drop(&mut self) {
+        self.0.0.borrow_mut().push(self.1);
+    }
+}
+
+#[track_caller]
+fn assert_drop_order(expected_drops: impl IntoIterator<Item = u32>, f: impl Fn(&DropOrder)) {
+    let order = DropOrder(RefCell::new(Vec::new()));
+    f(&order);
+    let order = order.0.into_inner();
+    let correct_order: Vec<u32> = expected_drops.into_iter().collect();
+    assert_eq!(order, correct_order);
+}
+
+#[expect(unused_variables, unused_assignments, irrefutable_let_patterns)]
+fn main() {
+    // When bindings are declared with `let pat;`, they're visited in left-to-right order, using the
+    // order given by the first occurrence of each variable. They're later dropped in reverse.
+    assert_drop_order(1..=3, |o| {
+        // Drops are right-to-left: `z`, `y`, `x`.
+        let (x, Ok(y) | Err(y), z);
+        // Assignment order doesn't matter.
+        z = LogDrop(o, 1);
+        y = LogDrop(o, 2);
+        x = LogDrop(o, 3);
+    });
+    assert_drop_order(1..=2, |o| {
+        // The first or-pattern alternative determines the bindings' drop order: `y`, `x`.
+        let ((true, x, y) | (false, y, x));
+        x = LogDrop(o, 2);
+        y = LogDrop(o, 1);
+    });
+
+    // When bindings are declared with `let pat = expr;`, bindings within or-patterns are seen last,
+    // thus they're dropped first.
+    assert_drop_order(1..=3, |o| {
+        // Drops are right-to-left, treating `y` as rightmost: `y`, `z`, `x`.
+        let (x, Ok(y) | Err(y), z) = (LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2));
+    });
+    assert_drop_order(1..=2, |o| {
+        // The first or-pattern alternative determines the bindings' drop order: `y`, `x`.
+        let ((true, x, y) | (false, y, x)) = (true, LogDrop(o, 2), LogDrop(o, 1));
+    });
+    assert_drop_order(1..=2, |o| {
+        // That drop order is used regardless of which or-pattern alternative matches: `y`, `x`.
+        let ((true, x, y) | (false, y, x)) = (false, LogDrop(o, 1), LogDrop(o, 2));
+    });
+
+    // `match` treats or-patterns as last like `let pat = expr;`, but also determines drop order
+    // using the order of the bindings in the *last* or-pattern alternative.
+    assert_drop_order(1..=3, |o| {
+        // Drops are right-to-left, treating `y` as rightmost: `y`, `z`, `x`.
+        match (LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2)) { (x, Ok(y) | Err(y), z) => {} }
+    });
+    assert_drop_order(1..=2, |o| {
+        // The last or-pattern alternative determines the bindings' drop order: `x`, `y`.
+        match (true, LogDrop(o, 1), LogDrop(o, 2)) { (true, x, y) | (false, y, x) => {} }
+    });
+    assert_drop_order(1..=2, |o| {
+        // That drop order is used regardless of which or-pattern alternative matches: `x`, `y`.
+        match (false, LogDrop(o, 2), LogDrop(o, 1)) { (true, x, y) | (false, y, x) => {} }
+    });
+
+    // Function params are visited one-by-one, and the order of bindings within a param's pattern is
+    // the same as `let pat = expr`;
+    assert_drop_order(1..=3, |o| {
+        // Among separate params, the drop order is right-to-left: `z`, `y`, `x`.
+        (|x, (Ok(y) | Err(y)), z| {})(LogDrop(o, 3), Ok(LogDrop(o, 2)), LogDrop(o, 1));
+    });
+    assert_drop_order(1..=3, |o| {
+        // Within a param's pattern, or-patterns are treated as rightmost: `y`, `z`, `x`.
+        (|(x, Ok(y) | Err(y), z)| {})((LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2)));
+    });
+    assert_drop_order(1..=2, |o| {
+        // The first or-pattern alternative determines the bindings' drop order: `y`, `x`.
+        (|((true, x, y) | (false, y, x))| {})((true, LogDrop(o, 2), LogDrop(o, 1)));
+    });
+
+    // `if let` and `let`-`else` see bindings in the same order as `let pat = expr;`.
+    // Vars in or-patterns are seen last (dropped first), and the first alternative's order is used.
+    assert_drop_order(1..=3, |o| {
+        if let (x, Ok(y) | Err(y), z) = (LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2)) {}
+    });
+    assert_drop_order(1..=3, |o| {
+        let (x, Ok(y) | Err(y), z) = (LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2)) else {
+            unreachable!();
+        };
+    });
+    assert_drop_order(1..=2, |o| {
+        if let (true, x, y) | (false, y, x) = (true, LogDrop(o, 2), LogDrop(o, 1)) {}
+    });
+    assert_drop_order(1..=2, |o| {
+        let ((true, x, y) | (false, y, x)) = (true, LogDrop(o, 2), LogDrop(o, 1)) else {
+            unreachable!();
+        };
+    });
+}
diff --git a/tests/ui/dropck/eager-by-ref-binding-for-guards.rs b/tests/ui/dropck/eager-by-ref-binding-for-guards.rs
new file mode 100644
index 00000000000..3f475839171
--- /dev/null
+++ b/tests/ui/dropck/eager-by-ref-binding-for-guards.rs
@@ -0,0 +1,31 @@
+//! The drop check is currently more permissive when match arms have guards, due to eagerly creating
+//! by-ref bindings for the guard (#142057).
+
+struct Struct<T>(T);
+impl<T> Drop for Struct<T> {
+    fn drop(&mut self) {}
+}
+
+fn main() {
+    // This is an error: `short1` is dead before `long1` is dropped.
+    match (Struct(&&0), 1) {
+        (mut long1, ref short1) => long1.0 = &short1,
+        //~^ ERROR `short1` does not live long enough
+    }
+    // This is OK: `short2`'s storage is live until after `long2`'s drop runs.
+    match (Struct(&&0), 1) {
+        (mut long2, ref short2) if true => long2.0 = &short2,
+        _ => unreachable!(),
+    }
+    // This depends on the binding modes of the final or-pattern alternatives (see #142163):
+    let res: &Result<u8, &u8> = &Ok(1);
+    match (Struct(&&0), res) {
+        (mut long3, Ok(short3) | &Err(short3)) if true => long3.0 = &short3,
+        //~^ ERROR `short3` does not live long enough
+        _ => unreachable!(),
+    }
+    match (Struct(&&0), res) {
+        (mut long4, &Err(short4) | Ok(short4)) if true => long4.0 = &short4,
+        _ => unreachable!(),
+    }
+}
diff --git a/tests/ui/dropck/eager-by-ref-binding-for-guards.stderr b/tests/ui/dropck/eager-by-ref-binding-for-guards.stderr
new file mode 100644
index 00000000000..cb1a04cd444
--- /dev/null
+++ b/tests/ui/dropck/eager-by-ref-binding-for-guards.stderr
@@ -0,0 +1,28 @@
+error[E0597]: `short1` does not live long enough
+  --> $DIR/eager-by-ref-binding-for-guards.rs:12:46
+   |
+LL |         (mut long1, ref short1) => long1.0 = &short1,
+   |                     ----------               ^^^^^^-
+   |                     |                        |     |
+   |                     |                        |     `short1` dropped here while still borrowed
+   |                     |                        |     borrow might be used here, when `long1` is dropped and runs the `Drop` code for type `Struct`
+   |                     |                        borrowed value does not live long enough
+   |                     binding `short1` declared here
+   |
+   = note: values in a scope are dropped in the opposite order they are defined
+
+error[E0597]: `short3` does not live long enough
+  --> $DIR/eager-by-ref-binding-for-guards.rs:23:69
+   |
+LL |         (mut long3, Ok(short3) | &Err(short3)) if true => long3.0 = &short3,
+   |                        ------                                       ^^^^^^-
+   |                        |                                            |     |
+   |                        |                                            |     `short3` dropped here while still borrowed
+   |                        |                                            |     borrow might be used here, when `long3` is dropped and runs the `Drop` code for type `Struct`
+   |                        binding `short3` declared here               borrowed value does not live long enough
+   |
+   = note: values in a scope are dropped in the opposite order they are defined
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/tests/ui/dropck/let-else-more-permissive.rs b/tests/ui/dropck/let-else-more-permissive.rs
new file mode 100644
index 00000000000..0020814aa81
--- /dev/null
+++ b/tests/ui/dropck/let-else-more-permissive.rs
@@ -0,0 +1,30 @@
+//! The drop check is currently more permissive when `let` statements have an `else` block, due to
+//! scheduling drops for bindings' storage before pattern-matching (#142056).
+
+struct Struct<T>(T);
+impl<T> Drop for Struct<T> {
+    fn drop(&mut self) {}
+}
+
+fn main() {
+    {
+        // This is an error: `short1` is dead before `long1` is dropped.
+        let (mut long1, short1) = (Struct(&0), 1);
+        long1.0 = &short1;
+        //~^ ERROR `short1` does not live long enough
+    }
+    {
+        // This is OK: `short2`'s storage is live until after `long2`'s drop runs.
+        #[expect(irrefutable_let_patterns)]
+        let (mut long2, short2) = (Struct(&0), 1) else { unreachable!() };
+        long2.0 = &short2;
+    }
+    {
+        // Sanity check: `short3`'s drop is significant; it's dropped before `long3`:
+        let tmp = Box::new(0);
+        #[expect(irrefutable_let_patterns)]
+        let (mut long3, short3) = (Struct(&tmp), Box::new(1)) else { unreachable!() };
+        long3.0 = &short3;
+        //~^ ERROR `short3` does not live long enough
+    }
+}
diff --git a/tests/ui/dropck/let-else-more-permissive.stderr b/tests/ui/dropck/let-else-more-permissive.stderr
new file mode 100644
index 00000000000..7c37e170afa
--- /dev/null
+++ b/tests/ui/dropck/let-else-more-permissive.stderr
@@ -0,0 +1,35 @@
+error[E0597]: `short1` does not live long enough
+  --> $DIR/let-else-more-permissive.rs:13:19
+   |
+LL |         let (mut long1, short1) = (Struct(&0), 1);
+   |                         ------ binding `short1` declared here
+LL |         long1.0 = &short1;
+   |                   ^^^^^^^ borrowed value does not live long enough
+LL |
+LL |     }
+   |     -
+   |     |
+   |     `short1` dropped here while still borrowed
+   |     borrow might be used here, when `long1` is dropped and runs the `Drop` code for type `Struct`
+   |
+   = note: values in a scope are dropped in the opposite order they are defined
+
+error[E0597]: `short3` does not live long enough
+  --> $DIR/let-else-more-permissive.rs:27:19
+   |
+LL |         let (mut long3, short3) = (Struct(&tmp), Box::new(1)) else { unreachable!() };
+   |                         ------ binding `short3` declared here
+LL |         long3.0 = &short3;
+   |                   ^^^^^^^ borrowed value does not live long enough
+LL |
+LL |     }
+   |     -
+   |     |
+   |     `short3` dropped here while still borrowed
+   |     borrow might be used here, when `long3` is dropped and runs the `Drop` code for type `Struct`
+   |
+   = note: values in a scope are dropped in the opposite order they are defined
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0597`.