// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
//
// 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.
/// 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 the impl of `is_bit_valid`
/// must only return `true` if its argument refers to a valid `$ty`.
macro_rules! unsafe_impl {
// Implement `$trait` for `$ty` with no bounds.
($(#[$attr:meta])* $ty:ty: $trait:ident $(; |$candidate:ident| $is_bit_valid:expr)?) => {{
crate::util::macros::__unsafe();
$(#[$attr])*
// SAFETY: The caller promises that this is sound.
unsafe impl $trait for $ty {
unsafe_impl!(@method $trait $(; |$candidate| $is_bit_valid)?);
}
}};
// Implement all `$traits` for `$ty` with no bounds.
//
// The 2 arms under this one are there so we can apply
// N attributes for each one of M trait implementations.
// The simple solution of:
//
// ($(#[$attrs:meta])* $ty:ty: $($traits:ident),*) => {
// $( unsafe_impl!( $(#[$attrs])* $ty: $traits ) );*
// }
//
// Won't work. The macro processor sees that the outer repetition
// contains both $attrs and $traits and expects them to match the same
// amount of fragments.
//
// To solve this we must:
// 1. Pack the attributes into a single token tree fragment we can match over.
// 2. Expand the traits.
// 3. Unpack and expand the attributes.
($(#[$attrs:meta])* $ty:ty: $($traits:ident),*) => {
unsafe_impl!(@impl_traits_with_packed_attrs { $(#[$attrs])* } $ty: $($traits),*)
};
(@impl_traits_with_packed_attrs $attrs:tt $ty:ty: $($traits:ident),*) => {{
$( unsafe_impl!(@unpack_attrs $attrs $ty: $traits); )*
}};
(@unpack_attrs { $(#[$attrs:meta])* } $ty:ty: $traits:ident) => {
unsafe_impl!($(#[$attrs])* $ty: $traits);
};
// This arm is identical to the following one, except it contains a
// preceding `const`. If we attempt to handle these with a single arm, there
// is an inherent ambiguity between `const` (the keyword) and `const` (the
// ident match for `$tyvar:ident`).
//
// To explain how this works, consider the following invocation:
//
// unsafe_impl!(const N: usize, T: ?Sized + Copy => Clone for Foo<T>);
//
// In this invocation, here are the assignments to meta-variables:
//
// |---------------|------------|
// | Meta-variable | Assignment |
// |---------------|------------|
// | $constname | N |
// | $constty | usize |
// | $tyvar | T |
// | $optbound | Sized |
// | $bound | Copy |
// | $trait | Clone |
// | $ty | Foo<T> |
// |---------------|------------|
//
// 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 $(; |$candidate:ident| $is_bit_valid:expr)?
) => {
unsafe_impl!(
@inner
$(#[$attr])*
@const $constname: $constty,
$($tyvar $(: $(? $optbound +)* + $($bound +)*)?,)*
=> $trait for $ty $(; |$candidate| $is_bit_valid)?
);
};
(
$(#[$attr:meta])*
$($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),*
=> $trait:ident for $ty:ty $(; |$candidate:ident| $is_bit_valid:expr)?
) => {{
unsafe_impl!(
@inner
$(#[$attr])*
$($tyvar $(: $(? $optbound +)* + $($bound +)*)?,)*
=> $trait for $ty $(; |$candidate| $is_bit_valid)?
);
}};
(
@inner
$(#[$attr:meta])*
$(@const $constname:ident