Skip to content

Commit a1b7324

Browse files
jswrennjoshlf
andcommitted
[derive] Support derive(TryFromBytes) for structs
Supersedes #370. Makes progress on #5. Co-authored-by: Joshua Liebow-Feeser <[email protected]>
1 parent b2e872d commit a1b7324

27 files changed

+1333
-556
lines changed

src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ pub use crate::wrappers::*;
254254

255255
#[cfg(any(feature = "derive", test))]
256256
#[cfg_attr(doc_cfg, doc(cfg(feature = "derive")))]
257-
pub use zerocopy_derive::Unaligned;
257+
pub use zerocopy_derive::{TryFromBytes, Unaligned};
258258

259259
// `pub use` separately here so that we can mark it `#[doc(hidden)]`.
260260
//
@@ -1193,6 +1193,13 @@ pub unsafe trait NoCell {
11931193
// TODO(#5): Update `try_from_ref` doc link once it exists
11941194
#[doc(hidden)]
11951195
pub unsafe trait TryFromBytes {
1196+
// The `Self: Sized` bound makes it so that `TryFromBytes` is still object
1197+
// safe.
1198+
#[doc(hidden)]
1199+
fn only_derive_is_allowed_to_implement_this_trait()
1200+
where
1201+
Self: Sized;
1202+
11961203
/// Does a given memory range contain a valid instance of `Self`?
11971204
///
11981205
/// # Safety

src/macros.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ macro_rules! unsafe_impl {
133133
};
134134

135135
(@method TryFromBytes ; |$candidate:ident: &$repr:ty| $is_bit_valid:expr) => {
136+
#[allow(clippy::missing_inline_in_public_items)]
137+
fn only_derive_is_allowed_to_implement_this_trait() {}
138+
136139
#[inline]
137140
unsafe fn is_bit_valid(candidate: Ptr<'_, Self>) -> bool {
138141
// SAFETY:
@@ -160,6 +163,9 @@ macro_rules! unsafe_impl {
160163
}
161164
};
162165
(@method TryFromBytes ; |$candidate:ident: Ptr<$repr:ty>| $is_bit_valid:expr) => {
166+
#[allow(clippy::missing_inline_in_public_items)]
167+
fn only_derive_is_allowed_to_implement_this_trait() {}
168+
163169
#[inline]
164170
unsafe fn is_bit_valid(candidate: Ptr<'_, Self>) -> bool {
165171
// SAFETY:
@@ -174,7 +180,11 @@ macro_rules! unsafe_impl {
174180
$is_bit_valid
175181
}
176182
};
177-
(@method TryFromBytes) => { #[inline(always)] unsafe fn is_bit_valid(_: Ptr<'_, Self>) -> bool { true } };
183+
(@method TryFromBytes) => {
184+
#[allow(clippy::missing_inline_in_public_items)]
185+
fn only_derive_is_allowed_to_implement_this_trait() {}
186+
#[inline(always)] unsafe fn is_bit_valid(_: Ptr<'_, Self>) -> bool { true }
187+
};
178188
(@method $trait:ident) => {
179189
#[allow(clippy::missing_inline_in_public_items)]
180190
fn only_derive_is_allowed_to_implement_this_trait() {}

src/util.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,65 @@ pub(crate) mod ptr {
139139
// - `U: 'a`
140140
Ptr { ptr, _lifetime: PhantomData }
141141
}
142+
143+
/// Project a field from `self`.
144+
///
145+
/// # Safety
146+
///
147+
/// ## Preconditions
148+
///
149+
/// The caller promises that `projector` projects a well-aligned field
150+
/// of its argument. Its argument will be `self` casted to a raw
151+
/// pointer.
152+
///
153+
/// ## Postconditions
154+
///
155+
/// If the preconditions of this function are met, this function will
156+
/// return a `Ptr` the field projected from `self` by `projector`.
157+
#[doc(hidden)]
158+
#[inline]
159+
pub unsafe fn project<U: 'a + ?Sized>(
160+
self,
161+
projector: impl FnOnce(*mut T) -> *mut U,
162+
) -> Ptr<'a, U> {
163+
// SAFETY: `projector` is provided with `self` casted to a raw
164+
// pointer.
165+
let field = projector(self.ptr.as_ptr());
166+
167+
// SAFETY: The caller of `project` promises that `projector` is
168+
// provided with `self` casted to a raw pointer. By invariant,
169+
// `self` is non-null. By contract, `projector` is a field
170+
// projection of its argument. The field projected by `projector`
171+
// will, too, be non-null.
172+
let field = unsafe { NonNull::new_unchecked(field) };
173+
174+
// SAFETY: The safety invariants of `Ptr` (see definition) are
175+
// satisfied:
176+
// 1. `field` is derived from a valid Rust allocation, because
177+
// `self` is derived from a valid Rust allocation, by invariant
178+
// on `Ptr`, and `projector` (by contract) is a field projection
179+
// through `self`.
180+
// 2. `field` has the same provenance as `self`, because it derived
181+
// from `self` using a series of provenance-preserving
182+
// operations.
183+
// 3. `field` is entirely contained in the allocation of `self`,
184+
// because it is derived by `projector`, which is (by contract) a
185+
// field projection through `self`.
186+
// 4. `field` addresses a byte range whose length fits in an
187+
// `isize`, because it is a field projection through `self` and
188+
// thus is entirely contained by `self`, which satisfies this
189+
// invariant.
190+
// 5. `field` addresses a byte range which does not wrap around the
191+
// address space (see above).
192+
// 6. `field` is validly-aligned for `U`, by contract on
193+
// `projector`.
194+
// 7. The allocation of `field` is guaranteed to live for at least
195+
// `'a`, because `field` is entirely contained in `self`, which
196+
// lives for at least `'a` by invariant on `Ptr`.
197+
// 8. `U: 'a`, because `field` is an element within `T`, and `T: 'a`
198+
// by invariant on `Ptr`.
199+
Ptr { ptr: field, _lifetime: PhantomData }
200+
}
142201
}
143202

144203
impl<'a> Ptr<'a, [u8]> {
Lines changed: 64 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,65 @@
1+
error[E0277]: the trait bound `T: zerocopy::TryFromBytes` is not satisfied
2+
--> tests/ui-msrv/invalid-impls/../../../src/macros.rs
3+
|
4+
| impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {}
5+
| ^^^^^^^^ the trait `zerocopy::TryFromBytes` is not implemented for `T`
6+
|
7+
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:26:1
8+
|
9+
26 | impl_or_verify!(T => TryFromBytes for Foo<T>);
10+
| --------------------------------------------- in this macro invocation
11+
|
12+
note: required because of the requirements on the impl of `zerocopy::TryFromBytes` for `Foo<T>`
13+
--> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:10
14+
|
15+
22 | #[derive(TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned)]
16+
| ^^^^^^^^^^^^
17+
note: required by a bound in `_::Subtrait`
18+
--> tests/ui-msrv/invalid-impls/../../../src/macros.rs
19+
|
20+
| trait Subtrait: $trait {}
21+
| ^^^^^^ required by this bound in `_::Subtrait`
22+
|
23+
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:26:1
24+
|
25+
26 | impl_or_verify!(T => TryFromBytes for Foo<T>);
26+
| --------------------------------------------- in this macro invocation
27+
= note: this error originates in the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
28+
help: consider restricting type parameter `T`
29+
|
30+
26 | impl_or_verify!(T: zerocopy::TryFromBytes => TryFromBytes for Foo<T>);
31+
| ++++++++++++++++++++++++
32+
133
error[E0277]: the trait bound `T: zerocopy::FromZeroes` is not satisfied
234
--> tests/ui-msrv/invalid-impls/../../../src/macros.rs
335
|
436
| impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {}
537
| ^^^^^^^^ the trait `zerocopy::FromZeroes` is not implemented for `T`
638
|
7-
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:26:1
39+
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:27:1
840
|
9-
26 | impl_or_verify!(T => FromZeroes for Foo<T>);
41+
27 | impl_or_verify!(T => FromZeroes for Foo<T>);
1042
| ------------------------------------------- in this macro invocation
1143
|
1244
note: required because of the requirements on the impl of `zerocopy::FromZeroes` for `Foo<T>`
13-
--> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:10
45+
--> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:24
1446
|
15-
22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
16-
| ^^^^^^^^^^
47+
22 | #[derive(TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned)]
48+
| ^^^^^^^^^^
1749
note: required by a bound in `_::Subtrait`
1850
--> tests/ui-msrv/invalid-impls/../../../src/macros.rs
1951
|
2052
| trait Subtrait: $trait {}
2153
| ^^^^^^ required by this bound in `_::Subtrait`
2254
|
23-
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:26:1
55+
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:27:1
2456
|
25-
26 | impl_or_verify!(T => FromZeroes for Foo<T>);
57+
27 | impl_or_verify!(T => FromZeroes for Foo<T>);
2658
| ------------------------------------------- in this macro invocation
2759
= note: this error originates in the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
2860
help: consider restricting type parameter `T`
2961
|
30-
26 | impl_or_verify!(T: zerocopy::FromZeroes => FromZeroes for Foo<T>);
62+
27 | impl_or_verify!(T: zerocopy::FromZeroes => FromZeroes for Foo<T>);
3163
| ++++++++++++++++++++++
3264

3365
error[E0277]: the trait bound `T: zerocopy::FromBytes` is not satisfied
@@ -36,30 +68,30 @@ error[E0277]: the trait bound `T: zerocopy::FromBytes` is not satisfied
3668
| impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {}
3769
| ^^^^^^^^ the trait `zerocopy::FromBytes` is not implemented for `T`
3870
|
39-
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:27:1
71+
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:28:1
4072
|
41-
27 | impl_or_verify!(T => FromBytes for Foo<T>);
73+
28 | impl_or_verify!(T => FromBytes for Foo<T>);
4274
| ------------------------------------------ in this macro invocation
4375
|
4476
note: required because of the requirements on the impl of `zerocopy::FromBytes` for `Foo<T>`
45-
--> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:22
77+
--> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:36
4678
|
47-
22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
48-
| ^^^^^^^^^
79+
22 | #[derive(TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned)]
80+
| ^^^^^^^^^
4981
note: required by a bound in `_::Subtrait`
5082
--> tests/ui-msrv/invalid-impls/../../../src/macros.rs
5183
|
5284
| trait Subtrait: $trait {}
5385
| ^^^^^^ required by this bound in `_::Subtrait`
5486
|
55-
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:27:1
87+
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:28:1
5688
|
57-
27 | impl_or_verify!(T => FromBytes for Foo<T>);
89+
28 | impl_or_verify!(T => FromBytes for Foo<T>);
5890
| ------------------------------------------ in this macro invocation
5991
= note: this error originates in the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
6092
help: consider restricting type parameter `T`
6193
|
62-
27 | impl_or_verify!(T: zerocopy::FromBytes => FromBytes for Foo<T>);
94+
28 | impl_or_verify!(T: zerocopy::FromBytes => FromBytes for Foo<T>);
6395
| +++++++++++++++++++++
6496

6597
error[E0277]: the trait bound `T: zerocopy::AsBytes` is not satisfied
@@ -68,30 +100,30 @@ error[E0277]: the trait bound `T: zerocopy::AsBytes` is not satisfied
68100
| impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {}
69101
| ^^^^^^^^ the trait `zerocopy::AsBytes` is not implemented for `T`
70102
|
71-
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:28:1
103+
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:29:1
72104
|
73-
28 | impl_or_verify!(T => AsBytes for Foo<T>);
105+
29 | impl_or_verify!(T => AsBytes for Foo<T>);
74106
| ---------------------------------------- in this macro invocation
75107
|
76108
note: required because of the requirements on the impl of `zerocopy::AsBytes` for `Foo<T>`
77-
--> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:33
109+
--> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:47
78110
|
79-
22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
80-
| ^^^^^^^
111+
22 | #[derive(TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned)]
112+
| ^^^^^^^
81113
note: required by a bound in `_::Subtrait`
82114
--> tests/ui-msrv/invalid-impls/../../../src/macros.rs
83115
|
84116
| trait Subtrait: $trait {}
85117
| ^^^^^^ required by this bound in `_::Subtrait`
86118
|
87-
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:28:1
119+
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:29:1
88120
|
89-
28 | impl_or_verify!(T => AsBytes for Foo<T>);
121+
29 | impl_or_verify!(T => AsBytes for Foo<T>);
90122
| ---------------------------------------- in this macro invocation
91123
= note: this error originates in the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
92124
help: consider restricting type parameter `T`
93125
|
94-
28 | impl_or_verify!(T: zerocopy::AsBytes => AsBytes for Foo<T>);
126+
29 | impl_or_verify!(T: zerocopy::AsBytes => AsBytes for Foo<T>);
95127
| +++++++++++++++++++
96128

97129
error[E0277]: the trait bound `T: zerocopy::Unaligned` is not satisfied
@@ -100,28 +132,28 @@ error[E0277]: the trait bound `T: zerocopy::Unaligned` is not satisfied
100132
| impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {}
101133
| ^^^^^^^^ the trait `zerocopy::Unaligned` is not implemented for `T`
102134
|
103-
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:29:1
135+
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:30:1
104136
|
105-
29 | impl_or_verify!(T => Unaligned for Foo<T>);
137+
30 | impl_or_verify!(T => Unaligned for Foo<T>);
106138
| ------------------------------------------ in this macro invocation
107139
|
108140
note: required because of the requirements on the impl of `zerocopy::Unaligned` for `Foo<T>`
109-
--> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:42
141+
--> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:56
110142
|
111-
22 | #[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
112-
| ^^^^^^^^^
143+
22 | #[derive(TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned)]
144+
| ^^^^^^^^^
113145
note: required by a bound in `_::Subtrait`
114146
--> tests/ui-msrv/invalid-impls/../../../src/macros.rs
115147
|
116148
| trait Subtrait: $trait {}
117149
| ^^^^^^ required by this bound in `_::Subtrait`
118150
|
119-
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:29:1
151+
::: tests/ui-msrv/invalid-impls/invalid-impls.rs:30:1
120152
|
121-
29 | impl_or_verify!(T => Unaligned for Foo<T>);
153+
30 | impl_or_verify!(T => Unaligned for Foo<T>);
122154
| ------------------------------------------ in this macro invocation
123155
= note: this error originates in the macro `impl_or_verify` (in Nightly builds, run with -Z macro-backtrace for more info)
124156
help: consider restricting type parameter `T`
125157
|
126-
29 | impl_or_verify!(T: zerocopy::Unaligned => Unaligned for Foo<T>);
158+
30 | impl_or_verify!(T: zerocopy::Unaligned => Unaligned for Foo<T>);
127159
| +++++++++++++++++++++

tests/ui-nightly/invalid-impls/invalid-impls.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ use zerocopy_derive::*;
1919

2020
fn main() {}
2121

22-
#[derive(FromZeroes, FromBytes, AsBytes, Unaligned)]
22+
#[derive(TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned)]
2323
#[repr(transparent)]
2424
struct Foo<T>(T);
2525

26+
impl_or_verify!(T => TryFromBytes for Foo<T>);
2627
impl_or_verify!(T => FromZeroes for Foo<T>);
2728
impl_or_verify!(T => FromBytes for Foo<T>);
2829
impl_or_verify!(T => AsBytes for Foo<T>);

0 commit comments

Comments
 (0)