1#![warn(rust_2018_idioms, single_use_lifetimes)]
2#![allow(dead_code)]
3
4#[macro_use]
5mod auxiliary;
6
7use std::{
8 marker::{PhantomData, PhantomPinned},
9 panic,
10 pin::Pin,
11};
12
13use pin_project::{pin_project, pinned_drop, UnsafeUnpin};
14
15#[test]
16fn projection() {
17 #[pin_project(
18 project = StructProj,
19 project_ref = StructProjRef,
20 project_replace = StructProjOwn,
21 )]
22 struct Struct<T, U> {
23 #[pin]
24 f1: T,
25 f2: U,
26 }
27
28 let mut s = Struct { f1: 1, f2: 2 };
29 let mut s_orig = Pin::new(&mut s);
30 let s = s_orig.as_mut().project();
31
32 let _: Pin<&mut i32> = s.f1;
33 assert_eq!(*s.f1, 1);
34 let _: &mut i32 = s.f2;
35 assert_eq!(*s.f2, 2);
36
37 assert_eq!(s_orig.as_ref().f1, 1);
38 assert_eq!(s_orig.as_ref().f2, 2);
39
40 let mut s = Struct { f1: 1, f2: 2 };
41 let mut s = Pin::new(&mut s);
42 {
43 let StructProj { f1, f2 } = s.as_mut().project();
44 let _: Pin<&mut i32> = f1;
45 let _: &mut i32 = f2;
46 }
47 {
48 let StructProjRef { f1, f2 } = s.as_ref().project_ref();
49 let _: Pin<&i32> = f1;
50 let _: &i32 = f2;
51 }
52 {
53 let StructProjOwn { f1, f2 } = s.as_mut().project_replace(Struct { f1: 3, f2: 4 });
54 let _: PhantomData<i32> = f1;
55 let _: i32 = f2;
56 assert_eq!(f2, 2);
57 assert_eq!(s.f1, 3);
58 assert_eq!(s.f2, 4);
59 }
60
61 #[pin_project(project_replace)]
62 struct TupleStruct<T, U>(#[pin] T, U);
63
64 let mut s = TupleStruct(1, 2);
65 let s = Pin::new(&mut s).project();
66
67 let _: Pin<&mut i32> = s.0;
68 assert_eq!(*s.0, 1);
69 let _: &mut i32 = s.1;
70 assert_eq!(*s.1, 2);
71
72 #[pin_project(project = EnumProj, project_ref = EnumProjRef, project_replace = EnumProjOwn)]
73 #[derive(Eq, PartialEq, Debug)]
74 enum Enum<A, B, C, D> {
75 Tuple(#[pin] A, B),
76 Struct {
77 #[pin]
78 f1: C,
79 f2: D,
80 },
81 Unit,
82 }
83
84 let mut e = Enum::Tuple(1, 2);
85 let mut e = Pin::new(&mut e);
86
87 match e.as_mut().project() {
88 EnumProj::Tuple(x, y) => {
89 let x: Pin<&mut i32> = x;
90 assert_eq!(*x, 1);
91 let y: &mut i32 = y;
92 assert_eq!(*y, 2);
93 }
94 EnumProj::Struct { f1, f2 } => {
95 let _: Pin<&mut i32> = f1;
96 let _: &mut i32 = f2;
97 unreachable!();
98 }
99 EnumProj::Unit => unreachable!(),
100 }
101
102 assert_eq!(&*e, &Enum::Tuple(1, 2));
103
104 let mut e = Enum::Struct { f1: 3, f2: 4 };
105 let mut e = Pin::new(&mut e);
106
107 match e.as_mut().project() {
108 EnumProj::Tuple(x, y) => {
109 let _: Pin<&mut i32> = x;
110 let _: &mut i32 = y;
111 unreachable!();
112 }
113 EnumProj::Struct { f1, f2 } => {
114 let _: Pin<&mut i32> = f1;
115 assert_eq!(*f1, 3);
116 let _: &mut i32 = f2;
117 assert_eq!(*f2, 4);
118 }
119 EnumProj::Unit => unreachable!(),
120 }
121
122 if let EnumProj::Struct { f1, f2 } = e.as_mut().project() {
123 let _: Pin<&mut i32> = f1;
124 assert_eq!(*f1, 3);
125 let _: &mut i32 = f2;
126 assert_eq!(*f2, 4);
127 }
128}
129
130#[test]
131fn enum_project_set() {
132 #[pin_project(project = EnumProj, project_ref = EnumProjRef, project_replace = EnumProjOwn)]
133 #[derive(Eq, PartialEq, Debug)]
134 enum Enum {
135 V1(#[pin] u8),
136 V2(bool),
137 }
138
139 let mut e = Enum::V1(25);
140 let mut e_orig = Pin::new(&mut e);
141 let e_proj = e_orig.as_mut().project();
142
143 match e_proj {
144 EnumProj::V1(val) => {
145 let new_e = Enum::V2(val.as_ref().get_ref() == &25);
146 e_orig.set(new_e);
147 }
148 EnumProj::V2(_) => unreachable!(),
149 }
150
151 assert_eq!(e, Enum::V2(true));
152}
153
154#[test]
155fn where_clause() {
156 #[pin_project]
157 struct Struct<T>
158 where
159 T: Copy,
160 {
161 f: T,
162 }
163
164 #[pin_project]
165 struct TupleStruct<T>(T)
166 where
167 T: Copy;
168
169 #[pin_project(project = EnumProj, project_ref = EnumProjRef, project_replace = EnumProjOwn)]
170 enum Enum<T>
171 where
172 T: Copy,
173 {
174 V(T),
175 }
176}
177
178#[test]
179fn where_clause_and_associated_type_field() {
180 #[pin_project(project_replace)]
181 struct Struct1<I>
182 where
183 I: Iterator,
184 {
185 #[pin]
186 f1: I,
187 f2: I::Item,
188 }
189
190 #[pin_project(project_replace)]
191 struct Struct2<I, J>
192 where
193 I: Iterator<Item = J>,
194 {
195 #[pin]
196 f1: I,
197 f2: J,
198 }
199
200 #[pin_project(project_replace)]
201 struct Struct3<T>
202 where
203 T: 'static,
204 {
205 f: T,
206 }
207
208 trait Static: 'static {}
209
210 impl<T> Static for Struct3<T> {}
211
212 #[pin_project(project_replace)]
213 struct TupleStruct<I>(#[pin] I, I::Item)
214 where
215 I: Iterator;
216
217 #[pin_project(project = EnumProj, project_ref = EnumProjRef, project_replace = EnumProjOwn)]
218 enum Enum<I>
219 where
220 I: Iterator,
221 {
222 V1(#[pin] I),
223 V2(I::Item),
224 }
225}
226
227#[test]
228fn derive_copy() {
229 #[pin_project(project_replace)]
230 #[derive(Clone, Copy)]
231 struct Struct<T> {
232 f: T,
233 }
234
235 fn is_copy<T: Copy>() {}
236
237 is_copy::<Struct<u8>>();
238}
239
240#[test]
241fn move_out() {
242 struct NotCopy;
243
244 #[pin_project(project_replace)]
245 struct Struct {
246 f: NotCopy,
247 }
248
249 let x = Struct { f: NotCopy };
250 let _val: NotCopy = x.f;
251
252 #[pin_project(project = EnumProj, project_ref = EnumProjRef, project_replace = EnumProjOwn)]
253 enum Enum {
254 V(NotCopy),
255 }
256
257 let x = Enum::V(NotCopy);
258 #[allow(clippy::infallible_destructuring_match)]
259 let _val: NotCopy = match x {
260 Enum::V(val) => val,
261 };
262}
263
264#[test]
265fn trait_bounds_on_type_generics() {
266 #[pin_project(project_replace)]
267 pub struct Struct1<'a, T: ?Sized> {
268 f: &'a mut T,
269 }
270
271 #[pin_project(project_replace)]
272 pub struct Struct2<'a, T: ::core::fmt::Debug> {
273 f: &'a mut T,
274 }
275
276 #[pin_project(project_replace)]
277 pub struct Struct3<'a, T: core::fmt::Debug> {
278 f: &'a mut T,
279 }
280
281 #[pin_project(project_replace)]
282 pub struct Struct4<'a, T: core::fmt::Debug + core::fmt::Display> {
283 f: &'a mut T,
284 }
285
286 #[pin_project(project_replace)]
287 pub struct Struct5<'a, T: core::fmt::Debug + ?Sized> {
288 f: &'a mut T,
289 }
290
291 #[pin_project(project_replace)]
292 pub struct Struct6<'a, T: core::fmt::Debug = [u8; 16]> {
293 f: &'a mut T,
294 }
295
296 let _: Struct6<'_> = Struct6 { f: &mut [0_u8; 16] };
297
298 #[pin_project(project_replace)]
299 pub struct Struct7<T: 'static> {
300 f: T,
301 }
302
303 trait Static: 'static {}
304
305 impl<T> Static for Struct7<T> {}
306
307 #[pin_project(project_replace)]
308 pub struct Struct8<'a, 'b: 'a> {
309 f1: &'a u8,
310 f2: &'b u8,
311 }
312
313 #[pin_project(project_replace)]
314 pub struct TupleStruct<'a, T: ?Sized>(&'a mut T);
315
316 #[pin_project(project = EnumProj, project_ref = EnumProjRef, project_replace = EnumProjOwn)]
317 enum Enum<'a, T: ?Sized> {
318 V(&'a mut T),
319 }
320}
321
322#[test]
323fn overlapping_lifetime_names() {
324 #[pin_project(project_replace)]
325 pub struct Struct1<'pin, T> {
326 #[pin]
327 f: &'pin mut T,
328 }
329
330 #[pin_project(project_replace)]
331 pub struct Struct2<'pin, 'pin_, 'pin__> {
332 #[pin]
333 f: &'pin &'pin_ &'pin__ (),
334 }
335
336 pub trait Trait<'a> {}
337
338 #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058
339 #[pin_project(project_replace)]
340 pub struct Hrtb<'pin___, T>
341 where
342 for<'pin> &'pin T: Unpin,
343 T: for<'pin> Trait<'pin>,
344 for<'pin, 'pin_, 'pin__> &'pin &'pin_ &'pin__ T: Unpin,
345 {
346 #[pin]
347 f: &'pin___ mut T,
348 }
349
350 #[pin_project(PinnedDrop)]
351 pub struct PinnedDropStruct<'pin> {
352 #[pin]
353 f: &'pin (),
354 }
355
356 #[pinned_drop]
357 impl PinnedDrop for PinnedDropStruct<'_> {
358 fn drop(self: Pin<&mut Self>) {}
359 }
360
361 #[pin_project(UnsafeUnpin)]
362 pub struct UnsafeUnpinStruct<'pin> {
363 #[pin]
364 f: &'pin (),
365 }
366
367 unsafe impl UnsafeUnpin for UnsafeUnpinStruct<'_> {}
368
369 #[pin_project(!Unpin)]
370 pub struct NotUnpinStruct<'pin> {
371 #[pin]
372 f: &'pin (),
373 }
374}
375
376#[test]
377fn combine() {
378 #[pin_project(PinnedDrop, UnsafeUnpin)]
379 pub struct PinnedDropWithUnsafeUnpin<T> {
380 #[pin]
381 f: T,
382 }
383
384 #[pinned_drop]
385 impl<T> PinnedDrop for PinnedDropWithUnsafeUnpin<T> {
386 fn drop(self: Pin<&mut Self>) {}
387 }
388
389 unsafe impl<T: Unpin> UnsafeUnpin for PinnedDropWithUnsafeUnpin<T> {}
390
391 #[pin_project(PinnedDrop, !Unpin)]
392 pub struct PinnedDropWithNotUnpin<T> {
393 #[pin]
394 f: T,
395 }
396
397 #[pinned_drop]
398 impl<T> PinnedDrop for PinnedDropWithNotUnpin<T> {
399 fn drop(self: Pin<&mut Self>) {}
400 }
401
402 #[pin_project(UnsafeUnpin, project_replace)]
403 pub struct UnsafeUnpinWithReplace<T> {
404 #[pin]
405 f: T,
406 }
407
408 unsafe impl<T: Unpin> UnsafeUnpin for UnsafeUnpinWithReplace<T> {}
409
410 #[pin_project(!Unpin, project_replace)]
411 pub struct NotUnpinWithReplace<T> {
412 #[pin]
413 f: T,
414 }
415}
416
417#[test]
418fn private_type_in_public_type() {
419 #[pin_project(project_replace)]
420 pub struct PublicStruct<T> {
421 #[pin]
422 inner: PrivateStruct<T>,
423 }
424
425 struct PrivateStruct<T>(T);
426}
427
428#[allow(clippy::needless_lifetimes)]
429#[test]
430fn lifetime_project() {
431 #[pin_project(project_replace)]
432 struct Struct1<T, U> {
433 #[pin]
434 pinned: T,
435 unpinned: U,
436 }
437
438 #[pin_project(project_replace)]
439 struct Struct2<'a, T, U> {
440 #[pin]
441 pinned: &'a T,
442 unpinned: U,
443 }
444
445 #[pin_project(project = EnumProj, project_ref = EnumProjRef, project_replace = EnumProjOwn)]
446 enum Enum<T, U> {
447 V {
448 #[pin]
449 pinned: T,
450 unpinned: U,
451 },
452 }
453
454 impl<T, U> Struct1<T, U> {
455 fn get_pin_ref<'a>(self: Pin<&'a Self>) -> Pin<&'a T> {
456 self.project_ref().pinned
457 }
458 fn get_pin_mut<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut T> {
459 self.project().pinned
460 }
461 fn get_pin_ref_elided(self: Pin<&Self>) -> Pin<&T> {
462 self.project_ref().pinned
463 }
464 fn get_pin_mut_elided(self: Pin<&mut Self>) -> Pin<&mut T> {
465 self.project().pinned
466 }
467 }
468
469 impl<'b, T, U> Struct2<'b, T, U> {
470 fn get_pin_ref<'a>(self: Pin<&'a Self>) -> Pin<&'a &'b T> {
471 self.project_ref().pinned
472 }
473 fn get_pin_mut<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut &'b T> {
474 self.project().pinned
475 }
476 fn get_pin_ref_elided(self: Pin<&Self>) -> Pin<&&'b T> {
477 self.project_ref().pinned
478 }
479 fn get_pin_mut_elided(self: Pin<&mut Self>) -> Pin<&mut &'b T> {
480 self.project().pinned
481 }
482 }
483
484 impl<T, U> Enum<T, U> {
485 fn get_pin_ref<'a>(self: Pin<&'a Self>) -> Pin<&'a T> {
486 match self.project_ref() {
487 EnumProjRef::V { pinned, .. } => pinned,
488 }
489 }
490 fn get_pin_mut<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut T> {
491 match self.project() {
492 EnumProj::V { pinned, .. } => pinned,
493 }
494 }
495 fn get_pin_ref_elided(self: Pin<&Self>) -> Pin<&T> {
496 match self.project_ref() {
497 EnumProjRef::V { pinned, .. } => pinned,
498 }
499 }
500 fn get_pin_mut_elided(self: Pin<&mut Self>) -> Pin<&mut T> {
501 match self.project() {
502 EnumProj::V { pinned, .. } => pinned,
503 }
504 }
505 }
506}
507
508mod visibility {
509 use pin_project::pin_project;
510
511 #[pin_project(project_replace)]
512 pub(crate) struct S {
513 pub f: u8,
514 }
515}
516
517#[test]
518fn visibility() {
519 let mut x = visibility::S { f: 0 };
520 let x = Pin::new(&mut x);
521 let y = x.as_ref().project_ref();
522 let _: &u8 = y.f;
523 let y = x.project();
524 let _: &mut u8 = y.f;
525}
526
527#[test]
528fn trivial_bounds() {
529 #[pin_project(project_replace)]
530 pub struct NoGenerics {
531 #[pin]
532 f: PhantomPinned,
533 }
534
535 assert_not_unpin!(NoGenerics);
536}
537
538#[test]
539fn dst() {
540 #[pin_project]
541 struct Struct1<T: ?Sized> {
542 f: T,
543 }
544
545 let mut x = Struct1 { f: 0_u8 };
546 let x: Pin<&mut Struct1<dyn core::fmt::Debug>> = Pin::new(&mut x as _);
547 let _: &mut (dyn core::fmt::Debug) = x.project().f;
548
549 #[pin_project]
550 struct Struct2<T: ?Sized> {
551 #[pin]
552 f: T,
553 }
554
555 let mut x = Struct2 { f: 0_u8 };
556 let x: Pin<&mut Struct2<dyn core::fmt::Debug + Unpin>> = Pin::new(&mut x as _);
557 let _: Pin<&mut (dyn core::fmt::Debug + Unpin)> = x.project().f;
558
559 #[allow(explicit_outlives_requirements)] // https://github.com/rust-lang/rust/issues/60993
560 #[pin_project]
561 struct Struct3<T>
562 where
563 T: ?Sized,
564 {
565 f: T,
566 }
567
568 #[allow(explicit_outlives_requirements)] // https://github.com/rust-lang/rust/issues/60993
569 #[pin_project]
570 struct Struct4<T>
571 where
572 T: ?Sized,
573 {
574 #[pin]
575 f: T,
576 }
577
578 #[pin_project(UnsafeUnpin)]
579 struct Struct5<T: ?Sized> {
580 f: T,
581 }
582
583 #[pin_project(UnsafeUnpin)]
584 struct Struct6<T: ?Sized> {
585 #[pin]
586 f: T,
587 }
588
589 #[pin_project(PinnedDrop)]
590 struct Struct7<T: ?Sized> {
591 f: T,
592 }
593
594 #[pinned_drop]
595 impl<T: ?Sized> PinnedDrop for Struct7<T> {
596 fn drop(self: Pin<&mut Self>) {}
597 }
598
599 #[pin_project(PinnedDrop)]
600 struct Struct8<T: ?Sized> {
601 #[pin]
602 f: T,
603 }
604
605 #[pinned_drop]
606 impl<T: ?Sized> PinnedDrop for Struct8<T> {
607 fn drop(self: Pin<&mut Self>) {}
608 }
609
610 #[pin_project(!Unpin)]
611 struct Struct9<T: ?Sized> {
612 f: T,
613 }
614
615 #[pin_project(!Unpin)]
616 struct Struct10<T: ?Sized> {
617 #[pin]
618 f: T,
619 }
620
621 #[pin_project]
622 struct Struct11<'a, T: ?Sized, U: ?Sized> {
623 f1: &'a mut T,
624 f2: U,
625 }
626
627 #[pin_project]
628 struct TupleStruct1<T: ?Sized>(T);
629
630 #[pin_project]
631 struct TupleStruct2<T: ?Sized>(#[pin] T);
632
633 #[allow(explicit_outlives_requirements)] // https://github.com/rust-lang/rust/issues/60993
634 #[pin_project]
635 struct TupleStruct3<T>(T)
636 where
637 T: ?Sized;
638
639 #[allow(explicit_outlives_requirements)] // https://github.com/rust-lang/rust/issues/60993
640 #[pin_project]
641 struct TupleStruct4<T>(#[pin] T)
642 where
643 T: ?Sized;
644
645 #[pin_project(UnsafeUnpin)]
646 struct TupleStruct5<T: ?Sized>(T);
647
648 #[pin_project(UnsafeUnpin)]
649 struct TupleStruct6<T: ?Sized>(#[pin] T);
650
651 #[pin_project(PinnedDrop)]
652 struct TupleStruct7<T: ?Sized>(T);
653
654 #[pinned_drop]
655 impl<T: ?Sized> PinnedDrop for TupleStruct7<T> {
656 fn drop(self: Pin<&mut Self>) {}
657 }
658
659 #[pin_project(PinnedDrop)]
660 struct TupleStruct8<T: ?Sized>(#[pin] T);
661
662 #[pinned_drop]
663 impl<T: ?Sized> PinnedDrop for TupleStruct8<T> {
664 fn drop(self: Pin<&mut Self>) {}
665 }
666
667 #[pin_project(!Unpin)]
668 struct TupleStruct9<T: ?Sized>(T);
669
670 #[pin_project(!Unpin)]
671 struct TupleStruct10<T: ?Sized>(#[pin] T);
672
673 #[pin_project]
674 struct TupleStruct11<'a, T: ?Sized, U: ?Sized>(&'a mut T, U);
675}
676
677#[test]
678fn dyn_type() {
679 #[pin_project]
680 struct Struct1 {
681 f: dyn core::fmt::Debug,
682 }
683
684 #[pin_project]
685 struct Struct2 {
686 #[pin]
687 f: dyn core::fmt::Debug,
688 }
689
690 #[pin_project]
691 struct Struct3 {
692 f: dyn core::fmt::Debug + Send,
693 }
694
695 #[pin_project]
696 struct Struct4 {
697 #[pin]
698 f: dyn core::fmt::Debug + Send,
699 }
700
701 #[pin_project]
702 struct TupleStruct1(dyn core::fmt::Debug);
703
704 #[pin_project]
705 struct TupleStruct2(#[pin] dyn core::fmt::Debug);
706
707 #[pin_project]
708 struct TupleStruct3(dyn core::fmt::Debug + Send);
709
710 #[pin_project]
711 struct TupleStruct4(#[pin] dyn core::fmt::Debug + Send);
712}
713
714#[allow(clippy::trailing_empty_array)] // TODO: how do we handle this? Should propagate #[repr(...)] to ProjectionOwned?
715#[test]
716fn parse_self() {
717 macro_rules! mac {
718 ($($tt:tt)*) => {
719 $($tt)*
720 };
721 }
722
723 pub trait Trait {
724 type Assoc;
725 }
726
727 #[allow(clippy::type_repetition_in_bounds)]
728 #[pin_project(project_replace)]
729 pub struct Generics<T: Trait<Assoc = Self>>
730 where
731 Self: Trait<Assoc = Self>,
732 <Self as Trait>::Assoc: Sized,
733 mac!(Self): Trait<Assoc = mac!(Self)>,
734 {
735 _f: T,
736 }
737
738 impl<T: Trait<Assoc = Self>> Trait for Generics<T> {
739 type Assoc = Self;
740 }
741
742 #[pin_project(project_replace)]
743 pub struct Struct {
744 _f1: Box<Self>,
745 _f2: Box<<Self as Trait>::Assoc>,
746 _f3: Box<mac!(Self)>,
747 _f4: [(); Self::ASSOC],
748 _f5: [(); Self::assoc()],
749 _f6: [(); mac!(Self::assoc())],
750 }
751
752 impl Struct {
753 const ASSOC: usize = 1;
754 const fn assoc() -> usize {
755 0
756 }
757 }
758
759 impl Trait for Struct {
760 type Assoc = Self;
761 }
762
763 #[pin_project(project_replace)]
764 struct Tuple(
765 Box<Self>,
766 Box<<Self as Trait>::Assoc>,
767 Box<mac!(Self)>,
768 [(); Self::ASSOC],
769 [(); Self::assoc()],
770 [(); mac!(Self::assoc())],
771 );
772
773 impl Tuple {
774 const ASSOC: usize = 1;
775 const fn assoc() -> usize {
776 0
777 }
778 }
779
780 impl Trait for Tuple {
781 type Assoc = Self;
782 }
783
784 #[pin_project(project = EnumProj, project_ref = EnumProjRef, project_replace = EnumProjOwn)]
785 enum Enum {
786 Struct {
787 _f1: Box<Self>,
788 _f2: Box<<Self as Trait>::Assoc>,
789 _f3: Box<mac!(Self)>,
790 _f4: [(); Self::ASSOC],
791 _f5: [(); Self::assoc()],
792 _f6: [(); mac!(Self::assoc())],
793 },
794 Tuple(
795 Box<Self>,
796 Box<<Self as Trait>::Assoc>,
797 Box<mac!(Self)>,
798 [(); Self::ASSOC],
799 [(); Self::assoc()],
800 [(); mac!(Self::assoc())],
801 ),
802 }
803
804 impl Enum {
805 const ASSOC: usize = 1;
806 const fn assoc() -> usize {
807 0
808 }
809 }
810
811 impl Trait for Enum {
812 type Assoc = Self;
813 }
814}
815
816#[test]
817fn no_infer_outlives() {
818 trait Trait<X> {
819 type Y;
820 }
821
822 struct Struct1<A>(A);
823
824 impl<X, T> Trait<X> for Struct1<T> {
825 type Y = Option<T>;
826 }
827
828 #[pin_project(project_replace)]
829 struct Struct2<A, B> {
830 _f: <Struct1<A> as Trait<B>>::Y,
831 }
832}
833
834// https://github.com/rust-lang/rust/issues/47949
835// https://github.com/taiki-e/pin-project/pull/194#discussion_r419098111
836#[allow(clippy::many_single_char_names)]
837#[test]
838fn project_replace_panic() {
839 #[pin_project(project_replace)]
840 struct S<T, U> {
841 #[pin]
842 pinned: T,
843 unpinned: U,
844 }
845
846 struct D<'a>(&'a mut bool, bool);
847 impl Drop for D<'_> {
848 fn drop(&mut self) {
849 *self.0 = true;
850 if self.1 {
851 panic!();
852 }
853 }
854 }
855
856 let (mut a, mut b, mut c, mut d) = (false, false, false, false);
857 let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
858 let mut x = S { pinned: D(&mut a, true), unpinned: D(&mut b, false) };
859 let _y = Pin::new(&mut x)
860 .project_replace(S { pinned: D(&mut c, false), unpinned: D(&mut d, false) });
861 // Previous `x.pinned` was dropped and panicked when `project_replace` is
862 // called, so this is unreachable.
863 unreachable!();
864 }));
865 assert!(res.is_err());
866 assert!(a);
867 assert!(b);
868 assert!(c);
869 assert!(d);
870
871 let (mut a, mut b, mut c, mut d) = (false, false, false, false);
872 let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
873 let mut x = S { pinned: D(&mut a, false), unpinned: D(&mut b, true) };
874 {
875 let _y = Pin::new(&mut x)
876 .project_replace(S { pinned: D(&mut c, false), unpinned: D(&mut d, false) });
877 // `_y` (previous `x.unpinned`) live to the end of this scope, so
878 // this is not unreachable.
879 // unreachable!();
880 }
881 unreachable!();
882 }));
883 assert!(res.is_err());
884 assert!(a);
885 assert!(b);
886 assert!(c);
887 assert!(d);
888}
889