summaryrefslogtreecommitdiffstats
path: root/third_party/rust/pin-project-lite/tests/drop_order.rs
diff options
context:
space:
mode:
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.rs169
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);
+}