Skip to content

Commit d406386

Browse files
committed
Add KnownLayout trait
In its initial form, the `KnownLayout` trait encodes type layout information slightly more complex than can be gleaned from any arbitrary `T: ?Sized`. This allows it to support not just sized and slice types, but also "custom DSTs" (those with fixed-size fields followed by a trailing slice type). This is the first step to supporting various operations on arbitrary custom DSTs. Makes progress on #29
1 parent 3773da2 commit d406386

File tree

2 files changed

+152
-11
lines changed

2 files changed

+152
-11
lines changed

src/lib.rs

Lines changed: 132 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,7 @@ use core::{
190190
#[cfg(feature = "alloc")]
191191
extern crate alloc;
192192
#[cfg(feature = "alloc")]
193-
use {
194-
alloc::boxed::Box,
195-
alloc::vec::Vec,
196-
core::{alloc::Layout, ptr::NonNull},
197-
};
193+
use {alloc::boxed::Box, alloc::vec::Vec, core::alloc::Layout};
198194

199195
// This is a hack to allow zerocopy-derive derives to work in this crate. They
200196
// assume that zerocopy is linked as an extern crate, so they access items from
@@ -204,6 +200,127 @@ mod zerocopy {
204200
pub(crate) use crate::*;
205201
}
206202

203+
/// The layout of a type which might be dynamically-sized.
204+
///
205+
/// `DstLayout` describes the layout of sized types, slice types, and "custom
206+
/// DSTs" - ie, those that are known by the type system to have a trailing slice
207+
/// (as distinguished from `dyn Trait` types - such types *might* have a
208+
/// trailing slice type, but the type system isn't aware of it).
209+
#[doc(hidden)]
210+
#[allow(missing_debug_implementations, missing_copy_implementations)]
211+
pub struct DstLayout {
212+
/// For sized types, `size_of::<T>()`. For DSTs, the size of the type when
213+
/// the trailing slice field contains 0 elements.
214+
_fixed_prefix_size: usize,
215+
/// The alignment of the type.
216+
_align: NonZeroUsize,
217+
/// For sized types, `None`. For DSTs, the size of the element type of the
218+
/// trailing slice.
219+
_trailing_slice_elem_size: Option<usize>,
220+
}
221+
222+
impl DstLayout {
223+
/// Constructs a `DstLayout` which describes `T`.
224+
///
225+
/// # Safety
226+
///
227+
/// Unsafe code may assume that `DstLayout` is the correct layout for `T`.
228+
const fn for_type<T>() -> DstLayout {
229+
DstLayout {
230+
_fixed_prefix_size: mem::size_of::<T>(),
231+
_align: match NonZeroUsize::new(mem::align_of::<T>()) {
232+
Some(align) => align,
233+
None => panic!("core::mem::align_of should never return 0"),
234+
},
235+
_trailing_slice_elem_size: None,
236+
}
237+
}
238+
239+
/// Constructs a `DstLayout` which describes `[T]`.
240+
///
241+
/// # Safety
242+
///
243+
/// Unsafe code may assume that `DstLayout` is the correct layout for `[T]`.
244+
const fn for_slice<T>() -> DstLayout {
245+
DstLayout {
246+
_fixed_prefix_size: 0,
247+
_align: match NonZeroUsize::new(mem::align_of::<T>()) {
248+
Some(align) => align,
249+
None => panic!("core::mem::align_of should never return 0"),
250+
},
251+
_trailing_slice_elem_size: Some(mem::size_of::<T>()),
252+
}
253+
}
254+
}
255+
256+
/// A trait which carries information about a type's layout that is used by the
257+
/// internals of this crate.
258+
///
259+
/// This trait is not meant for consumption by code outsie of this crate. While
260+
/// the normal semver stability guarantees apply with respect to which types
261+
/// implement this trait and which trait implementations are implied by this
262+
/// trait, no semver stability guarantees are made regarding its internals; they
263+
/// may change at any time, and code which makes use of them may break.
264+
///
265+
/// # Safety
266+
///
267+
/// This trait does not convey any safety guarantees to code outside this crate.
268+
#[doc(hidden)] // TODO: Remove this once KnownLayout is used by other APIs
269+
pub unsafe trait KnownLayout: sealed::KnownLayoutSealed {
270+
#[doc(hidden)]
271+
const LAYOUT: DstLayout;
272+
}
273+
274+
impl<T: KnownLayout> sealed::KnownLayoutSealed for [T] {}
275+
// SAFETY: Delegates safety to `DstLayout::for_slice`.
276+
unsafe impl<T: KnownLayout> KnownLayout for [T] {
277+
const LAYOUT: DstLayout = DstLayout::for_slice::<T>();
278+
}
279+
280+
/// Implements `KnownLayout` for a sized type.
281+
macro_rules! impl_known_layout {
282+
($(const $constvar:ident : $constty:ty, $tyvar:ident $(: ?$optbound:ident)? => $ty:ty),* $(,)?) => {
283+
$(impl_known_layout!(@inner const $constvar: $constty, $tyvar $(: ?$optbound)? => $ty);)*
284+
};
285+
($($tyvar:ident $(: ?$optbound:ident)? => $ty:ty),* $(,)?) => {
286+
$(impl_known_layout!(@inner , $tyvar $(: ?$optbound)? => $ty);)*
287+
};
288+
($($ty:ty),*) => { $(impl_known_layout!(@inner , => $ty);)* };
289+
(@inner $(const $constvar:ident : $constty:ty)? , $($tyvar:ident $(: ?$optbound:ident)?)? => $ty:ty) => {
290+
impl<$(const $constvar : $constty,)? $($tyvar $(: ?$optbound)?)?> sealed::KnownLayoutSealed for $ty {}
291+
// SAFETY: Delegates safety to `DstLayout::for_type`.
292+
unsafe impl<$(const $constvar : $constty,)? $($tyvar $(: ?$optbound)?)?> KnownLayout for $ty {
293+
const LAYOUT: DstLayout = DstLayout::for_type::<$ty>();
294+
}
295+
};
296+
}
297+
298+
#[rustfmt::skip]
299+
impl_known_layout!(
300+
(),
301+
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64,
302+
bool, char,
303+
NonZeroU8, NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32,
304+
NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize
305+
);
306+
#[rustfmt::skip]
307+
impl_known_layout!(
308+
T => Option<T>,
309+
T: ?Sized => PhantomData<T>,
310+
T => Wrapping<T>,
311+
T => MaybeUninit<T>,
312+
);
313+
impl_known_layout!(const N: usize, T => [T; N]);
314+
315+
safety_comment! {
316+
/// SAFETY:
317+
/// `str` and `ManuallyDrop<[T]>` have the same representations as `[u8]`
318+
/// and `[T]` repsectively. `str` has different bit validity than `[u8]`,
319+
/// but that doesn't affect the soundness of this impl.
320+
unsafe_impl_known_layout!(#[repr([u8])] str);
321+
unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] ManuallyDrop<T>);
322+
}
323+
207324
/// Types for which a sequence of bytes all set to zero represents a valid
208325
/// instance of the type.
209326
///
@@ -1171,6 +1288,7 @@ mod simd {
11711288
use core::arch::$arch::{$($typ),*};
11721289

11731290
use crate::*;
1291+
impl_known_layout!($($typ),*);
11741292
safety_comment! {
11751293
/// SAFETY:
11761294
/// See comment on module definition for justification.
@@ -2279,7 +2397,8 @@ where
22792397
}
22802398

22812399
mod sealed {
2282-
pub trait Sealed {}
2400+
pub trait ByteSliceSealed {}
2401+
pub trait KnownLayoutSealed {}
22832402
}
22842403

22852404
// ByteSlice and ByteSliceMut abstract over [u8] references (&[u8], &mut [u8],
@@ -2305,7 +2424,9 @@ mod sealed {
23052424
///
23062425
/// [`Vec<u8>`]: alloc::vec::Vec
23072426
/// [`split_at`]: crate::ByteSlice::split_at
2308-
pub unsafe trait ByteSlice: Deref<Target = [u8]> + Sized + self::sealed::Sealed {
2427+
pub unsafe trait ByteSlice:
2428+
Deref<Target = [u8]> + Sized + self::sealed::ByteSliceSealed
2429+
{
23092430
/// Gets a raw pointer to the first byte in the slice.
23102431
#[inline]
23112432
fn as_ptr(&self) -> *const u8 {
@@ -2336,7 +2457,7 @@ pub unsafe trait ByteSliceMut: ByteSlice + DerefMut {
23362457
}
23372458
}
23382459

2339-
impl<'a> sealed::Sealed for &'a [u8] {}
2460+
impl<'a> sealed::ByteSliceSealed for &'a [u8] {}
23402461
// TODO(#61): Add a "SAFETY" comment and remove this `allow`.
23412462
#[allow(clippy::undocumented_unsafe_blocks)]
23422463
unsafe impl<'a> ByteSlice for &'a [u8] {
@@ -2346,7 +2467,7 @@ unsafe impl<'a> ByteSlice for &'a [u8] {
23462467
}
23472468
}
23482469

2349-
impl<'a> sealed::Sealed for &'a mut [u8] {}
2470+
impl<'a> sealed::ByteSliceSealed for &'a mut [u8] {}
23502471
// TODO(#61): Add a "SAFETY" comment and remove this `allow`.
23512472
#[allow(clippy::undocumented_unsafe_blocks)]
23522473
unsafe impl<'a> ByteSlice for &'a mut [u8] {
@@ -2356,7 +2477,7 @@ unsafe impl<'a> ByteSlice for &'a mut [u8] {
23562477
}
23572478
}
23582479

2359-
impl<'a> sealed::Sealed for cell::Ref<'a, [u8]> {}
2480+
impl<'a> sealed::ByteSliceSealed for cell::Ref<'a, [u8]> {}
23602481
// TODO(#61): Add a "SAFETY" comment and remove this `allow`.
23612482
#[allow(clippy::undocumented_unsafe_blocks)]
23622483
unsafe impl<'a> ByteSlice for cell::Ref<'a, [u8]> {
@@ -2366,7 +2487,7 @@ unsafe impl<'a> ByteSlice for cell::Ref<'a, [u8]> {
23662487
}
23672488
}
23682489

2369-
impl<'a> sealed::Sealed for RefMut<'a, [u8]> {}
2490+
impl<'a> sealed::ByteSliceSealed for RefMut<'a, [u8]> {}
23702491
// TODO(#61): Add a "SAFETY" comment and remove this `allow`.
23712492
#[allow(clippy::undocumented_unsafe_blocks)]
23722493
unsafe impl<'a> ByteSlice for RefMut<'a, [u8]> {

src/macros.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,26 @@ macro_rules! impl_or_verify {
193193
};
194194
}
195195

196+
/// Implements `KnownLayout` for a type in terms of the implementation of
197+
/// another type with the same representation.
198+
///
199+
/// # Safety
200+
///
201+
/// - `$ty` and `$repr` must have the same:
202+
/// - Fixed prefix size
203+
/// - Alignment
204+
/// - (For DSTs) trailing slice element size
205+
/// - It must be valid to perform an `as` cast from `*mut $repr` to `*mut $ty`,
206+
/// and this operation must preserve referent size (ie, `size_of_val_raw`).
207+
macro_rules! unsafe_impl_known_layout {
208+
($($tyvar:ident: ?Sized + KnownLayout =>)? #[repr($repr:ty)] $ty:ty) => {
209+
impl<$($tyvar: ?Sized + KnownLayout)?> sealed::KnownLayoutSealed for $ty {}
210+
unsafe impl<$($tyvar: ?Sized + KnownLayout)?> KnownLayout for $ty {
211+
const LAYOUT: DstLayout = <$repr as KnownLayout>::LAYOUT;
212+
}
213+
};
214+
}
215+
196216
/// Uses `align_of` to confirm that a type or set of types have alignment 1.
197217
///
198218
/// Note that `align_of<T>` requires `T: Sized`, so this macro doesn't work for

0 commit comments

Comments
 (0)