Skip to content

Commit bb9b06d

Browse files
joshlfjswrenn
andcommitted
Teach transmute_{ref,mut}! to handle slice DSTs
This requires us to generalize our prior support for transmuting between unsized types. In particular, we previously used the `SizeEq` trait to denote that two types have equal sizes in the face of a cast operation (in particular, that `*const T as *const U` preserves referent size). In this commit, we add support for metadata fix-up, which means that we support casts for which `*const T as *const U` does *not* preserve referent size. Instead, we compute an affine function at compile time and apply it at runtime - computing the destination type's metadata as a function of the source metadata, `dst_meta = A + src_meta * B`. `A` and `B` are computed at compile time. We generalize `SizeEq` to permit its `cast_from_raw` method to perform a runtime metadata fix-up operation. Makes progress on #1817 Co-authored-by: Jack Wrenn <[email protected]> gherrit-pr-id: Ib4bc62202e0b3b09d155333b525087f7aa8f02c2
1 parent 7fdec6f commit bb9b06d

File tree

156 files changed

+1457
-5007
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

156 files changed

+1457
-5007
lines changed

src/doctests.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright 2025 The Fuchsia Authors
2+
//
3+
// Licensed under the 2-Clause BSD License <LICENSE-BSD or
4+
// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0
5+
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
6+
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
7+
// This file may not be copied, modified, or distributed except according to
8+
// those terms.
9+
10+
#![cfg(feature = "derive")] // Required for derives on `SliceDst`
11+
#![allow(dead_code)]
12+
13+
//! Our UI test framework, built on the `trybuild` crate, does not support
14+
//! testing for post-monomorphization errors. Instead, we use doctests, which
15+
//! are able to test for post-monomorphization errors.
16+
17+
use crate::*;
18+
19+
#[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
20+
#[repr(C)]
21+
#[allow(missing_debug_implementations, missing_copy_implementations)]
22+
pub struct SliceDst<T, U> {
23+
pub t: T,
24+
pub u: [U],
25+
}
26+
27+
#[allow(clippy::must_use_candidate, clippy::missing_inline_in_public_items, clippy::todo)]
28+
impl<T: FromBytes + IntoBytes, U: FromBytes + IntoBytes> SliceDst<T, U> {
29+
pub fn new() -> &'static SliceDst<T, U> {
30+
todo!()
31+
}
32+
33+
pub fn new_mut() -> &'static mut SliceDst<T, U> {
34+
todo!()
35+
}
36+
}
37+
38+
/// `transmute_ref!` does not support transmuting from a type of smaller
39+
/// alignment to one of larger alignment.
40+
///
41+
/// ```compile_fail,E0080
42+
/// let increase_alignment: &u16 = zerocopy::transmute_ref!(&[0u8; 2]);
43+
/// ```
44+
///
45+
/// ```compile_fail,E0080
46+
/// let mut src = [0u8; 2];
47+
/// let increase_alignment: &mut u16 = zerocopy::transmute_mut!(&mut src);
48+
/// ```
49+
enum TransmuteRefMutAlignmentIncrease {}
50+
51+
/// We require that the size of the destination type is not larger than the size
52+
/// of the source type.
53+
///
54+
/// ```compile_fail,E0080
55+
/// let increase_size: &[u8; 2] = zerocopy::transmute_ref!(&0u8);
56+
/// ```
57+
///
58+
/// ```compile_fail,E0080
59+
/// let mut src = 0u8;
60+
/// let increase_size: &mut [u8; 2] = zerocopy::transmute_mut!(&mut src);
61+
/// ```
62+
enum TransmuteRefMutSizeIncrease {}
63+
64+
/// We require that the size of the destination type is not smaller than the
65+
/// size of the source type.
66+
///
67+
/// ```compile_fail,E0080
68+
/// let decrease_size: &u8 = zerocopy::transmute_ref!(&[0u8; 2]);
69+
/// ```
70+
///
71+
/// ```compile_fail,E0080
72+
/// let mut src = [0u8; 2];
73+
/// let decrease_size: &mut u8 = zerocopy::transmute_mut!(&mut src);
74+
/// ```
75+
enum TransmuteRefMutSizeDecrease {}
76+
77+
/// It's not possible in the general case to increase the trailing slice offset
78+
/// during a reference transmutation - some pointer metadata values would not be
79+
/// supportable, and so such a transmutation would be fallible.
80+
///
81+
/// ```compile_fail,E0080
82+
/// use zerocopy::doctests::SliceDst;
83+
/// let src: &SliceDst<u8, u8> = SliceDst::new();
84+
/// let increase_offset: &SliceDst<[u8; 2], u8> = zerocopy::transmute_ref!(src);
85+
/// ```
86+
///
87+
/// ```compile_fail,E0080
88+
/// use zerocopy::doctests::SliceDst;
89+
/// let src: &mut SliceDst<u8, u8> = SliceDst::new_mut();
90+
/// let increase_offset: &mut SliceDst<[u8; 2], u8> = zerocopy::transmute_mut!(src);
91+
/// ```
92+
enum TransmuteRefMutDstOffsetIncrease {}
93+
94+
/// Reference transmutes are not possible when the difference between the source
95+
/// and destination types' trailing slice offsets is not a multiple of the
96+
/// destination type's trailing slice element size.
97+
///
98+
/// ```compile_fail,E0080
99+
/// use zerocopy::doctests::SliceDst;
100+
/// let src: &SliceDst<[u8; 3], [u8; 2]> = SliceDst::new();
101+
/// let _: &SliceDst<[u8; 2], [u8; 2]> = zerocopy::transmute_ref!(src);
102+
/// ```
103+
///
104+
/// ```compile_fail,E0080
105+
/// use zerocopy::doctests::SliceDst;
106+
/// let src: &mut SliceDst<[u8; 3], [u8; 2]> = SliceDst::new_mut();
107+
/// let _: &mut SliceDst<[u8; 2], [u8; 2]> = zerocopy::transmute_mut!(src);
108+
/// ```
109+
enum TransmuteRefMutDstOffsetNotMultiple {}
110+
111+
/// Reference transmutes are not possible when the source's trailing slice
112+
/// element size is not a multiple of the destination's.
113+
///
114+
/// ```compile_fail,E0080
115+
/// use zerocopy::doctests::SliceDst;
116+
/// let src: &SliceDst<(), [u8; 3]> = SliceDst::new();
117+
/// let _: &SliceDst<(), [u8; 2]> = zerocopy::transmute_ref!(src);
118+
/// ```
119+
///
120+
/// ```compile_fail,E0080
121+
/// use zerocopy::doctests::SliceDst;
122+
/// let src: &mut SliceDst<(), [u8; 3]> = SliceDst::new_mut();
123+
/// let _: &mut SliceDst<(), [u8; 2]> = zerocopy::transmute_mut!(src);
124+
/// ```
125+
enum TransmuteRefMutDstElemSizeNotMultiple {}

src/impls.rs

Lines changed: 24 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -179,38 +179,13 @@ safety_comment! {
179179
});
180180
}
181181

182-
// SAFETY: `str` and `[u8]` have the same layout [1].
183-
//
184-
// [1] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#str-layout:
185-
//
186-
// String slices are a UTF-8 representation of characters that have the same
187-
// layout as slices of type `[u8]`.
188-
unsafe impl pointer::SizeEq<str> for [u8] {
189-
fn cast_from_raw(s: NonNull<str>) -> NonNull<[u8]> {
190-
cast!(s)
191-
}
192-
}
193-
// SAFETY: See previous safety comment.
194-
unsafe impl pointer::SizeEq<[u8]> for str {
195-
fn cast_from_raw(bytes: NonNull<[u8]>) -> NonNull<str> {
196-
cast!(bytes)
197-
}
198-
}
182+
impl_size_eq!(str, [u8]);
199183

200184
macro_rules! unsafe_impl_try_from_bytes_for_nonzero {
201185
($($nonzero:ident[$prim:ty]),*) => {
202186
$(
203187
unsafe_impl!(=> TryFromBytes for $nonzero; |n| {
204-
unsafe impl pointer::SizeEq<$nonzero> for Unalign<$prim> {
205-
fn cast_from_raw(n: NonNull<$nonzero>) -> NonNull<Unalign<$prim>> {
206-
cast!(n)
207-
}
208-
}
209-
unsafe impl pointer::SizeEq<Unalign<$prim>> for $nonzero {
210-
fn cast_from_raw(p: NonNull<Unalign<$prim>>) -> NonNull<$nonzero> {
211-
cast!(p)
212-
}
213-
}
188+
impl_size_eq!($nonzero, Unalign<$prim>);
214189

215190
let n = n.transmute::<Unalign<$prim>, invariant::Valid, _>();
216191
$nonzero::new(n.read_unaligned().into_inner()).is_some()
@@ -429,8 +404,8 @@ mod atomics {
429404
macro_rules! unsafe_impl_transmute_from_for_atomic {
430405
($($($tyvar:ident)? => $atomic:ty [$prim:ty]),*) => {
431406
const _: () = {
432-
use core::{cell::UnsafeCell, ptr::NonNull};
433-
use crate::pointer::{TransmuteFrom, SizeEq, invariant::Valid};
407+
use core::cell::UnsafeCell;
408+
use crate::pointer::{PtrInner, SizeEq, TransmuteFrom, invariant::Valid};
434409

435410
$(
436411
#[allow(unused_unsafe)] // Force the caller to call this macro inside `safety_comment!`.
@@ -443,18 +418,23 @@ mod atomics {
443418
// the same size and bit validity.
444419
unsafe impl<$($tyvar)?> TransmuteFrom<$prim, Valid, Valid> for $atomic {}
445420

446-
// SAFETY: THe caller promised that `$atomic` and `$prim`
421+
// SAFETY: The caller promised that `$atomic` and `$prim`
447422
// have the same size.
448423
unsafe impl<$($tyvar)?> SizeEq<$atomic> for $prim {
449-
fn cast_from_raw(a: NonNull<$atomic>) -> NonNull<$prim> {
450-
cast!(a)
424+
#[inline]
425+
fn cast_from_raw(a: PtrInner<'_, $atomic>) -> PtrInner<'_, $prim> {
426+
// SAFETY: The caller promised that `$atomic` and `$prim`
427+
// have the same size. Thus, this cast preserves
428+
// address, referent size, and provenance.
429+
unsafe { cast!(a) }
451430
}
452431
}
453-
// SAFETY: THe caller promised that `$atomic` and `$prim`
454-
// have the same size.
432+
// SAFETY: See previous safety comment.
455433
unsafe impl<$($tyvar)?> SizeEq<$prim> for $atomic {
456-
fn cast_from_raw(p: NonNull<$prim>) -> NonNull<$atomic> {
457-
cast!(p)
434+
#[inline]
435+
fn cast_from_raw(p: PtrInner<'_, $prim>) -> PtrInner<'_, $atomic> {
436+
// SAFETY: See previous safety comment.
437+
unsafe { cast!(p) }
458438
}
459439
}
460440
// SAFETY: The caller promised that `$atomic` and `$prim`
@@ -467,14 +447,18 @@ mod atomics {
467447
// its inner type `T`. A consequence of this guarantee is that
468448
// it is possible to convert between `T` and `UnsafeCell<T>`.
469449
unsafe impl<$($tyvar)?> SizeEq<$atomic> for UnsafeCell<$prim> {
470-
fn cast_from_raw(a: NonNull<$atomic>) -> NonNull<UnsafeCell<$prim>> {
471-
cast!(a)
450+
#[inline]
451+
fn cast_from_raw(a: PtrInner<'_, $atomic>) -> PtrInner<'_, UnsafeCell<$prim>> {
452+
// SAFETY: See previous safety comment.
453+
unsafe { cast!(a) }
472454
}
473455
}
474456
// SAFETY: See previous safety comment.
475457
unsafe impl<$($tyvar)?> SizeEq<UnsafeCell<$prim>> for $atomic {
476-
fn cast_from_raw(p: NonNull<UnsafeCell<$prim>>) -> NonNull<$atomic> {
477-
cast!(p)
458+
#[inline]
459+
fn cast_from_raw(p: PtrInner<'_, UnsafeCell<$prim>>) -> PtrInner<'_, $atomic> {
460+
// SAFETY: See previous safety comment.
461+
unsafe { cast!(p) }
478462
}
479463
}
480464

0 commit comments

Comments
 (0)