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