Skip to content

Commit 52922b4

Browse files
committed
Add MaybeUninit type
The standard library's `MaybeUninit` type does not currently support wrapping unsized types. This commit introduces a polyfill with the same behavior as `MaybeUninit` which does support wrapping unsized types. In this commit, the only supported types are sized types and slice types. Later (as part of #29), we will add the ability to derive the `AsMaybeUninit` trait, which will extend support to custom DSTs. Makes progress on #29
1 parent 62f76d2 commit 52922b4

File tree

2 files changed

+288
-13
lines changed

2 files changed

+288
-13
lines changed

src/lib.rs

+286-7
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ use core::{
173173
fmt::{self, Debug, Display, Formatter},
174174
hash::{Hash, Hasher},
175175
marker::PhantomData,
176-
mem::{self, ManuallyDrop, MaybeUninit},
176+
mem::{self, ManuallyDrop},
177177
num::{
178178
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
179179
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
@@ -1004,17 +1004,16 @@ safety_comment! {
10041004
/// - `Unaligned`: `MaybeUninit<T>` is guaranteed by its documentation [1]
10051005
/// to have the same alignment as `T`.
10061006
///
1007-
/// [1]
1008-
/// https://doc.rust-lang.org/nightly/core/mem/union.MaybeUninit.html#layout-1
1007+
/// [1] https://doc.rust-lang.org/nightly/core/mem/union.MaybeUninit.html#layout-1
10091008
///
10101009
/// TODO(https://github.com/google/zerocopy/issues/251): If we split
10111010
/// `FromBytes` and `RefFromBytes`, or if we introduce a separate
10121011
/// `NoCell`/`Freeze` trait, we can relax the trait bounds for `FromZeroes`
10131012
/// and `FromBytes`.
1014-
unsafe_impl!(T: FromZeroes => FromZeroes for MaybeUninit<T>);
1015-
unsafe_impl!(T: FromBytes => FromBytes for MaybeUninit<T>);
1016-
unsafe_impl!(T: Unaligned => Unaligned for MaybeUninit<T>);
1017-
assert_unaligned!(MaybeUninit<()>, MaybeUninit<u8>);
1013+
unsafe_impl!(T: FromZeroes => FromZeroes for mem::MaybeUninit<T>);
1014+
unsafe_impl!(T: FromBytes => FromBytes for mem::MaybeUninit<T>);
1015+
unsafe_impl!(T: Unaligned => Unaligned for mem::MaybeUninit<T>);
1016+
assert_unaligned!(mem::MaybeUninit<()>, mem::MaybeUninit<u8>);
10181017
}
10191018
safety_comment! {
10201019
/// SAFETY:
@@ -1201,6 +1200,279 @@ mod simd {
12011200
simd_arch_mod!(arm, int8x4_t, uint8x4_t);
12021201
}
12031202

1203+
/// An alternative to the standard library's [`MaybeUninit`] that supports
1204+
/// unsized types.
1205+
///
1206+
/// `MaybeUninit<T>` is identical to the standard library's `MaybeUninit` type
1207+
/// with the exception that it supports wrapping unsized types. Namely,
1208+
/// `MaybeUninit<T>` has the same layout as `T`, but it has no bit validity
1209+
/// constraints - any byte of a `MaybeUninit<T>` may have any value, including
1210+
/// uninitialized.
1211+
///
1212+
/// [`MaybeUninit`]: core::mem::MaybeUninit
1213+
#[derive(Copy, Clone)]
1214+
#[repr(transparent)]
1215+
pub struct MaybeUninit<T: AsMaybeUninit + ?Sized> {
1216+
inner: T::MaybeUninit,
1217+
}
1218+
1219+
impl<T: AsMaybeUninit + ?Sized> Debug for MaybeUninit<T> {
1220+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1221+
f.pad(core::any::type_name::<Self>())
1222+
}
1223+
}
1224+
1225+
impl<T: AsMaybeUninit + ?Sized> MaybeUninit<T> {
1226+
/// Gets a shared reference to the contained value.
1227+
///
1228+
/// # Safety
1229+
///
1230+
/// Calling this when the content is not yet fully initialized causes
1231+
/// undefined behavior. It is up to the caller to guarantee that `self` is
1232+
/// really in an initialized state.
1233+
pub unsafe fn assume_init_ref(&self) -> &T {
1234+
let ptr = T::raw_from_maybe_uninit(&self.inner);
1235+
// SAFETY: The caller has promised that `self` contains an initialized
1236+
// `T`. Since `Self` is `repr(transparent)`, it has the same layout as
1237+
// `T::MaybeUninit`, which in turn is guaranteed (by safety invariant)
1238+
// to have the same layout as `T`. Thus, it is sound to treat `ptr` as
1239+
// pointing to a valid `T` of the correct size and alignment.
1240+
unsafe { &*ptr }
1241+
}
1242+
1243+
/// Gets a mutable reference to the contained value.
1244+
///
1245+
/// # Safety
1246+
///
1247+
/// Calling this when the content is not yet fully initialized causes
1248+
/// undefined behavior. It is up to the caller to guarantee that `self` is
1249+
/// really in an initialized state.
1250+
pub unsafe fn assume_init_mut(&mut self) -> &mut T {
1251+
let ptr = T::raw_mut_from_maybe_uninit(&mut self.inner);
1252+
// SAFETY: The caller has promised that `self` contains an initialized
1253+
// `T`. Since `Self` is `repr(transparent)`, it has the same layout as
1254+
// `T::MaybeUninit`, which in turn is guaranteed (by safety invariant)
1255+
// to have the same layout as `T`. Thus, it is sound to treat `ptr` as
1256+
// pointing to a valid `T` of the correct size and alignment.
1257+
unsafe { &mut *ptr }
1258+
}
1259+
}
1260+
1261+
impl<T: Sized> MaybeUninit<T> {
1262+
/// Creates a new `MaybeUninit<T>` in an uninitialized state.
1263+
pub const fn uninit() -> MaybeUninit<T> {
1264+
MaybeUninit { inner: mem::MaybeUninit::uninit() }
1265+
}
1266+
1267+
/// Extracts the value from the `MaybeUninit<T>` container.
1268+
///
1269+
/// # Safety
1270+
///
1271+
/// `assume_init` has the same safety requirements and guarantees as the
1272+
/// standard library's [`MaybeUninit::assume_init`] method.
1273+
///
1274+
/// [`MaybeUninit::assume_init`]: mem::MaybeUninit::assume_init
1275+
pub const unsafe fn assume_init(self) -> T {
1276+
// SAFETY: The caller has promised to uphold the safety invariants of
1277+
// the exact function we're calling here. Since, for `T: Sized`,
1278+
// `MaybeUninit<T>` is a `repr(transparent)` wrapper around
1279+
// `mem::MaybeUninit<T>`, it is sound to treat `Self` as equivalent to a
1280+
// `mem::MaybeUninit<T>` for the purposes of
1281+
// `mem::MaybeUninit::assume_init`'s safety invariants.
1282+
unsafe { self.inner.assume_init() }
1283+
}
1284+
}
1285+
1286+
/// A type which can be wrapped in [`MaybeUninit`].
1287+
///
1288+
/// # Safety
1289+
///
1290+
/// The safety invariants on the associated `MaybeUninit` type and on all
1291+
/// methods must be upheld.
1292+
pub unsafe trait AsMaybeUninit {
1293+
/// A type which has the same layout as `Self`, but which has no validity
1294+
/// constraints.
1295+
///
1296+
/// Roughly speaking, this type is equivalent to what the standard library's
1297+
/// [`MaybeUninit<Self>`] would be if it supported unsized types.
1298+
///
1299+
/// # Safety
1300+
///
1301+
/// For `T: AsMaybeUninit`, the following must hold:
1302+
/// - Given `m: T::MaybeUninit`, it is sound to write any byte value,
1303+
/// including an uninitialized byte, at any byte offset in `m`
1304+
/// - `T` and `T::MaybeUninit` have the same alignment requirement
1305+
/// - It is valid to use an `as` cast to convert a `t: *const T` to a `m:
1306+
/// *const T::MaybeUninit` and vice-versa (and likewise for `*mut T`/`*mut
1307+
/// T::MaybeUninit`). Regardless of which direction the conversion was
1308+
/// performed, the sizes of the pointers' referents are always equal (in
1309+
/// terms of an API which is not yet stable, `size_of_val_raw(t) ==
1310+
/// size_of_val_raw(m)`).
1311+
/// - `T::MaybeUninit` contains [`UnsafeCell`]s at exactly the same byte
1312+
/// ranges that `T` does.
1313+
///
1314+
/// [`MaybeUninit<Self>`]: core::mem::MaybeUninit
1315+
/// [`UnsafeCell`]: core::cell::UnsafeCell
1316+
type MaybeUninit: ?Sized;
1317+
1318+
/// Converts a const pointer at the type level.
1319+
///
1320+
/// # Safety
1321+
///
1322+
/// Callers may assume that the memory region addressed by the return value
1323+
/// is the same as that addressed by the argument, and that both the return
1324+
/// value and the argument have the same provenance.
1325+
fn raw_from_maybe_uninit(maybe_uninit: *const Self::MaybeUninit) -> *const Self;
1326+
1327+
/// Converts a mut pointer at the type level.
1328+
///
1329+
/// # Safety
1330+
///
1331+
/// Callers may assume that the memory region addressed by the return value
1332+
/// is the same as that addressed by the argument, and that both the return
1333+
/// value and the argument have the same provenance.
1334+
fn raw_mut_from_maybe_uninit(maybe_uninit: *mut Self::MaybeUninit) -> *mut Self;
1335+
}
1336+
1337+
// SAFETY: See inline safety comments.
1338+
unsafe impl<T: Sized> AsMaybeUninit for T {
1339+
// SAFETY:
1340+
// - `MaybeUninit` has no validity requirements, so it is sound to write any
1341+
// byte value, including an uninitialized byte, at any offset.
1342+
// - `MaybeUninit<T>` has the same layout as `T`, so they have the same
1343+
// alignment requirement. For the same reason, their sizes are equal.
1344+
// - Since their sizes are equal, raw pointers to both types are thin
1345+
// pointers, and thus can be converted using as casts. For the same
1346+
// reason, the sizes of these pointers' referents are always equal.
1347+
// - `MaybeUninit<T>` has the same field offsets as `T`, and so it contains
1348+
// `UnsafeCell`s at exactly the same byte ranges as `T`.
1349+
type MaybeUninit = mem::MaybeUninit<T>;
1350+
1351+
// SAFETY: `.cast` preserves pointer address and provenance.
1352+
fn raw_from_maybe_uninit(maybe_uninit: *const mem::MaybeUninit<T>) -> *const T {
1353+
maybe_uninit.cast::<T>()
1354+
}
1355+
1356+
// SAFETY: `.cast` preserves pointer address and provenance.
1357+
fn raw_mut_from_maybe_uninit(maybe_uninit: *mut mem::MaybeUninit<T>) -> *mut T {
1358+
maybe_uninit.cast::<T>()
1359+
}
1360+
}
1361+
1362+
// SAFETY: See inline safety comments.
1363+
unsafe impl<T: Sized> AsMaybeUninit for [T] {
1364+
// SAFETY:
1365+
// - `MaybeUninit` has no bit validity requirements and `[U]` has the same
1366+
// bit validity requirements as `U`, so `[MaybeUninit<T>]` has no bit
1367+
// validity requirements. Thus, it is sound to write any byte value,
1368+
// including an uninitialized byte, at any byte offset.
1369+
// - Since `MaybeUninit<T>` has the same layout as `T`, and `[U]` has the
1370+
// same alignment as `U`, `[MaybeUninit<T>]` has the same alignment as
1371+
// `[T]`.
1372+
// - `[T]` and `[MaybeUninit<T>]` are both slice types, and so pointers can
1373+
// be converted using an `as` cast. Since `T` and `MaybeUninit<T>` have
1374+
// the same size, and since such a cast preserves the number of elements
1375+
// in the slice, the referent slices themselves will have the same size.
1376+
// - `MaybeUninit<T>` has the same field offsets as `[T]`, and so it
1377+
// contains `UnsafeCell`s at exactly the same byte ranges as `[T]`.
1378+
type MaybeUninit = [mem::MaybeUninit<T>];
1379+
1380+
// SAFETY: `as` preserves pointer address and provenance.
1381+
#[allow(clippy::as_conversions)]
1382+
fn raw_from_maybe_uninit(maybe_uninit: *const [mem::MaybeUninit<T>]) -> *const [T] {
1383+
maybe_uninit as *const [T]
1384+
}
1385+
1386+
// SAFETY: `as` preserves pointer address and provenance.
1387+
#[allow(clippy::as_conversions)]
1388+
fn raw_mut_from_maybe_uninit(maybe_uninit: *mut [mem::MaybeUninit<T>]) -> *mut [T] {
1389+
maybe_uninit as *mut [T]
1390+
}
1391+
}
1392+
1393+
// SAFETY: See inline safety comments.
1394+
unsafe impl AsMaybeUninit for str {
1395+
// SAFETY: `str` has the same layout as `[u8]`. Thus, the same safety
1396+
// argument for `<[u8] as AsMaybeUninit>::MaybeUninit` applies here.
1397+
type MaybeUninit = <[u8] as AsMaybeUninit>::MaybeUninit;
1398+
1399+
// SAFETY: `as` preserves pointer address and provenance.
1400+
#[allow(clippy::as_conversions)]
1401+
fn raw_from_maybe_uninit(
1402+
maybe_uninit: *const <[u8] as AsMaybeUninit>::MaybeUninit,
1403+
) -> *const str {
1404+
maybe_uninit as *const str
1405+
}
1406+
1407+
// SAFETY: `as` preserves pointer address and provenance.
1408+
#[allow(clippy::as_conversions)]
1409+
fn raw_mut_from_maybe_uninit(
1410+
maybe_uninit: *mut <[u8] as AsMaybeUninit>::MaybeUninit,
1411+
) -> *mut str {
1412+
maybe_uninit as *mut str
1413+
}
1414+
}
1415+
1416+
// SAFETY: See inline safety comments.
1417+
unsafe impl<T: Sized> AsMaybeUninit for MaybeUninit<[T]> {
1418+
// SAFETY: `MaybeUninit<[T]>` is a `repr(transparent)` wrapper around
1419+
// `[T::MaybeUninit]`. Thus:
1420+
// - Given `m: Self::MaybeUninit = [T::MaybeUninit]`, it is sound to write
1421+
// any byte value, including an uninitialized byte, at any byte offset in
1422+
// `m` because that is already required of `T::MaybeUninit`, and thus of
1423+
// [`T::MaybeUninit`]
1424+
// - `Self` and `[T::MaybeUninit]` have the same representation, and so:
1425+
// - Alignments are equal
1426+
// - Pointer casts are valid, and sizes of referents of both pointer types
1427+
// are equal.
1428+
// - `Self::MaybeUninit = [T::MaybeUninit]` contains `UnsafeCell`s at
1429+
// exactly the same byte ranges that `Self` does because `Self` has the
1430+
// same bit validity as `[T::MaybeUninit]`.
1431+
type MaybeUninit = [<T as AsMaybeUninit>::MaybeUninit];
1432+
1433+
// SAFETY: `as` preserves pointer address and provenance.
1434+
#[allow(clippy::as_conversions)]
1435+
fn raw_from_maybe_uninit(
1436+
maybe_uninit: *const [<T as AsMaybeUninit>::MaybeUninit],
1437+
) -> *const MaybeUninit<[T]> {
1438+
maybe_uninit as *const MaybeUninit<[T]>
1439+
}
1440+
1441+
// SAFETY: `as` preserves pointer address and provenance.
1442+
#[allow(clippy::as_conversions)]
1443+
fn raw_mut_from_maybe_uninit(
1444+
maybe_uninit: *mut [<T as AsMaybeUninit>::MaybeUninit],
1445+
) -> *mut MaybeUninit<[T]> {
1446+
maybe_uninit as *mut MaybeUninit<[T]>
1447+
}
1448+
}
1449+
1450+
safety_comment! {
1451+
// `MaybeUninit<T>` is `FromZeroes` and `FromBytes`, but never `AsBytes`
1452+
// since it may contain uninitialized bytes.
1453+
//
1454+
/// SAFETY:
1455+
/// - `FromZeroes`, `FromBytes`: `MaybeUninit<T>` has no restrictions on its
1456+
/// contents. Unfortunately, in addition to bit validity, `FromZeroes` and
1457+
/// `FromBytes` also require that implementers contain no `UnsafeCell`s.
1458+
/// Thus, we require `T: FromZeroes` and `T: FromBytes` in order to ensure
1459+
/// that `T` - and thus `MaybeUninit<T>` - contains to `UnsafeCell`s.
1460+
/// Thus, requiring that `T` implement each of these traits is sufficient
1461+
/// - `Unaligned`: `MaybeUninit<T>` is guaranteed by its documentation [1]
1462+
/// to have the same alignment as `T`.
1463+
///
1464+
/// [1] https://doc.rust-lang.org/nightly/core/mem/union.MaybeUninit.html#layout-1
1465+
///
1466+
/// TODO(https://github.com/google/zerocopy/issues/251): If we split
1467+
/// `FromBytes` and `RefFromBytes`, or if we introduce a separate
1468+
/// `NoCell`/`Freeze` trait, we can relax the trait bounds for `FromZeroes`
1469+
/// and `FromBytes`.
1470+
unsafe_impl!(T: ?Sized + AsMaybeUninit + FromZeroes => FromZeroes for MaybeUninit<T>);
1471+
unsafe_impl!(T: ?Sized + AsMaybeUninit + FromBytes => FromBytes for MaybeUninit<T>);
1472+
unsafe_impl!(T: ?Sized + AsMaybeUninit + Unaligned => Unaligned for MaybeUninit<T>);
1473+
assert_unaligned!(mem::MaybeUninit<()>, MaybeUninit<u8>);
1474+
}
1475+
12041476
/// A type with no alignment requirement.
12051477
///
12061478
/// An `Unalign` wraps a `T`, removing any alignment requirement. `Unalign<T>`
@@ -4068,8 +4340,15 @@ mod tests {
40684340
assert_impls!(ManuallyDrop<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
40694341
assert_impls!(ManuallyDrop<[NotZerocopy]>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
40704342

4343+
assert_impls!(mem::MaybeUninit<u8>: FromZeroes, FromBytes, Unaligned, !AsBytes);
4344+
assert_impls!(mem::MaybeUninit<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
4345+
40714346
assert_impls!(MaybeUninit<u8>: FromZeroes, FromBytes, Unaligned, !AsBytes);
4347+
assert_impls!(MaybeUninit<MaybeUninit<u8>>: FromZeroes, FromBytes, Unaligned, !AsBytes);
4348+
assert_impls!(MaybeUninit<[u8]>: FromZeroes, FromBytes, Unaligned, !AsBytes);
4349+
assert_impls!(MaybeUninit<MaybeUninit<[u8]>>: FromZeroes, FromBytes, Unaligned, !AsBytes);
40724350
assert_impls!(MaybeUninit<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
4351+
assert_impls!(MaybeUninit<MaybeUninit<NotZerocopy>>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);
40734352

40744353
assert_impls!(Wrapping<u8>: FromZeroes, FromBytes, AsBytes, Unaligned);
40754354
assert_impls!(Wrapping<NotZerocopy>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned);

src/macros.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,13 @@ macro_rules! unsafe_impl {
3939
($tyvar:ident => $trait:ident for $ty:ty) => {
4040
unsafe impl<$tyvar> $trait for $ty { fn only_derive_is_allowed_to_implement_this_trait() {} }
4141
};
42-
// For all `$tyvar: ?Sized` with no bounds, implement `$trait` for `$ty`.
43-
($tyvar:ident: ?Sized => $trait:ident for $ty:ty) => {
44-
unsafe impl<$tyvar: ?Sized> $trait for $ty { fn only_derive_is_allowed_to_implement_this_trait() {} }
45-
};
4642
// For all `$tyvar: $bound`, implement `$trait` for `$ty`.
4743
($tyvar:ident: $bound:path => $trait:ident for $ty:ty) => {
4844
unsafe impl<$tyvar: $bound> $trait for $ty { fn only_derive_is_allowed_to_implement_this_trait() {} }
4945
};
5046
// For all `$tyvar: $bound + ?Sized`, implement `$trait` for `$ty`.
51-
($tyvar:ident: ?Sized + $bound:path => $trait:ident for $ty:ty) => {
52-
unsafe impl<$tyvar: ?Sized + $bound> $trait for $ty { fn only_derive_is_allowed_to_implement_this_trait() {} }
47+
($tyvar:ident: ?Sized $(+ $bounds:path)* => $trait:ident for $ty:ty) => {
48+
unsafe impl<$tyvar: ?Sized $(+ $bounds)*> $trait for $ty { fn only_derive_is_allowed_to_implement_this_trait() {} }
5349
};
5450
// For all `$tyvar: $bound` and for all `const $constvar: $constty`,
5551
// implement `$trait` for `$ty`.

0 commit comments

Comments
 (0)