Skip to content

Commit

Permalink
[pointer][transmute] Migrate from util
Browse files Browse the repository at this point in the history
This prepares us to rename `TransparentWrapper` to `TransmuteFrom`,
which we'll do in a follow-up commit to preserve nice git diffs.

Makes progress on #1122

gherrit-pr-id: Ifc49755af0d90eeefe7822d755d508403c266bda
  • Loading branch information
joshlf committed Oct 24, 2024
1 parent a407be2 commit 995e1f0
Show file tree
Hide file tree
Showing 5 changed files with 469 additions and 458 deletions.
1 change: 1 addition & 0 deletions src/pointer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod inner;
#[doc(hidden)]
pub mod invariant;
mod ptr;
pub(crate) mod transmute;

#[doc(hidden)]
pub use invariant::{BecauseExclusive, BecauseImmutable, Read};
Expand Down
4 changes: 3 additions & 1 deletion src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ mod _external {
/// Methods for converting to and from `Ptr` and Rust's safe reference types.
mod _conversions {
use super::*;
use crate::util::{AlignmentVariance, Covariant, TransparentWrapper, ValidityVariance};
use crate::pointer::transmute::{
AlignmentVariance, Covariant, TransparentWrapper, ValidityVariance,
};

/// `&'a T` → `Ptr<'a, T>`
impl<'a, T> Ptr<'a, T, (Shared, Aligned, Valid)>
Expand Down
359 changes: 359 additions & 0 deletions src/pointer/transmute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,359 @@
// Copyright 2024 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.

use core::{
cell::UnsafeCell,
mem::{ManuallyDrop, MaybeUninit},
num::Wrapping,
};

use crate::{
pointer::invariant::{self, Invariants},
Unalign,
};

/// A type which has the same layout as the type it wraps.
///
/// # Safety
///
/// `T: TransparentWrapper` implies that `T` has the same size as [`T::Inner`].
/// Further, `T: TransparentWrapper<I>` implies that:
/// - If `T::UnsafeCellVariance = Covariant`, then `T` has `UnsafeCell`s
/// covering the same byte ranges as `T::Inner`.
/// - If a `T` pointer satisfies the alignment invariant `I::Alignment`, then
/// that same pointer, cast to `T::Inner`, satisfies the alignment invariant
/// `<T::AlignmentVariance as AlignmentVariance<I::Alignment>>::Applied`.
/// - If a `T` pointer satisfies the validity invariant `I::Validity`, then that
/// same pointer, cast to `T::Inner`, satisfies the validity invariant
/// `<T::ValidityVariance as ValidityVariance<I::Validity>>::Applied`.
///
/// [`T::Inner`]: TransparentWrapper::Inner
/// [`UnsafeCell`]: core::cell::UnsafeCell
/// [`T::AlignmentVariance`]: TransparentWrapper::AlignmentVariance
/// [`T::ValidityVariance`]: TransparentWrapper::ValidityVariance
#[doc(hidden)]
pub unsafe trait TransparentWrapper<I: Invariants> {
type Inner: ?Sized;

type UnsafeCellVariance;
type AlignmentVariance: AlignmentVariance<I::Alignment>;
type ValidityVariance: ValidityVariance<I::Validity>;

/// Casts a wrapper pointer to an inner pointer.
///
/// # Safety
///
/// The resulting pointer has the same address and provenance as `ptr`, and
/// addresses the same number of bytes.
fn cast_into_inner(ptr: *mut Self) -> *mut Self::Inner;

/// Casts an inner pointer to a wrapper pointer.
///
/// # Safety
///
/// The resulting pointer has the same address and provenance as `ptr`, and
/// addresses the same number of bytes.
fn cast_from_inner(ptr: *mut Self::Inner) -> *mut Self;
}

#[allow(unreachable_pub)]
#[doc(hidden)]
pub trait AlignmentVariance<I: invariant::Alignment> {
type Applied: invariant::Alignment;
}

#[allow(unreachable_pub)]
#[doc(hidden)]
pub trait ValidityVariance<I: invariant::Validity> {
type Applied: invariant::Validity;
}

#[doc(hidden)]
#[allow(missing_copy_implementations, missing_debug_implementations)]
pub enum Covariant {}

impl<I: invariant::Alignment> AlignmentVariance<I> for Covariant {
type Applied = I;
}

impl<I: invariant::Validity> ValidityVariance<I> for Covariant {
type Applied = I;
}

#[doc(hidden)]
#[allow(missing_copy_implementations, missing_debug_implementations)]
pub enum Invariant {}

impl<I: invariant::Alignment> AlignmentVariance<I> for Invariant {
type Applied = invariant::Unknown;
}

impl<I: invariant::Validity> ValidityVariance<I> for Invariant {
type Applied = invariant::Unknown;
}

// SAFETY:
// - Per [1], `MaybeUninit<T>` has the same size as `T`.
// - See inline comments for other safety justifications.
//
// [1] Per https://doc.rust-lang.org/1.81.0/std/mem/union.MaybeUninit.html#layout-1:
//
// `MaybeUninit<T>` is guaranteed to have the same size, alignment, and ABI as
// `T`
unsafe impl<T, I: Invariants> TransparentWrapper<I> for MaybeUninit<T> {
type Inner = T;

// SAFETY: `MaybeUninit<T>` has `UnsafeCell`s covering the same byte ranges
// as `Inner = T`. This is not explicitly documented, but it can be
// inferred. Per [1] in the preceding safety comment, `MaybeUninit<T>` has
// the same size as `T`. Further, note the signature of
// `MaybeUninit::assume_init_ref` [2]:
//
// pub unsafe fn assume_init_ref(&self) -> &T
//
// If the argument `&MaybeUninit<T>` and the returned `&T` had `UnsafeCell`s
// at different offsets, this would be unsound. Its existence is proof that
// this is not the case.
//
// [2] https://doc.rust-lang.org/1.81.0/std/mem/union.MaybeUninit.html#method.assume_init_ref
type UnsafeCellVariance = Covariant;
// SAFETY: Per [1], `MaybeUninit<T>` has the same layout as `T`, and thus
// has the same alignment as `T`.
//
// [1] Per https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#layout-1:
//
// `MaybeUninit<T>` is guaranteed to have the same size, alignment, and
// ABI as `T`.
type AlignmentVariance = Covariant;
// SAFETY: `MaybeUninit` has no validity invariants. Thus, a valid
// `MaybeUninit<T>` is not necessarily a valid `T`.
type ValidityVariance = Invariant;

#[inline(always)]
fn cast_into_inner(ptr: *mut MaybeUninit<T>) -> *mut T {
// SAFETY: Per [1] (from comment above), `MaybeUninit<T>` has the same
// layout as `T`. Thus, this cast preserves size.
//
// This cast trivially preserves provenance.
ptr.cast::<T>()
}

#[inline(always)]
fn cast_from_inner(ptr: *mut T) -> *mut MaybeUninit<T> {
// SAFETY: Per [1] (from comment above), `MaybeUninit<T>` has the same
// layout as `T`. Thus, this cast preserves size.
//
// This cast trivially preserves provenance.
ptr.cast::<MaybeUninit<T>>()
}
}

// SAFETY:
// - Per [1], `ManuallyDrop<T>` has the same size as `T`.
// - See inline comments for other safety justifications.
//
// [1] Per https://doc.rust-lang.org/1.81.0/std/mem/struct.ManuallyDrop.html:
//
// `ManuallyDrop<T>` is guaranteed to have the same layout and bit validity as
// `T`
unsafe impl<T: ?Sized, I: Invariants> TransparentWrapper<I> for ManuallyDrop<T> {
type Inner = T;

// SAFETY: Per [1], `ManuallyDrop<T>` has `UnsafeCell`s covering the same
// byte ranges as `Inner = T`.
//
// [1] Per https://doc.rust-lang.org/1.81.0/std/mem/struct.ManuallyDrop.html:
//
// `ManuallyDrop<T>` is guaranteed to have the same layout and bit
// validity as `T`, and is subject to the same layout optimizations as
// `T`. As a consequence, it has no effect on the assumptions that the
// compiler makes about its contents.
type UnsafeCellVariance = Covariant;
// SAFETY: Per [1], `ManuallyDrop<T>` has the same layout as `T`, and thus
// has the same alignment as `T`.
//
// [1] Per https://doc.rust-lang.org/nightly/core/mem/struct.ManuallyDrop.html:
//
// `ManuallyDrop<T>` is guaranteed to have the same layout and bit
// validity as `T`
type AlignmentVariance = Covariant;

// SAFETY: Per [1] (from comment above), `ManuallyDrop<T>` has the same bit
// validity as `T`.
type ValidityVariance = Covariant;

#[inline(always)]
fn cast_into_inner(ptr: *mut ManuallyDrop<T>) -> *mut T {
// SAFETY: Per [1] (from comment above), `ManuallyDrop<T>` has the same
// layout as `T`. Thus, this cast preserves size even if `T` is unsized.
//
// This cast trivially preserves provenance.
#[allow(clippy::as_conversions)]
return ptr as *mut T;
}

#[inline(always)]
fn cast_from_inner(ptr: *mut T) -> *mut ManuallyDrop<T> {
// SAFETY: Per [1] (from comment above), `ManuallyDrop<T>` has the same
// layout as `T`. Thus, this cast preserves size even if `T` is unsized.
//
// This cast trivially preserves provenance.
#[allow(clippy::as_conversions)]
return ptr as *mut ManuallyDrop<T>;
}
}

// SAFETY:
// - Per [1], `Wrapping<T>` has the same size as `T`.
// - See inline comments for other safety justifications.
//
// [1] Per https://doc.rust-lang.org/1.81.0/std/num/struct.Wrapping.html#layout-1:
//
// `Wrapping<T>` is guaranteed to have the same layout and ABI as `T`.
unsafe impl<T, I: Invariants> TransparentWrapper<I> for Wrapping<T> {
type Inner = T;

// SAFETY: Per [1], `Wrapping<T>` has the same layout as `T`. Since its
// single field (of type `T`) is public, it would be a breaking change to
// add or remove fields. Thus, we know that `Wrapping<T>` contains a `T` (as
// opposed to just having the same size and alignment as `T`) with no pre-
// or post-padding. Thus, `Wrapping<T>` must have `UnsafeCell`s covering the
// same byte ranges as `Inner = T`.
//
// [1] Per https://doc.rust-lang.org/1.81.0/std/num/struct.Wrapping.html#layout-1:
//
// `Wrapping<T>` is guaranteed to have the same layout and ABI as `T`.
type UnsafeCellVariance = Covariant;
// SAFETY: Per [1], `Wrapping<T>` has the same layout as `T`, and thus has
// the same alignment as `T`.
//
// [1] Per https://doc.rust-lang.org/core/num/struct.Wrapping.html#layout-1:
//
// `Wrapping<T>` is guaranteed to have the same layout and ABI as `T`.
type AlignmentVariance = Covariant;

// SAFETY: `Wrapping<T>` has only one field, which is `pub` [2]. We are also
// guaranteed per [1] (from the comment above) that `Wrapping<T>` has the
// same layout as `T`. The only way for both of these to be true
// simultaneously is for `Wrapping<T>` to have the same bit validity as `T`.
// In particular, in order to change the bit validity, one of the following
// would need to happen:
// - `Wrapping` could change its `repr`, but this would violate the layout
// guarantee.
// - `Wrapping` could add or change its fields, but this would be a
// stability-breaking change.
//
// [2] https://doc.rust-lang.org/core/num/struct.Wrapping.html
type ValidityVariance = Covariant;

#[inline(always)]
fn cast_into_inner(ptr: *mut Wrapping<T>) -> *mut T {
// SAFETY: Per [1] (from comment above), `Wrapping<T>` has the same
// layout as `T`. Thus, this cast preserves size.
//
// This cast trivially preserves provenance.
ptr.cast::<T>()
}

#[inline(always)]
fn cast_from_inner(ptr: *mut T) -> *mut Wrapping<T> {
// SAFETY: Per [1] (from comment above), `Wrapping<T>` has the same
// layout as `T`. Thus, this cast preserves size.
//
// This cast trivially preserves provenance.
ptr.cast::<Wrapping<T>>()
}
}

// SAFETY:
// - Per [1], `UnsafeCell<T>` has the same size as `T`.
// - See inline comments for other safety justifications.
//
// [1] Per https://doc.rust-lang.org/1.81.0/core/cell/struct.UnsafeCell.html#memory-layout:
//
// `UnsafeCell<T>` has the same in-memory representation as its inner type
// `T`.
unsafe impl<T: ?Sized, I: Invariants> TransparentWrapper<I> for UnsafeCell<T> {
type Inner = T;

// SAFETY: Since we set this to `Invariant`, we make no safety claims.
type UnsafeCellVariance = Invariant;

// SAFETY: Per [1] (from comment on impl), `Unalign<T>` has the same
// representation as `T`, and thus has the same alignment as `T`.
type AlignmentVariance = Covariant;

// SAFETY: Per [1], `Unalign<T>` has the same bit validity as `T`.
// Technically the term "representation" doesn't guarantee this, but the
// subsequent sentence in the documentation makes it clear that this is the
// intention.
//
// [1] Per https://doc.rust-lang.org/1.81.0/core/cell/struct.UnsafeCell.html#memory-layout:
//
// `UnsafeCell<T>` has the same in-memory representation as its inner type
// `T`. A consequence of this guarantee is that it is possible to convert
// between `T` and `UnsafeCell<T>`.
type ValidityVariance = Covariant;

#[inline(always)]
fn cast_into_inner(ptr: *mut UnsafeCell<T>) -> *mut T {
// SAFETY: Per [1] (from comment above), `UnsafeCell<T>` has the same
// representation as `T`. Thus, this cast preserves size.
//
// This cast trivially preserves provenance.
#[allow(clippy::as_conversions)]
return ptr as *mut T;
}

#[inline(always)]
fn cast_from_inner(ptr: *mut T) -> *mut UnsafeCell<T> {
// SAFETY: Per [1] (from comment above), `UnsafeCell<T>` has the same
// representation as `T`. Thus, this cast preserves size.
//
// This cast trivially preserves provenance.
#[allow(clippy::as_conversions)]
return ptr as *mut UnsafeCell<T>;
}
}

// SAFETY: `Unalign<T>` promises to have the same size as `T`.
//
// See inline comments for other safety justifications.
unsafe impl<T, I: Invariants> TransparentWrapper<I> for Unalign<T> {
type Inner = T;

// SAFETY: `Unalign<T>` promises to have `UnsafeCell`s covering the same
// byte ranges as `Inner = T`.
type UnsafeCellVariance = Covariant;

// SAFETY: Since `Unalign<T>` promises to have alignment 1 regardless of
// `T`'s alignment. Thus, an aligned pointer to `Unalign<T>` is not
// necessarily an aligned pointer to `T`.
type AlignmentVariance = Invariant;

// SAFETY: `Unalign<T>` promises to have the same validity as `T`.
type ValidityVariance = Covariant;

#[inline(always)]
fn cast_into_inner(ptr: *mut Unalign<T>) -> *mut T {
// SAFETY: Per the safety comment on the impl block, `Unalign<T>` has
// the size as `T`. Thus, this cast preserves size.
//
// This cast trivially preserves provenance.
ptr.cast::<T>()
}

#[inline(always)]
fn cast_from_inner(ptr: *mut T) -> *mut Unalign<T> {
// SAFETY: Per the safety comment on the impl block, `Unalign<T>` has
// the size as `T`. Thus, this cast preserves size.
//
// This cast trivially preserves provenance.
ptr.cast::<Unalign<T>>()
}
}
Loading

0 comments on commit 995e1f0

Please sign in to comment.