aboutsummaryrefslogtreecommitdiff
path: root/src/macros.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/macros.rs')
-rw-r--r--src/macros.rs213
1 files changed, 190 insertions, 23 deletions
diff --git a/src/macros.rs b/src/macros.rs
index aebc8d6..2da78af 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -1,6 +1,10 @@
-// Copyright 2023 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
+// Copyright 2023 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.
/// Documents multiple unsafe blocks with a single safety comment.
///
@@ -22,20 +26,46 @@
/// The macro invocations are emitted, each decorated with the following
/// attribute: `#[allow(clippy::undocumented_unsafe_blocks)]`.
macro_rules! safety_comment {
- (#[doc = r" SAFETY:"] $($(#[doc = $_doc:literal])* $macro:ident!$args:tt;)*) => {
- #[allow(clippy::undocumented_unsafe_blocks)]
- const _: () = { $($macro!$args;)* };
+ (#[doc = r" SAFETY:"] $($(#[$attr:meta])* $macro:ident!$args:tt;)*) => {
+ #[allow(clippy::undocumented_unsafe_blocks, unused_attributes)]
+ const _: () = { $($(#[$attr])* $macro!$args;)* };
}
}
/// Unsafely implements trait(s) for a type.
+///
+/// # Safety
+///
+/// The trait impl must be sound.
+///
+/// When implementing `TryFromBytes`:
+/// - If no `is_bit_valid` impl is provided, then it must be valid for
+/// `is_bit_valid` to unconditionally return `true`. In other words, it must
+/// be the case that any initialized sequence of bytes constitutes a valid
+/// instance of `$ty`.
+/// - If an `is_bit_valid` impl is provided, then:
+/// - Regardless of whether the provided closure takes a `Ptr<$repr>` or
+/// `&$repr` argument, it must be the case that, given `t: *mut $ty` and
+/// `let r = t as *mut $repr`, `r` refers to an object of equal or lesser
+/// size than the object referred to by `t`.
+/// - If the provided closure takes a `&$repr` argument, then given a `Ptr<'a,
+/// $ty>` which satisfies the preconditions of
+/// `TryFromBytes::<$ty>::is_bit_valid`, it must be guaranteed that the
+/// memory referenced by that `Ptr` always contains a valid `$repr`.
+/// - The alignment of `$repr` is less than or equal to the alignment of
+/// `$ty`.
+/// - The impl of `is_bit_valid` must only return `true` for its argument
+/// `Ptr<$repr>` if the original `Ptr<$ty>` refers to a valid `$ty`.
macro_rules! unsafe_impl {
// Implement `$trait` for `$ty` with no bounds.
- ($ty:ty: $trait:ty) => {
- unsafe impl $trait for $ty { #[allow(clippy::missing_inline_in_public_items)] fn only_derive_is_allowed_to_implement_this_trait() {} }
+ ($(#[$attr:meta])* $ty:ty: $trait:ident $(; |$candidate:ident: &$repr:ty| $is_bit_valid:expr)?) => {
+ $(#[$attr])*
+ unsafe impl $trait for $ty {
+ unsafe_impl!(@method $trait $(; |$candidate: &$repr| $is_bit_valid)?);
+ }
};
// Implement all `$traits` for `$ty` with no bounds.
- ($ty:ty: $($traits:ty),*) => {
+ ($ty:ty: $($traits:ident),*) => {
$( unsafe_impl!($ty: $traits); )*
};
// This arm is identical to the following one, except it contains a
@@ -64,40 +94,142 @@ macro_rules! unsafe_impl {
// The following arm has the same behavior with the exception of the lack of
// support for a leading `const` parameter.
(
+ $(#[$attr:meta])*
const $constname:ident : $constty:ident $(,)?
$($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),*
- => $trait:ident for $ty:ty
+ => $trait:ident for $ty:ty $(; |$candidate:ident $(: &$ref_repr:ty)? $(: Ptr<$ptr_repr:ty>)?| $is_bit_valid:expr)?
) => {
unsafe_impl!(
@inner
+ $(#[$attr])*
@const $constname: $constty,
$($tyvar $(: $(? $optbound +)* + $($bound +)*)?,)*
- => $trait for $ty
+ => $trait for $ty $(; |$candidate $(: &$ref_repr)? $(: Ptr<$ptr_repr>)?| $is_bit_valid)?
);
};
(
+ $(#[$attr:meta])*
$($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),*
- => $trait:ident for $ty:ty
+ => $trait:ident for $ty:ty $(; |$candidate:ident $(: &$ref_repr:ty)? $(: Ptr<$ptr_repr:ty>)?| $is_bit_valid:expr)?
) => {
unsafe_impl!(
@inner
+ $(#[$attr])*
$($tyvar $(: $(? $optbound +)* + $($bound +)*)?,)*
- => $trait for $ty
+ => $trait for $ty $(; |$candidate $(: &$ref_repr)? $(: Ptr<$ptr_repr>)?| $is_bit_valid)?
);
};
(
@inner
+ $(#[$attr:meta])*
$(@const $constname:ident : $constty:ident,)*
$($tyvar:ident $(: $(? $optbound:ident +)* + $($bound:ident +)* )?,)*
- => $trait:ident for $ty:ty
+ => $trait:ident for $ty:ty $(; |$candidate:ident $(: &$ref_repr:ty)? $(: Ptr<$ptr_repr:ty>)?| $is_bit_valid:expr)?
) => {
+ $(#[$attr])*
unsafe impl<$(const $constname: $constty,)* $($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> $trait for $ty {
+ unsafe_impl!(@method $trait $(; |$candidate: $(&$ref_repr)? $(Ptr<$ptr_repr>)?| $is_bit_valid)?);
+ }
+ };
+
+ (@method TryFromBytes ; |$candidate:ident: &$repr:ty| $is_bit_valid:expr) => {
+ #[inline]
+ unsafe fn is_bit_valid(candidate: Ptr<'_, Self>) -> bool {
+ // SAFETY:
+ // - The argument to `cast_unsized` is `|p| p as *mut _` as required
+ // by that method's safety precondition.
+ // - The caller has promised that the cast results in an object of
+ // equal or lesser size.
+ // - The caller has promised that `$repr`'s alignment is less than
+ // or equal to `Self`'s alignment.
+ #[allow(clippy::as_conversions)]
+ let candidate = unsafe { candidate.cast_unsized::<$repr, _>(|p| p as *mut _) };
+ // SAFETY:
+ // - The caller has promised that the referenced memory region will
+ // contain a valid `$repr` for `'a`.
+ // - The memory may not be referenced by any mutable references.
+ // This is a precondition of `is_bit_valid`.
+ // - The memory may not be mutated even via `UnsafeCell`s. This is a
+ // precondition of `is_bit_valid`.
+ // - There must not exist any references to the same memory region
+ // which contain `UnsafeCell`s at byte ranges which are not
+ // identical to the byte ranges at which `T` contains
+ // `UnsafeCell`s. This is a precondition of `is_bit_valid`.
+ let $candidate: &$repr = unsafe { candidate.as_ref() };
+ $is_bit_valid
+ }
+ };
+ (@method TryFromBytes ; |$candidate:ident: Ptr<$repr:ty>| $is_bit_valid:expr) => {
+ #[inline]
+ unsafe fn is_bit_valid(candidate: Ptr<'_, Self>) -> bool {
+ // SAFETY:
+ // - The argument to `cast_unsized` is `|p| p as *mut _` as required
+ // by that method's safety precondition.
+ // - The caller has promised that the cast results in an object of
+ // equal or lesser size.
+ // - The caller has promised that `$repr`'s alignment is less than
+ // or equal to `Self`'s alignment.
+ #[allow(clippy::as_conversions)]
+ let $candidate = unsafe { candidate.cast_unsized::<$repr, _>(|p| p as *mut _) };
+ $is_bit_valid
+ }
+ };
+ (@method TryFromBytes) => { #[inline(always)] unsafe fn is_bit_valid(_: Ptr<'_, Self>) -> bool { true } };
+ (@method $trait:ident) => {
+ #[allow(clippy::missing_inline_in_public_items)]
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ };
+ (@method $trait:ident; |$_candidate:ident $(: &$_ref_repr:ty)? $(: NonNull<$_ptr_repr:ty>)?| $_is_bit_valid:expr) => {
+ compile_error!("Can't provide `is_bit_valid` impl for trait other than `TryFromBytes`");
+ };
+}
+
+/// Implements a trait for a type, bounding on each memeber of the power set of
+/// a set of type variables. This is useful for implementing traits for tuples
+/// or `fn` types.
+///
+/// The last argument is the name of a macro which will be called in every
+/// `impl` block, and is expected to expand to the name of the type for which to
+/// implement the trait.
+///
+/// For example, the invocation:
+/// ```ignore
+/// unsafe_impl_for_power_set!(A, B => Foo for type!(...))
+/// ```
+/// ...expands to:
+/// ```ignore
+/// unsafe impl Foo for type!() { ... }
+/// unsafe impl<B> Foo for type!(B) { ... }
+/// unsafe impl<A, B> Foo for type!(A, B) { ... }
+/// ```
+macro_rules! unsafe_impl_for_power_set {
+ ($first:ident $(, $rest:ident)* $(-> $ret:ident)? => $trait:ident for $macro:ident!(...)) => {
+ unsafe_impl_for_power_set!($($rest),* $(-> $ret)? => $trait for $macro!(...));
+ unsafe_impl_for_power_set!(@impl $first $(, $rest)* $(-> $ret)? => $trait for $macro!(...));
+ };
+ ($(-> $ret:ident)? => $trait:ident for $macro:ident!(...)) => {
+ unsafe_impl_for_power_set!(@impl $(-> $ret)? => $trait for $macro!(...));
+ };
+ (@impl $($vars:ident),* $(-> $ret:ident)? => $trait:ident for $macro:ident!(...)) => {
+ unsafe impl<$($vars,)* $($ret)?> $trait for $macro!($($vars),* $(-> $ret)?) {
#[allow(clippy::missing_inline_in_public_items)]
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};
}
+/// Expands to an `Option<extern "C" fn>` type with the given argument types and
+/// return type. Designed for use with `unsafe_impl_for_power_set`.
+macro_rules! opt_extern_c_fn {
+ ($($args:ident),* -> $ret:ident) => { Option<extern "C" fn($($args),*) -> $ret> };
+}
+
+/// Expands to a `Option<fn>` type with the given argument types and return
+/// type. Designed for use with `unsafe_impl_for_power_set`.
+macro_rules! opt_fn {
+ ($($args:ident),* -> $ret:ident) => { Option<fn($($args),*) -> $ret> };
+}
+
/// Implements trait(s) for a type or verifies the given implementation by
/// referencing an existing (derived) implementation.
///
@@ -204,11 +336,26 @@ macro_rules! impl_known_layout {
};
($($ty:ty),*) => { $(impl_known_layout!(@inner , => $ty);)* };
(@inner $(const $constvar:ident : $constty:ty)? , $($tyvar:ident $(: ?$optbound:ident)?)? => $ty:ty) => {
- impl<$(const $constvar : $constty,)? $($tyvar $(: ?$optbound)?)?> sealed::KnownLayoutSealed for $ty {}
- // SAFETY: Delegates safety to `DstLayout::for_type`.
- unsafe impl<$(const $constvar : $constty,)? $($tyvar $(: ?$optbound)?)?> KnownLayout for $ty {
- const LAYOUT: DstLayout = DstLayout::for_type::<$ty>();
- }
+ const _: () = {
+ use core::ptr::NonNull;
+
+ // SAFETY: Delegates safety to `DstLayout::for_type`.
+ unsafe impl<$(const $constvar : $constty,)? $($tyvar $(: ?$optbound)?)?> KnownLayout for $ty {
+ #[allow(clippy::missing_inline_in_public_items)]
+ fn only_derive_is_allowed_to_implement_this_trait() where Self: Sized {}
+
+ const LAYOUT: DstLayout = DstLayout::for_type::<$ty>();
+
+ // SAFETY: `.cast` preserves address and provenance.
+ //
+ // TODO(#429): Add documentation to `.cast` that promises that
+ // it preserves provenance.
+ #[inline(always)]
+ fn raw_from_ptr_len(bytes: NonNull<u8>, _elems: usize) -> NonNull<Self> {
+ bytes.cast::<Self>()
+ }
+ }
+ };
};
}
@@ -225,10 +372,30 @@ macro_rules! impl_known_layout {
/// and this operation must preserve referent size (ie, `size_of_val_raw`).
macro_rules! unsafe_impl_known_layout {
($($tyvar:ident: ?Sized + KnownLayout =>)? #[repr($repr:ty)] $ty:ty) => {
- impl<$($tyvar: ?Sized + KnownLayout)?> sealed::KnownLayoutSealed for $ty {}
- unsafe impl<$($tyvar: ?Sized + KnownLayout)?> KnownLayout for $ty {
- const LAYOUT: DstLayout = <$repr as KnownLayout>::LAYOUT;
- }
+ const _: () = {
+ use core::ptr::NonNull;
+
+ unsafe impl<$($tyvar: ?Sized + KnownLayout)?> KnownLayout for $ty {
+ #[allow(clippy::missing_inline_in_public_items)]
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+
+ const LAYOUT: DstLayout = <$repr as KnownLayout>::LAYOUT;
+
+ // SAFETY: All operations preserve address and provenance.
+ // Caller has promised that the `as` cast preserves size.
+ //
+ // TODO(#429): Add documentation to `NonNull::new_unchecked`
+ // that it preserves provenance.
+ #[inline(always)]
+ #[allow(unused_qualifications)] // for `core::ptr::NonNull`
+ fn raw_from_ptr_len(bytes: NonNull<u8>, elems: usize) -> NonNull<Self> {
+ #[allow(clippy::as_conversions)]
+ let ptr = <$repr>::raw_from_ptr_len(bytes, elems).as_ptr() as *mut Self;
+ // SAFETY: `ptr` was converted from `bytes`, which is non-null.
+ unsafe { NonNull::new_unchecked(ptr) }
+ }
+ }
+ };
};
}