rust/tests/ui/drop/drop-order-comparisons.rs

575 lines
16 KiB
Rust

// 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
#![cfg_attr(e2021, 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);
}
}