1 // Copyright 2019 The Fuchsia Authors
2 //
3 // Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4 // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6 // This file may not be copied, modified, or distributed except according to
7 // those terms.
8 
9 #![allow(warnings)]
10 
11 mod util;
12 
13 use std::{marker::PhantomData, mem::ManuallyDrop, option::IntoIter};
14 
15 use {static_assertions::assert_impl_all, zerocopy::AsBytes};
16 
17 use self::util::AU16;
18 
19 // A struct is `AsBytes` if:
20 // - all fields are `AsBytes`
21 // - `repr(C)` or `repr(transparent)` and
22 //   - no padding (size of struct equals sum of size of field types)
23 // - `repr(packed)`
24 
25 #[derive(AsBytes)]
26 #[repr(C)]
27 struct CZst;
28 
29 assert_impl_all!(CZst: AsBytes);
30 
31 #[derive(AsBytes)]
32 #[repr(C)]
33 struct C {
34     a: u8,
35     b: u8,
36     c: AU16,
37 }
38 
39 assert_impl_all!(C: AsBytes);
40 
41 #[derive(AsBytes)]
42 #[repr(transparent)]
43 struct Transparent {
44     a: u8,
45     b: CZst,
46 }
47 
48 assert_impl_all!(Transparent: AsBytes);
49 
50 #[derive(AsBytes)]
51 #[repr(transparent)]
52 struct TransparentGeneric<T: ?Sized> {
53     a: CZst,
54     b: T,
55 }
56 
57 assert_impl_all!(TransparentGeneric<u64>: AsBytes);
58 assert_impl_all!(TransparentGeneric<[u64]>: AsBytes);
59 
60 #[derive(AsBytes)]
61 #[repr(C, packed)]
62 struct CZstPacked;
63 
64 assert_impl_all!(CZstPacked: AsBytes);
65 
66 #[derive(AsBytes)]
67 #[repr(C, packed)]
68 struct CPacked {
69     a: u8,
70     // NOTE: The `u16` type is not guaranteed to have alignment 2, although it
71     // does on many platforms. However, to fix this would require a custom type
72     // with a `#[repr(align(2))]` attribute, and `#[repr(packed)]` types are not
73     // allowed to transitively contain `#[repr(align(...))]` types. Thus, we
74     // have no choice but to use `u16` here. Luckily, these tests run in CI on
75     // platforms on which `u16` has alignment 2, so this isn't that big of a
76     // deal.
77     b: u16,
78 }
79 
80 assert_impl_all!(CPacked: AsBytes);
81 
82 #[derive(AsBytes)]
83 #[repr(C, packed(2))]
84 // The same caveats as for CPacked apply - we're assuming u64 is at least
85 // 4-byte aligned by default. Without packed(2), this should fail, as there
86 // would be padding between a/b assuming u64 is 4+ byte aligned.
87 struct CPacked2 {
88     a: u16,
89     b: u64,
90 }
91 
92 assert_impl_all!(CPacked2: AsBytes);
93 
94 #[derive(AsBytes)]
95 #[repr(C, packed)]
96 struct CPackedGeneric<T, U: ?Sized> {
97     t: T,
98     // Unsized types stored in `repr(packed)` structs must not be dropped
99     // because dropping them in-place might be unsound depending on the
100     // alignment of the outer struct. Sized types can be dropped by first being
101     // moved to an aligned stack variable, but this isn't possible with unsized
102     // types.
103     u: ManuallyDrop<U>,
104 }
105 
106 assert_impl_all!(CPackedGeneric<u8, AU16>: AsBytes);
107 assert_impl_all!(CPackedGeneric<u8, [AU16]>: AsBytes);
108 
109 #[derive(AsBytes)]
110 #[repr(packed)]
111 struct Packed {
112     a: u8,
113     // NOTE: The `u16` type is not guaranteed to have alignment 2, although it
114     // does on many platforms. However, to fix this would require a custom type
115     // with a `#[repr(align(2))]` attribute, and `#[repr(packed)]` types are not
116     // allowed to transitively contain `#[repr(align(...))]` types. Thus, we
117     // have no choice but to use `u16` here. Luckily, these tests run in CI on
118     // platforms on which `u16` has alignment 2, so this isn't that big of a
119     // deal.
120     b: u16,
121 }
122 
123 assert_impl_all!(Packed: AsBytes);
124 
125 #[derive(AsBytes)]
126 #[repr(packed)]
127 struct PackedGeneric<T, U: ?Sized> {
128     t: T,
129     // Unsized types stored in `repr(packed)` structs must not be dropped
130     // because dropping them in-place might be unsound depending on the
131     // alignment of the outer struct. Sized types can be dropped by first being
132     // moved to an aligned stack variable, but this isn't possible with unsized
133     // types.
134     u: ManuallyDrop<U>,
135 }
136 
137 assert_impl_all!(PackedGeneric<u8, AU16>: AsBytes);
138 assert_impl_all!(PackedGeneric<u8, [AU16]>: AsBytes);
139 
140 #[derive(AsBytes)]
141 #[repr(transparent)]
142 struct Unsized {
143     a: [u8],
144 }
145 
146 assert_impl_all!(Unsized: AsBytes);
147 
148 // Deriving `AsBytes` should work if the struct has bounded parameters.
149 
150 #[derive(AsBytes)]
151 #[repr(transparent)]
152 struct WithParams<'a: 'b, 'b: 'a, const N: usize, T: 'a + 'b + AsBytes>(
153     [T; N],
154     PhantomData<&'a &'b ()>,
155 )
156 where
157     'a: 'b,
158     'b: 'a,
159     T: 'a + 'b + AsBytes;
160 
161 assert_impl_all!(WithParams<'static, 'static, 42, u8>: AsBytes);
162