| // N.B. drop-order-comparisons-let-chains.rs is part of this test. |
| // It is separate because let chains cannot be parsed before Rust 2024. |
| // |
| // 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(if_let_guard)] |
| #![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_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); |
| } |
| |
| 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_nested_else(); |
| t_if_let_nested_then_else(); |
| t_let_else_chained_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); |
| } |
| } |