Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] extend TransparentWrapper conversion functions #58

Merged
merged 8 commits into from
Mar 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rustfmt.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ use_small_heuristics = "Max"

# Unstable
format_code_in_doc_comments = true
merge_imports = true
imports_granularity = "Crate"
wrap_comments = true
4 changes: 2 additions & 2 deletions src/contiguous.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ pub unsafe trait Contiguous: Copy + 'static {
/// `#[repr(Int)]` or `#[repr(C)]` attribute, (if it does not, it is
/// *unsound* to implement `Contiguous`!).
///
/// - For `#[repr(Int)]`, use the listed `Int`. e.g. `#[repr(u8)]` should
/// use `type Int = u8`.
/// - For `#[repr(Int)]`, use the listed `Int`. e.g. `#[repr(u8)]` should use
/// `type Int = u8`.
///
/// - For `#[repr(C)]`, use whichever type the C compiler will use to
/// represent the given enum. This is usually `c_int` (from `std::os::raw`
Expand Down
15 changes: 9 additions & 6 deletions src/offset_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
/// # use bytemuck::offset_of;
/// // enums can't derive default, and for this example we don't pick one
/// enum MyExampleEnum {
/// A, B, C,
/// A,
/// B,
/// C,
/// }
///
/// // so now our struct here doesn't have Default
Expand Down Expand Up @@ -65,11 +67,11 @@
/// [rust-lang/rust#27060]: https://github.com/rust-lang/rust/issues/27060
///
/// <p style="background:rgba(255,181,77,0.16);padding:0.75em;">
/// <strong>Warning:</strong> This is only true for versions of bytemuck > 1.4.0.
/// Previous versions of
/// <strong>Warning:</strong> This is only true for versions of bytemuck >
/// 1.4.0. Previous versions of
/// <code style="background:rgba(41,24,0,0.1);">bytemuck::offset_of!</code>
/// will only emit a warning when used on the field of a packed struct in safe code,
/// which can lead to unsoundness.
/// will only emit a warning when used on the field of a packed struct in safe
/// code, which can lead to unsoundness.
/// </p>
///
/// For example, the following will fail to compile:
Expand All @@ -91,7 +93,8 @@
/// ```compile_fail
/// # #[repr(C, packed)] #[derive(Default)] struct Example { field: u32 }
/// // Still doesn't compile:
/// #[allow(safe_packed_borrows)] {
/// #[allow(safe_packed_borrows)]
/// {
/// let _offset = bytemuck::offset_of!(Example, field);
/// }
/// ```
Expand Down
224 changes: 169 additions & 55 deletions src/transparent.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,42 @@
use super::*;

/// A trait which indicates that a type is a `repr(transparent)` wrapper around
/// the `Wrapped` value.
/// A trait which indicates that a type is a `#[repr(transparent)]` wrapper
/// around the `Inner` value.
///
/// This allows safely creating references to `T` from those to the `Wrapped`
/// type, using the `wrap_ref` and `wrap_mut` functions.
/// This allows safely copy transmuting between the `Inner` type and the
/// `TransparentWrapper` type.
/// Functions like `wrap_{}` convert from the inner
/// type to the wrapper type and `unwrap_{}` functions do the inverse conversion
/// from the wrapper type to the inner type.
///
/// # Safety
///
/// The safety contract of `TransparentWrapper` is relatively simple:
///
/// For a given `Wrapper` which implements `TransparentWrapper<Wrapped>`:
/// For a given `Wrapper` which implements `TransparentWrapper<Inner>`:
///
/// 1. Wrapper must be a `#[repr(transparent)]` wrapper around `Wrapped`. This
/// either means that it must be a `#[repr(transparent)]` struct which
/// contains a either a field of type `Wrapped` (or a field of some other
/// transparent wrapper for `Wrapped`) as the only non-ZST field.
/// 1. `Wrapper` must be a wrapper around `Inner` with an identical data
/// representations. This either means that it must be a
/// `#[repr(transparent)]` struct which contains a either a field of type
/// `Inner` (or a field of some other transparent wrapper for `Inner`) as the
/// only non-ZST field.
///
/// 2. Any fields *other* than the `Wrapped` field must be trivially
/// 2. Any fields *other* than the `Inner` field must be trivially
/// constructable ZSTs, for example `PhantomData`, `PhantomPinned`, etc.
///
/// 3. The `Wrapper` may not impose additional alignment requirements over
/// `Wrapped`.
/// `Inner`.
/// - Note: this is currently guaranteed by `repr(transparent)`, but there
/// have been discussions of lifting it, so it's stated here explicitly.
///
/// 4. The `wrap_ref` and `wrap_mut` functions on `TransparentWrapper` may not
/// be overridden.
/// 4. All functions on
/// `TransparentWrapper` **may not** be overridden.
///
/// ## Caveats
///
/// If the wrapper imposes additional constraints upon the wrapped type which
/// If the wrapper imposes additional constraints upon the inner type which
/// are required for safety, it's responsible for ensuring those still hold --
/// this generally requires preventing access to instances of the wrapped type,
/// this generally requires preventing access to instances of the inner type,
/// as implementing `TransparentWrapper<U> for T` means anybody can call
/// `T::cast_ref(any_instance_of_u)`.
///
Expand All @@ -55,13 +59,13 @@ use super::*;
///
/// // interpret a reference to &SomeStruct as a &MyWrapper
/// let thing = SomeStruct::default();
/// let wrapped_ref: &MyWrapper = MyWrapper::wrap_ref(&thing);
/// let inner_ref: &MyWrapper = MyWrapper::wrap_ref(&thing);
///
/// // Works with &mut too.
/// let mut mut_thing = SomeStruct::default();
/// let wrapped_mut: &mut MyWrapper = MyWrapper::wrap_mut(&mut mut_thing);
/// let inner_mut: &mut MyWrapper = MyWrapper::wrap_mut(&mut mut_thing);
///
/// # let _ = (wrapped_ref, wrapped_mut); // silence warnings
/// # let _ = (inner_ref, inner_mut); // silence warnings
/// ```
///
/// ## Use with dynamically sized types
Expand All @@ -80,56 +84,166 @@ use super::*;
/// let mut buf = [1, 2, 3u8];
/// let sm = Slice::wrap_mut(&mut buf);
/// ```
pub unsafe trait TransparentWrapper<Wrapped: ?Sized> {
/// Convert a reference to a wrapped type into a reference to the wrapper.
///
/// This is a trait method so that you can write `MyType::wrap_ref(...)` in
/// your code. It is part of the safety contract for this trait that if you
/// implement `TransparentWrapper<_>` for your type you **must not** override
/// this method.
pub unsafe trait TransparentWrapper<Inner: ?Sized> {
/// Convert the inner type into the wrapper type.
#[inline]
fn wrap_ref(s: &Wrapped) -> &Self {
fn wrap(s: Inner) -> Self
where
Self: Sized,
Inner: Sized,
{
// SAFETY: The unsafe contract requires that `Self` and `Inner` have
// identical representations.
unsafe { transmute_copy(&s) }
}

/// Convert a reference to the inner type into a reference to the wrapper
/// type.
#[inline]
fn wrap_ref(s: &Inner) -> &Self {
unsafe {
assert!(size_of::<*const Wrapped>() == size_of::<*const Self>());
// Using a pointer cast doesn't work here because rustc can't tell that the
// vtables match (if we lifted the ?Sized restriction, this would go away),
// and transmute doesn't work for the usual reasons it doesn't work inside
// generic functions.
assert!(size_of::<*const Inner>() == size_of::<*const Self>());
// A pointer cast does't work here because rustc can't tell that
// the vtables match (because of the `?Sized` restriction relaxation).
// A `transmute` doesn't work because the sizes are unspecified.
//
// SAFETY: The unsafe contract requires that these have identical
// representations. Using this transmute_copy instead of transmute here is
// annoying, but is required as `Self` and `Wrapped` have unspecified
// sizes still.
let wrapped_ptr = s as *const Wrapped;
let wrapper_ptr: *const Self = transmute_copy(&wrapped_ptr);
// SAFETY: The unsafe contract requires that these two have
// identical representations.
let inner_ptr = s as *const Inner;
let wrapper_ptr: *const Self = transmute_copy(&inner_ptr);
&*wrapper_ptr
}
}

/// Convert a mut reference to a wrapped type into a mut reference to the
/// wrapper.
///
/// This is a trait method so that you can write `MyType::wrap_mut(...)` in
/// your code. It is part of the safety contract for this trait that if you implement
/// `TransparentWrapper<_>` for your type you **must not** override this method.
/// Convert a mutable reference to the inner type into a mutable reference to
/// the wrapper type.
#[inline]
fn wrap_mut(s: &mut Wrapped) -> &mut Self {
fn wrap_mut(s: &mut Inner) -> &mut Self {
unsafe {
assert!(size_of::<*mut Wrapped>() == size_of::<*mut Self>());
// Using a pointer cast doesn't work here because rustc can't tell that the
// vtables match (if we lifted the ?Sized restriction, this would go away),
// and transmute doesn't work for the usual reasons it doesn't work inside
// generic functions.
assert!(size_of::<*mut Inner>() == size_of::<*mut Self>());
// A pointer cast does't work here because rustc can't tell that
// the vtables match (because of the `?Sized` restriction relaxation).
// A `transmute` doesn't work because the sizes are unspecified.
//
// SAFETY: The unsafe contract requires that these have identical
// representations. Using this transmute_copy instead of transmute here is
// annoying, but is required as `Self` and `Wrapped` have unspecified
// sizes still.
let wrapped_ptr = s as *mut Wrapped;
let wrapper_ptr: *mut Self = transmute_copy(&wrapped_ptr);
// SAFETY: The unsafe contract requires that these two have
// identical representations.
let inner_ptr = s as *mut Inner;
let wrapper_ptr: *mut Self = transmute_copy(&inner_ptr);
&mut *wrapper_ptr
}
}

/// Convert a slice to the inner type into a slice to the wrapper type.
#[inline]
fn wrap_slice(s: &[Inner]) -> &[Self]
where
Self: Sized,
Inner: Sized,
{
unsafe {
assert!(size_of::<*const Inner>() == size_of::<*const Self>());
assert!(align_of::<*const Inner>() == align_of::<*const Self>());
// SAFETY: The unsafe contract requires that these two have
// identical representations (size and alignment).
core::slice::from_raw_parts(s.as_ptr() as *const Self, s.len())
}
}

/// Convert a mutable slice to the inner type into a mutable slice to the
/// wrapper type.
#[inline]
fn wrap_slice_mut(s: &mut [Inner]) -> &mut [Self]
where
Self: Sized,
Inner: Sized,
{
unsafe {
assert!(size_of::<*mut Inner>() == size_of::<*mut Self>());
assert!(align_of::<*mut Inner>() == align_of::<*mut Self>());
// SAFETY: The unsafe contract requires that these two have
// identical representations (size and alignment).
core::slice::from_raw_parts_mut(s.as_mut_ptr() as *mut Self, s.len())
}
}

/// Convert the wrapper type into the inner type.
#[inline]
fn unwrap(s: Self) -> Inner
where
Self: Sized,
Inner: Sized,
{
unsafe { transmute_copy(&s) }
}

/// Convert a reference to the wrapper type into a reference to the inner
/// type.
#[inline]
fn unwrap_ref(s: &Self) -> &Inner {
unsafe {
assert!(size_of::<*const Inner>() == size_of::<*const Self>());
// A pointer cast does't work here because rustc can't tell that
// the vtables match (because of the `?Sized` restriction relaxation).
// A `transmute` doesn't work because the sizes are unspecified.
//
// SAFETY: The unsafe contract requires that these two have
// identical representations.
let wrapper_ptr = s as *const Self;
let inner_ptr: *const Inner = transmute_copy(&wrapper_ptr);
&*inner_ptr
}
}

/// Convert a mutable reference to the wrapper type into a mutable reference
/// to the inner type.
#[inline]
fn unwrap_mut(s: &mut Self) -> &mut Inner {
unsafe {
assert!(size_of::<*mut Inner>() == size_of::<*mut Self>());
// A pointer cast does't work here because rustc can't tell that
// the vtables match (because of the `?Sized` restriction relaxation).
// A `transmute` doesn't work because the sizes are unspecified.
//
// SAFETY: The unsafe contract requires that these two have
// identical representations.
let wrapper_ptr = s as *mut Self;
let inner_ptr: *mut Inner = transmute_copy(&wrapper_ptr);
&mut *inner_ptr
}
}

/// Convert a slice to the wrapped type into a slice to the inner type.
#[inline]
fn unwrap_slice(s: &[Self]) -> &[Inner]
where
Self: Sized,
Inner: Sized,
{
unsafe {
assert!(size_of::<*const Inner>() == size_of::<*const Self>());
assert!(align_of::<*const Inner>() == align_of::<*const Self>());
// SAFETY: The unsafe contract requires that these two have
// identical representations (size and alignment).
core::slice::from_raw_parts(s.as_ptr() as *const Inner, s.len())
}
}

/// Convert a mutable slice to the wrapped type into a mutable slice to the
/// inner type.
#[inline]
fn unwrap_slice_mut(s: &mut [Self]) -> &mut [Inner]
where
Self: Sized,
Inner: Sized,
{
unsafe {
assert!(size_of::<*mut Inner>() == size_of::<*mut Self>());
assert!(align_of::<*mut Inner>() == align_of::<*mut Self>());
// SAFETY: The unsafe contract requires that these two have
// identical representations (size and alignment).
core::slice::from_raw_parts_mut(s.as_mut_ptr() as *mut Inner, s.len())
}
}
}

unsafe impl<T> TransparentWrapper<T> for core::num::Wrapping<T> {}
Loading