1 #![warn(rust_2018_idioms, single_use_lifetimes)]
2 #![deny(safe_packed_borrows)]
3 
4 use std::cell::Cell;
5 
6 // Ensure that the compiler doesn't copy the fields
7 // of #[repr(packed)] types during drop, if the field has alignment 1
8 // (that is, any reference to the field is guaranteed to have proper alignment)
9 // We are currently unable to statically prevent the usage of #[pin_project]
10 // on #[repr(packed)] types composed entirely of fields of alignment 1.
11 // This shouldn't lead to undefined behavior, as long as the compiler doesn't
12 // try to move the field anyway during drop.
13 //
14 // This tests validates that the compiler is doing what we expect.
15 #[test]
weird_repr_packed()16 fn weird_repr_packed() {
17     // We keep track of the field address during
18     // drop using a thread local, to avoid changing
19     // the layout of our #[repr(packed)] type.
20     thread_local! {
21         static FIELD_ADDR: Cell<usize> = Cell::new(0);
22     }
23 
24     #[repr(packed)]
25     struct Foo {
26         field: u8,
27     }
28 
29     impl Drop for Foo {
30         fn drop(&mut self) {
31             FIELD_ADDR.with(|f| {
32                 f.set(&self.field as *const u8 as usize);
33             })
34         }
35     }
36 
37     #[allow(clippy::let_and_return)]
38     let field_addr = {
39         // We let this field drop by going out of scope,
40         // rather than explicitly calling drop(foo).
41         // Calling drop(foo) causes 'foo' to be moved
42         // into the 'drop' function, resulting in a different
43         // address.
44         let x = Foo { field: 27 };
45         let field_addr = &x.field as *const u8 as usize;
46         field_addr
47     };
48     assert_eq!(field_addr, FIELD_ADDR.with(|f| f.get()));
49 }
50