diff options
Diffstat (limited to 'third_party/rust/pin-project-lite/tests/drop_order.rs')
-rw-r--r-- | third_party/rust/pin-project-lite/tests/drop_order.rs | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/third_party/rust/pin-project-lite/tests/drop_order.rs b/third_party/rust/pin-project-lite/tests/drop_order.rs new file mode 100644 index 0000000000..6e5deaf154 --- /dev/null +++ b/third_party/rust/pin-project-lite/tests/drop_order.rs @@ -0,0 +1,169 @@ +#![warn(rust_2018_idioms, single_use_lifetimes)] + +// Refs: https://doc.rust-lang.org/reference/destructors.html + +use std::{cell::Cell, panic, pin::Pin, thread}; + +use pin_project_lite::pin_project; + +struct D<'a>(&'a Cell<usize>, usize); + +impl Drop for D<'_> { + fn drop(&mut self) { + if !thread::panicking() { + let old = self.0.replace(self.1); + assert_eq!(old, self.1 - 1); + } + } +} + +pin_project! { +#[project = StructPinnedProj] +#[project_ref = StructPinnedProjRef] +#[project_replace = StructPinnedProjReplace] +struct StructPinned<'a> { + #[pin] + f1: D<'a>, + #[pin] + f2: D<'a>, +} +} + +pin_project! { +#[project = StructUnpinnedProj] +#[project_ref = StructUnpinnedProjRef] +#[project_replace = StructUnpinnedProjReplace] +struct StructUnpinned<'a> { + f1: D<'a>, + f2: D<'a>, +} +} + +pin_project! { +#[project_replace = EnumProjReplace] +enum Enum<'a> { + #[allow(dead_code)] // false positive that fixed in Rust 1.38 + StructPinned { + #[pin] + f1: D<'a>, + #[pin] + f2: D<'a>, + }, + #[allow(dead_code)] // false positive that fixed in Rust 1.38 + StructUnpinned { + f1: D<'a>, + f2: D<'a>, + }, +} +} + +#[test] +fn struct_pinned() { + { + let c = Cell::new(0); + let _x = StructPinned { f1: D(&c, 1), f2: D(&c, 2) }; + } + { + let c = Cell::new(0); + let mut x = StructPinned { f1: D(&c, 1), f2: D(&c, 2) }; + let y = Pin::new(&mut x); + let _z = y.project_replace(StructPinned { f1: D(&c, 3), f2: D(&c, 4) }); + } +} + +#[test] +fn struct_unpinned() { + { + let c = Cell::new(0); + let _x = StructUnpinned { f1: D(&c, 1), f2: D(&c, 2) }; + } + { + let c = Cell::new(0); + let mut x = StructUnpinned { f1: D(&c, 1), f2: D(&c, 2) }; + let y = Pin::new(&mut x); + let _z = y.project_replace(StructUnpinned { f1: D(&c, 3), f2: D(&c, 4) }); + } +} + +#[test] +fn enum_struct() { + { + let c = Cell::new(0); + let _x = Enum::StructPinned { f1: D(&c, 1), f2: D(&c, 2) }; + } + { + let c = Cell::new(0); + let mut x = Enum::StructPinned { f1: D(&c, 1), f2: D(&c, 2) }; + let y = Pin::new(&mut x); + let _z = y.project_replace(Enum::StructPinned { f1: D(&c, 3), f2: D(&c, 4) }); + } + + { + let c = Cell::new(0); + let _x = Enum::StructUnpinned { f1: D(&c, 1), f2: D(&c, 2) }; + } + { + let c = Cell::new(0); + let mut x = Enum::StructUnpinned { f1: D(&c, 1), f2: D(&c, 2) }; + let y = Pin::new(&mut x); + let _z = y.project_replace(Enum::StructUnpinned { f1: D(&c, 3), f2: D(&c, 4) }); + } +} + +// https://github.com/rust-lang/rust/issues/47949 +// https://github.com/taiki-e/pin-project/pull/194#discussion_r419098111 +#[allow(clippy::many_single_char_names)] +#[test] +fn project_replace_panic() { + pin_project! { + #[project_replace = SProjReplace] + struct S<T, U> { + #[pin] + pinned: T, + unpinned: U, + } + } + + struct D<'a>(&'a mut bool, bool); + impl Drop for D<'_> { + fn drop(&mut self) { + *self.0 = true; + if self.1 { + panic!(); + } + } + } + + let (mut a, mut b, mut c, mut d) = (false, false, false, false); + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { + let mut x = S { pinned: D(&mut a, true), unpinned: D(&mut b, false) }; + let _y = Pin::new(&mut x) + .project_replace(S { pinned: D(&mut c, false), unpinned: D(&mut d, false) }); + // Previous `x.pinned` was dropped and panicked when `project_replace` is + // called, so this is unreachable. + unreachable!(); + })); + assert!(res.is_err()); + assert!(a); + assert!(b); + assert!(c); + assert!(d); + + let (mut a, mut b, mut c, mut d) = (false, false, false, false); + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { + let mut x = S { pinned: D(&mut a, false), unpinned: D(&mut b, true) }; + { + let _y = Pin::new(&mut x) + .project_replace(S { pinned: D(&mut c, false), unpinned: D(&mut d, false) }); + // `_y` (previous `x.unpinned`) live to the end of this scope, so + // this is not unreachable. + // unreachable!(); + } + unreachable!(); + })); + assert!(res.is_err()); + assert!(a); + assert!(b); + assert!(c); + assert!(d); +} |