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

Add InvariantFree trait #17

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
64 changes: 64 additions & 0 deletions crates/macros/src/invariant_free.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use quote::quote;
use syn::DeriveInput;

pub fn expand(input: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
let type_name = &input.ident;
let attrs = &input.attrs;
if !input.generics.params.is_empty() {
return Err(syn::Error::new(
proc_macro2::Span::call_site(),
"InvariantFree on types with generics is not currently supported",
));
}
let ensure = match &input.data {
syn::Data::Struct(s) => ensure_struct_invariant_free(type_name, s)?,
syn::Data::Enum(e) => ensure_enum_invariant_free(type_name, e, attrs)?,
syn::Data::Union(u) => ensure_union_invariant_free(type_name, u, attrs)?,
};

let stream = quote! {
#ensure
unsafe impl ::mem_markers::InvariantFree for #type_name {}
};
Ok(stream)
}

fn ensure_struct_invariant_free(
type_name: &syn::Ident,
s: &syn::DataStruct,
) -> syn::Result<proc_macro2::TokenStream> {
let field_types = crate::utils::struct_field_types(s);
if field_types.is_empty() {
return Ok(quote! {});
}

let stream = crate::utils::ensure_field_types(
field_types,
type_name,
&quote::format_ident!("InvariantFree"),
None,
);
Ok(stream)
}

fn ensure_enum_invariant_free(
_type_name: &syn::Ident,
_e: &syn::DataEnum,
_attrs: &Vec<syn::Attribute>,
) -> syn::Result<proc_macro2::TokenStream> {
return Err(syn::Error::new(
proc_macro2::Span::call_site(),
"InvariantFree on enums is not currently supported",
));
}

fn ensure_union_invariant_free(
_type_name: &syn::Ident,
_u: &syn::DataUnion,
_attrs: &Vec<syn::Attribute>,
) -> syn::Result<proc_macro2::TokenStream> {
return Err(syn::Error::new(
proc_macro2::Span::call_site(),
"InvariantFree on unions is not currently supported",
));
}
26 changes: 11 additions & 15 deletions crates/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,25 @@ extern crate proc_macro;
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput};

mod as_bytes;
mod byte_complete;
mod fixed_layout;
mod from_bytes;
mod no_uninit;
mod utils;
mod zeroable;

macro_rules! define_derive {
($item:meta => $fun:ident => $expand:path) => {
($item:meta => $mod:ident) => {
mod $mod;
#[proc_macro_derive($item)]
pub fn $fun(item: TokenStream) -> TokenStream {
pub fn $mod(item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as DeriveInput);
$expand(input)
$mod::expand(input)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
};
}

define_derive!(FixedLayout => fixed_layout => fixed_layout::expand);
define_derive!(ByteComplete => byte_complete => byte_complete::expand);
define_derive!(FromBytes => from_bytes => from_bytes::expand);
define_derive!(NoUninit => no_uninit => no_uninit::expand);
define_derive!(AsBytes => as_bytes => as_bytes::expand);
define_derive!(Zeroable => zeroable => zeroable::expand);
define_derive!(FixedLayout => fixed_layout);
define_derive!(ByteComplete => byte_complete);
define_derive!(FromBytes => from_bytes);
define_derive!(NoUninit => no_uninit);
define_derive!(AsBytes => as_bytes);
define_derive!(Zeroable => zeroable);
define_derive!(InvariantFree => invariant_free);
2 changes: 1 addition & 1 deletion crates/macros/tests/fail/from-bytes/not_fixed_layout.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use mem_markers::*;

#[derive(FromBytes, ByteComplete, Zeroable)]
#[derive(FromBytes, ByteComplete, InvariantFree, Zeroable)]
#[repr(C)]
struct Foo {
a: u8,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
error[E0277]: the trait bound `Foo: mem_markers::fixed_layout::FixedLayout` is not satisfied
--> $DIR/not_fixed_layout.rs:3:10
|
3 | #[derive(FromBytes, ByteComplete, Zeroable)]
3 | #[derive(FromBytes, ByteComplete, InvariantFree, Zeroable)]
| ^^^^^^^^^ the trait `mem_markers::fixed_layout::FixedLayout` is not implemented for `Foo`
|
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
4 changes: 2 additions & 2 deletions crates/macros/tests/pass/as-bytes/struct.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use mem_markers::{AsBytes, FixedLayout, NoUninit};
use mem_markers::{AsBytes, FixedLayout, InvariantFree, NoUninit};

#[derive(AsBytes, FixedLayout, NoUninit)]
#[derive(AsBytes, FixedLayout, InvariantFree, NoUninit)]
#[repr(C)]
struct Foo {
a: u8,
Expand Down
2 changes: 1 addition & 1 deletion crates/macros/tests/pass/from_bytes/struct.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use mem_markers::*;

#[derive(FromBytes, FixedLayout, ByteComplete, Zeroable)]
#[derive(FromBytes, FixedLayout, InvariantFree, ByteComplete, Zeroable)]
#[repr(C)]
struct Foo {
a: u8,
Expand Down
12 changes: 10 additions & 2 deletions src/as_bytes.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
/// A type which can be legally converted to raw bytes.
///
/// It is necessary for this type have a fixed layout and not
/// have any uninitialized bytes hence it requires those two traits
/// Types that implement `AsBytes` require the following traits to also
/// be implemented:
/// * [`NoUninit`]: It is undefined behavior to view uninitialized memory so `AsByte` types
/// must not contain any uninitialized memory.
/// * [`FixedLayout`]: In order to be sure that a type does not have uninitialized memory,
/// it must have a fixed layout otherwise it is possible that the compiler rearranges the type
/// to have padding (and thus uninitialized memory).
///
/// [`FixedLayout`]: trait.FixedLayout.html
/// [`NoUninit`]: trait.InvariantFree.html
pub unsafe trait AsBytes: crate::NoUninit + crate::FixedLayout {}

macro_rules! as_bytes_impl {
Expand Down
2 changes: 1 addition & 1 deletion src/byte_complete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::Zeroable;
/// `ByteComplete` is a more general version of `Zeroable` which has the same constraints but only for
/// zero bytes.
///
/// `ByteComplete` vs `FromBytes`
/// # `ByteComplete` vs `FromBytes`
/// `ByteComplete` types are not guranteed to have fixed layouts so creating types from bytes
/// may not be predictable though it will always yield a valid value. If you want predictability,
/// use `FromBytes`.
Expand Down
25 changes: 18 additions & 7 deletions src/from_bytes.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
use crate::{ByteComplete, FixedLayout};

/// A type which can be legally instantiated from raw bytes.
/// A type which can be safely _and_ correctly instantiated from raw
/// bytes _and_ then safely _and_ correctly used.
///
/// It is necessary for this type have a fixed layout and be
/// Types that implement `FromBytes` require the following traits to also
/// be implemented:
/// * [`FixedLayout`]: in order to get reliable results, the type
/// must have a well known layout.
/// * [`ByteComplete`]: in order to be able to turn _any_ properly sized
/// aligned byte array into the type, the type must be byte complete.
/// byte complete hence it requires the type implements
/// [`ByteComplete`] and [`FixedLayout`]. See those types
/// documentation for information on the invariants those
/// * [`InvariantFree`]: The type must not rely on any invariants to be confirmed
/// before the type can be used safely _and_ correctly.
///
/// See those types documentation for information on the invariants those
/// traits represent.
///
/// [`FromBytes`] is the logical opposite as [`AsBytes`].
Expand All @@ -14,7 +20,11 @@ use crate::{ByteComplete, FixedLayout};
/// [`AsBytes`]: trait.AsBytes.html
/// [`ByteComplete`]: trait.ByteComplete.html
/// [`FixedLayout`]: trait.FixedLayout.html
pub unsafe trait FromBytes: ByteComplete + FixedLayout {}
/// [`InvariantFree`]: trait.InvariantFree.html
pub unsafe trait FromBytes:
crate::InvariantFree + crate::ByteComplete + crate::FixedLayout
{
}

macro_rules! from_bytes_impl {
($($type:ty),*) => {
Expand All @@ -23,6 +33,7 @@ macro_rules! from_bytes_impl {
}

from_bytes_impl!(
(),
u8,
u16,
u32,
Expand Down
91 changes: 91 additions & 0 deletions src/invariant_free.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/// A type does not have any internal invariants that must be held in order for the
/// type to be used safely _and_ correctly.
///
/// Such data is often known as "plain ol' data" (a.k.a. POD) types.
///
/// # Examples
///
/// For example, `u8`s are `InvariantFree` since they are valid no matter what value they contain.
/// Conversely, `NonZeroU8` is not `InvariantFree` since it requires that it cannot be made zero.
///
/// It is important to note that this trait assumes that types keep no invariants even if violating
/// those invariants is memory safe. For example, the following type is _not_ `InvariantFree`:
///
/// ```rust
/// // A `u8` that is never `5`
/// struct NotFive(u8);
/// impl NotFive {
/// fn inc(&mut self) {
/// if self.0 == 4 {
/// self.0 = 6;
/// } else {
/// self.0 = self.0.wrapping_add(1);
/// }
/// }
/// }
/// ```
pub unsafe trait InvariantFree {}

macro_rules! invariant_free_impl {
($($type:ty),*) => {
$(unsafe impl InvariantFree for $type {})*
};
}

invariant_free_impl!(
(),
u8,
u16,
u32,
u64,
u128,
i8,
i16,
i32,
i64,
i128,
usize,
isize,
Option<core::num::NonZeroI8>,
Option<core::num::NonZeroU8>,
Option<core::num::NonZeroI16>,
Option<core::num::NonZeroU16>,
Option<core::num::NonZeroI32>,
Option<core::num::NonZeroU32>,
Option<core::num::NonZeroI64>,
Option<core::num::NonZeroU64>,
Option<core::num::NonZeroI128>,
Option<core::num::NonZeroU128>,
f32,
f64
);

unsafe impl<T> InvariantFree for *const T {}
unsafe impl<T> InvariantFree for *mut T {}
unsafe impl<T> InvariantFree for Option<core::ptr::NonNull<T>> {}

unsafe impl<T: InvariantFree> InvariantFree for [T; 0] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 1] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 2] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 3] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 4] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 5] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 6] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 7] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 8] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 9] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 10] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 11] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 12] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 13] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 14] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 15] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 16] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 17] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 18] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 19] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 20] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 21] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 22] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 23] {}
unsafe impl<T: InvariantFree> InvariantFree for [T; 24] {}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod as_bytes;
mod byte_complete;
mod fixed_layout;
mod from_bytes;
mod invariant_free;
mod no_uninit;
mod zeroable;

Expand All @@ -19,6 +20,8 @@ pub use fixed_layout::FixedLayout;
#[doc(inline)]
pub use from_bytes::FromBytes;
#[doc(inline)]
pub use invariant_free::InvariantFree;
#[doc(inline)]
pub use no_uninit::NoUninit;
#[doc(inline)]
pub use zeroable::Zeroable;
Expand Down