Skip to content

Commit f60767f

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 62ee07e commit f60767f

27 files changed

+1520
-343
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: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ pub(crate) mod ptr {
110110
/// than or equal to the size of the object referenced by `self`.
111111
/// - The alignment of `U` is less than or equal to the alignment of
112112
/// `T`.
113-
pub(crate) unsafe fn cast_unsized<U: 'a + ?Sized, F: FnOnce(*mut T) -> *mut U>(
113+
#[doc(hidden)]
114+
#[inline]
115+
pub unsafe fn cast_unsized<U: 'a + ?Sized, F: FnOnce(*mut T) -> *mut U>(
114116
self,
115117
cast: F,
116118
) -> Ptr<'a, U> {
@@ -139,6 +141,72 @@ pub(crate) mod ptr {
139141
// - `U: 'a`
140142
Ptr { ptr, _lifetime: PhantomData }
141143
}
144+
145+
/// Projects a field from `self`.
146+
///
147+
/// # Safety
148+
///
149+
/// ## Preconditions
150+
///
151+
/// The caller promises that `projector` projects a well-aligned field
152+
/// of its argument. Its argument will be `self` casted to a raw
153+
/// pointer.
154+
///
155+
/// ## Postconditions
156+
///
157+
/// If the preconditions of this function are met, this function will
158+
/// return a `Ptr` to the field projected from `self` by `projector`.
159+
#[doc(hidden)]
160+
#[inline]
161+
pub unsafe fn project<U: 'a + ?Sized>(
162+
self,
163+
projector: impl FnOnce(*mut T) -> *mut U,
164+
) -> Ptr<'a, U> {
165+
// SAFETY: `projector` is provided with `self` casted to a raw
166+
// pointer.
167+
let field = projector(self.ptr.as_ptr());
168+
169+
// SAFETY: We promise that `projector` is provided with `self`
170+
// casted to a raw pointer, and the caller promises that `projector`
171+
// is a field projection of its argument. By invariant on `Ptr`,
172+
// `self` is non-null, and by contract on `projector`, so too will
173+
// its return value.
174+
//
175+
// Note that field projection cannot wrap around the address space
176+
// to null.
177+
//
178+
// TODO(https://github.com/rust-lang/rust/pull/116675): Cite
179+
// documentation that allocated objects do not wrap around the
180+
// address space.
181+
let field = unsafe { NonNull::new_unchecked(field) };
182+
183+
// SAFETY: The safety invariants of `Ptr` (see definition) are
184+
// satisfied:
185+
// 1. `field` is derived from a valid Rust allocation, because
186+
// `self` is derived from a valid Rust allocation, by invariant
187+
// on `Ptr`, and `projector` (by contract) is a field projection
188+
// through `self`.
189+
// 2. `field` has the same provenance as `self`, because it derived
190+
// from `self` using a series of provenance-preserving
191+
// operations.
192+
// 3. `field` is entirely contained in the allocation of `self`,
193+
// because it is derived by `projector`, which is (by contract) a
194+
// field projection through `self`.
195+
// 4. `field` addresses a byte range whose length fits in an
196+
// `isize`, because it is a field projection through `self` and
197+
// thus is entirely contained by `self`, which satisfies this
198+
// invariant.
199+
// 5. `field` addresses a byte range which does not wrap around the
200+
// address space (see above).
201+
// 6. `field` is validly-aligned for `U`, by contract on
202+
// `projector`.
203+
// 7. The allocation of `field` is guaranteed to live for at least
204+
// `'a`, because `field` is entirely contained in `self`, which
205+
// lives for at least `'a` by invariant on `Ptr`.
206+
// 8. `U: 'a`, because `field` is an element within `T`, and `T: 'a`
207+
// by invariant on `Ptr`.
208+
Ptr { ptr: field, _lifetime: PhantomData }
209+
}
142210
}
143211

144212
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, FromZeros, 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: 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 `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 => FromZeros for Foo<T>);
41+
27 | impl_or_verify!(T => FromZeros for Foo<T>);
1042
| ------------------------------------------ in this macro invocation
1143
|
1244
note: required because of the requirements on the impl of `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(FromZeros, FromBytes, AsBytes, Unaligned)]
16-
| ^^^^^^^^^
47+
22 | #[derive(TryFromBytes, FromZeros, 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 => FromZeros for Foo<T>);
57+
27 | impl_or_verify!(T => FromZeros 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::FromZeros => FromZeros for Foo<T>);
62+
27 | impl_or_verify!(T: zerocopy::FromZeros => FromZeros 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:21
77+
--> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:35
4678
|
47-
22 | #[derive(FromZeros, FromBytes, AsBytes, Unaligned)]
48-
| ^^^^^^^^^
79+
22 | #[derive(TryFromBytes, FromZeros, 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:32
109+
--> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:46
78110
|
79-
22 | #[derive(FromZeros, FromBytes, AsBytes, Unaligned)]
80-
| ^^^^^^^
111+
22 | #[derive(TryFromBytes, FromZeros, 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:41
141+
--> tests/ui-msrv/invalid-impls/invalid-impls.rs:22:55
110142
|
111-
22 | #[derive(FromZeros, FromBytes, AsBytes, Unaligned)]
112-
| ^^^^^^^^^
143+
22 | #[derive(TryFromBytes, FromZeros, 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(FromZeros, FromBytes, AsBytes, Unaligned)]
22+
#[derive(TryFromBytes, FromZeros, 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 => FromZeros 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)