1#![warn(rust_2018_idioms, single_use_lifetimes)]
2
3// Refs: https://doc.rust-lang.org/reference/destructors.html
4
5use std::{cell::Cell, panic, pin::Pin, thread};
6
7use pin_project_lite::pin_project;
8
9struct D<'a>(&'a Cell<usize>, usize);
10
11impl Drop for D<'_> {
12 fn drop(&mut self) {
13 if !thread::panicking() {
14 let old = self.0.replace(self.1);
15 assert_eq!(old, self.1 - 1);
16 }
17 }
18}
19
20pin_project! {
21#[project = StructPinnedProj]
22#[project_ref = StructPinnedProjRef]
23#[project_replace = StructPinnedProjReplace]
24struct StructPinned<'a> {
25 #[pin]
26 f1: D<'a>,
27 #[pin]
28 f2: D<'a>,
29}
30}
31
32pin_project! {
33#[project = StructUnpinnedProj]
34#[project_ref = StructUnpinnedProjRef]
35#[project_replace = StructUnpinnedProjReplace]
36struct StructUnpinned<'a> {
37 f1: D<'a>,
38 f2: D<'a>,
39}
40}
41
42pin_project! {
43#[project_replace = EnumProjReplace]
44enum Enum<'a> {
45 #[allow(dead_code)] // false positive that fixed in Rust 1.38
46 StructPinned {
47 #[pin]
48 f1: D<'a>,
49 #[pin]
50 f2: D<'a>,
51 },
52 #[allow(dead_code)] // false positive that fixed in Rust 1.38
53 StructUnpinned {
54 f1: D<'a>,
55 f2: D<'a>,
56 },
57}
58}
59
60#[test]
61fn struct_pinned() {
62 {
63 let c = Cell::new(0);
64 let _x = StructPinned { f1: D(&c, 1), f2: D(&c, 2) };
65 }
66 {
67 let c = Cell::new(0);
68 let mut x = StructPinned { f1: D(&c, 1), f2: D(&c, 2) };
69 let y = Pin::new(&mut x);
70 let _z = y.project_replace(StructPinned { f1: D(&c, 3), f2: D(&c, 4) });
71 }
72}
73
74#[test]
75fn struct_unpinned() {
76 {
77 let c = Cell::new(0);
78 let _x = StructUnpinned { f1: D(&c, 1), f2: D(&c, 2) };
79 }
80 {
81 let c = Cell::new(0);
82 let mut x = StructUnpinned { f1: D(&c, 1), f2: D(&c, 2) };
83 let y = Pin::new(&mut x);
84 let _z = y.project_replace(StructUnpinned { f1: D(&c, 3), f2: D(&c, 4) });
85 }
86}
87
88#[test]
89fn enum_struct() {
90 {
91 let c = Cell::new(0);
92 let _x = Enum::StructPinned { f1: D(&c, 1), f2: D(&c, 2) };
93 }
94 {
95 let c = Cell::new(0);
96 let mut x = Enum::StructPinned { f1: D(&c, 1), f2: D(&c, 2) };
97 let y = Pin::new(&mut x);
98 let _z = y.project_replace(Enum::StructPinned { f1: D(&c, 3), f2: D(&c, 4) });
99 }
100
101 {
102 let c = Cell::new(0);
103 let _x = Enum::StructUnpinned { f1: D(&c, 1), f2: D(&c, 2) };
104 }
105 {
106 let c = Cell::new(0);
107 let mut x = Enum::StructUnpinned { f1: D(&c, 1), f2: D(&c, 2) };
108 let y = Pin::new(&mut x);
109 let _z = y.project_replace(Enum::StructUnpinned { f1: D(&c, 3), f2: D(&c, 4) });
110 }
111}
112
113// https://github.com/rust-lang/rust/issues/47949
114// https://github.com/taiki-e/pin-project/pull/194#discussion_r419098111
115#[allow(clippy::many_single_char_names)]
116#[test]
117fn project_replace_panic() {
118 pin_project! {
119 #[project_replace = SProjReplace]
120 struct S<T, U> {
121 #[pin]
122 pinned: T,
123 unpinned: U,
124 }
125 }
126
127 struct D<'a>(&'a mut bool, bool);
128 impl Drop for D<'_> {
129 fn drop(&mut self) {
130 *self.0 = true;
131 if self.1 {
132 panic!();
133 }
134 }
135 }
136
137 let (mut a, mut b, mut c, mut d) = (false, false, false, false);
138 let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
139 let mut x = S { pinned: D(&mut a, true), unpinned: D(&mut b, false) };
140 let _y = Pin::new(&mut x)
141 .project_replace(S { pinned: D(&mut c, false), unpinned: D(&mut d, false) });
142 // Previous `x.pinned` was dropped and panicked when `project_replace` is
143 // called, so this is unreachable.
144 unreachable!();
145 }));
146 assert!(res.is_err());
147 assert!(a);
148 assert!(b);
149 assert!(c);
150 assert!(d);
151
152 let (mut a, mut b, mut c, mut d) = (false, false, false, false);
153 let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
154 let mut x = S { pinned: D(&mut a, false), unpinned: D(&mut b, true) };
155 {
156 let _y = Pin::new(&mut x)
157 .project_replace(S { pinned: D(&mut c, false), unpinned: D(&mut d, false) });
158 // `_y` (previous `x.unpinned`) live to the end of this scope, so
159 // this is not unreachable.
160 // unreachable!();
161 }
162 unreachable!();
163 }));
164 assert!(res.is_err());
165 assert!(a);
166 assert!(b);
167 assert!(c);
168 assert!(d);
169}
170