• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #![warn(rust_2018_idioms, single_use_lifetimes)]
2 
3 // Refs: https://doc.rust-lang.org/reference/destructors.html
4 
5 use std::{cell::Cell, panic, pin::Pin, thread};
6 
7 use pin_project_lite::pin_project;
8 
9 struct D<'a>(&'a Cell<usize>, usize);
10 
11 impl Drop for D<'_> {
drop(&mut self)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 
20 pin_project! {
21 #[project = StructPinnedProj]
22 #[project_ref = StructPinnedProjRef]
23 #[project_replace = StructPinnedProjReplace]
24 struct StructPinned<'a> {
25     #[pin]
26     f1: D<'a>,
27     #[pin]
28     f2: D<'a>,
29 }
30 }
31 
32 pin_project! {
33 #[project = StructUnpinnedProj]
34 #[project_ref = StructUnpinnedProjRef]
35 #[project_replace = StructUnpinnedProjReplace]
36 struct StructUnpinned<'a> {
37     f1: D<'a>,
38     f2: D<'a>,
39 }
40 }
41 
42 pin_project! {
43 #[project_replace = EnumProjReplace]
44 enum 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]
struct_pinned()61 fn 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]
struct_unpinned()75 fn 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]
enum_struct()89 fn 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]
project_replace_panic()117 fn 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