Skip to content

Simplify zerocopy-derive-generated code #109

@joshlf

Description

@joshlf

We ought to be able to simplify the code generated by zerocopy-derive's AsBytes impl by adding the following module to zerocopy:

/// Utilities used by `zerocopy-derive`.
///
/// These are defined in `zerocopy` rather than in code generated by
/// `zerocopy-derive` so that they can be compiled once rather than recompiled
/// for every pair of type and trait (in other words, if they were defined in
/// generated code, then deriving `AsBytes` and `FromBytes` on three different
/// types would result in the code in question being emitted and compiled six
/// different times).
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub mod derive_util {
    /// Implemented for `Bool<true>`.
    pub trait True {}

    /// A boolean constant value which can be used in type bounds.
    pub struct Bool<const TERM: bool>();

    impl True for Bool<true> {}

    /// Does the struct type `$t` have padding?
    ///
    /// `$ts` is the list of the type of every field in `$t`. `$t` must be a
    /// struct type, or else `has_padding!`'s result may be meaningless.
    ///
    /// Note that `has_padding!`'s results are independent of `repr` since they
    /// only consider the size of the type and the sizes of the fields. Whatever
    /// the repr, the size of the type already takes into account any padding
    /// that the compiler has decided to add. Note that while this is *probably*
    /// also true of `repr(rust)`, the author is not confident of that fact, and
    /// it should not be relied upon for soundness.
    #[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`.
    #[macro_export]
    macro_rules! has_padding {
        ($t:ty, $($ts:ty),*) => {
            core::mem::size_of::<$t>() > 0 $(+ core::mem::size_of::<$ts>())*
        };
    }
}

Then, for a type like:

#[derive(AsBytes)]
#[repr(C)]
struct Foo(u8, u16);

...we'd emit an impl like:

unsafe impl AsBytes for Foo where Bool<{!zerocopy::has_padding!(Foo, u8, u16)}>: True {
    fn only_derive_is_allowed_to_implement_this_trait()
    where
        Self: Sized,
    {}
}

Notably, this ought to make it a very simple change to support type parameters by emitting code like:

unsafe impl AsBytes for Bar<T> where Bool<{!zerocopy::has_padding!(Bar<T>, T)}>: True {
    fn only_derive_is_allowed_to_implement_this_trait()
    where
        Self: Sized,
    {}
}

Of course, that code isn't a supported use of const generics today, but whenever it is in the future, the change will be simple.

We might be able to use a similar technique to simplify impls of other traits as well.

Metadata

Metadata

Assignees

No one assigned

    Labels

    experience-hardThis issue is hard, and requires a lot of experience

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions