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

Unions are not supported #59

Open
DBLouis opened this issue May 11, 2021 · 2 comments
Open

Unions are not supported #59

DBLouis opened this issue May 11, 2021 · 2 comments

Comments

@DBLouis
Copy link
Contributor

DBLouis commented May 11, 2021

#[derive_ReprC]
#[repr(C)]
#[derive(Copy, Clone)]
pub union Foobar {
    foo: i32,
    bar: f64,
}

This fails to compile with: error: `union`s are not supported yet.

@DBLouis
Copy link
Contributor Author

DBLouis commented May 11, 2021

Could I make this work quickly? I need this; Maybe with a more recent branch?

@danielhenrymantilla
Copy link
Collaborator

danielhenrymantilla commented May 11, 2021

@DBLouis you surely can do this, it'll be a matter of "copy-pasting" the struct derive logic and replacing the occurrences of struct with union:

  1. Copy-paste the proc-macro logic for the struct here:

    | Data::Struct(DataStruct {
    struct_token: ref struct_,
    ref fields,
    semi_token: ref maybe_semi_colon,
    }) => {
    let (params, bounds) = generics.my_split();
    quote! {
    ::safer_ffi::layout::#name! {
    #(#attrs)*
    #vis
    #struct_ #ident
    [#params]
    where {
    #(#bounds ,)*
    }
    #fields
    #maybe_semi_colon
    }
    }
    },
    | Data::Union(ref union_) => {
    Error::new_spanned(
    union_.union_token,
    "`union`s are not supported yet."
    ).to_compile_error()
    },

    and replace the union compile_error with that, replacing struct_ with union_.

  2. Copy-paste (and add to the macro) the whole macro_rules! rule at

    macro_rules! CType {(
    $(
    @doc_meta( $($doc_meta:tt)* )
    )?
    #[repr(C)]
    $(#[$($meta:tt)*])*
    $pub:vis
    struct $StructName:ident $(
    [
    $($lt:lifetime ,)*
    $($($generics:ident),+ $(,)?)?
    ]
    $(where { $($bounds:tt)* })?
    )?
    {
    $(
    $(#[$($field_meta:tt)*])*
    $field_pub:vis
    $field_name:ident : $field_ty:ty
    ),+ $(,)?
    }
    ) => (
    #[repr(C)]
    $(#[$($meta)*])*
    $pub
    struct $StructName
    $(<$($lt ,)* $($($generics),+)?> $(where $($bounds)* )?)?
    {
    $(
    $(#[$($field_meta)*])*
    $field_pub
    $field_name : $field_ty,
    )*
    }
    unsafe // Safety: struct is `#[repr(C)]` and contains `CType` fields
    impl $(<$($lt ,)* $($($generics),+)?>)? $crate::layout::CType
    for $StructName$(<$($lt ,)* $($($generics),+)?>)?
    where
    $(
    $field_ty : $crate::layout::CType,
    )*
    $(
    $($(
    $generics : $crate::layout::ReprC,
    )+)?
    $($($bounds)*)?
    )?
    { $crate::__cfg_headers__! {
    fn c_short_name_fmt (fmt: &'_ mut $crate::core::fmt::Formatter<'_>)
    -> $crate::core::fmt::Result
    {
    fmt.write_str($crate::core::stringify!($StructName))?;
    $($(
    $(
    $crate::core::write!(fmt, "_{}",
    <
    <$generics as $crate::layout::ReprC>::CLayout
    as
    $crate::layout::CType
    >::c_short_name()
    )?;
    )+
    )?)?
    Ok(())
    }
    fn c_define_self (definer: &'_ mut dyn $crate::headers::Definer)
    -> $crate::std::io::Result<()>
    {
    assert_ne!(
    $crate::core::mem::size_of::<Self>(), 0,
    "C does not support zero-sized structs!",
    );
    let ref me =
    <Self as $crate::layout::CType>
    ::c_short_name().to_string()
    ;
    definer.define_once(
    me,
    &mut |definer| {
    $(
    <$field_ty as $crate::layout::CType>::c_define_self(definer)?;
    )*
    let out = definer.out();
    $(
    $crate::__output_docs__!(out, "", $($doc_meta)*);
    )?
    $crate::__output_docs__!(out, "", $(#[$($meta)*])*);
    $crate::core::writeln!(out, "typedef struct {{\n")?;

    Replacing, again, struct with union there input-of-the-macro-rule-wise, and also at the last line of the quoted snippet:

    $crate::core::writeln!(out, "typedef struct {{\n")?;

  3. Similarly, copy-paste

    (
    $( @[doc = $doc:expr] )?
    $(#[doc = $prev_doc:tt])* // support doc comments _before_ `#[repr(C)]`
    #[repr(C)]
    $(#[$($meta:tt)*])*
    $pub:vis
    struct $StructName:ident $(
    [
    $($lt:lifetime ,)*
    $($($generics:ident),+ $(,)?)?
    ]
    $(where { $($bounds:tt)* })?
    )?
    {
    $(
    $(#[$($field_meta:tt)*])*
    $field_pub:vis
    $field_name:ident : $field_ty:ty
    ),+ $(,)?
    }
    ) => (
    $crate::__with_doc__! {
    #[doc = $crate::core::concat!(
    " - [`",
    $crate::core::stringify!($StructName),
    "_Layout`]"
    )]
    $(#[doc = $prev_doc])*
    #[repr(C)]
    $(#[doc = $doc])?
    $(#[$($meta)*])*
    /// # C Layout
    ///
    $pub
    struct $StructName $(
    <$($lt ,)* $($($generics),+)?> $(
    where $($bounds)*
    )?
    )?
    {
    $(
    $(#[$($field_meta)*])*
    $field_pub
    $field_name : $field_ty,
    )*
    }
    }
    $crate::paste::item! {
    #[allow(nonstandard_style)]
    $pub use
    [< __ $StructName _safer_ffi_mod >]::$StructName
    as
    [< $StructName _Layout >]
    ;
    }
    #[allow(trivial_bounds)]
    unsafe // Safety: struct is `#[repr(C)]` and contains `ReprC` fields
    impl $(<$($lt ,)* $($($generics),+)?>)? $crate::layout::ReprC
    for $StructName $(<$($lt ,)* $($($generics),+)?>)?
    where
    $(
    $field_ty : $crate::layout::ReprC,
    <$field_ty as $crate::layout::ReprC>::CLayout
    : $crate::layout::CType<
    OPAQUE_KIND = $crate::layout::OpaqueKind::Concrete,
    >,
    )*
    $(
    $($(
    $generics : $crate::layout::ReprC,
    )+)?
    $($($bounds)*)?
    )?
    {
    type CLayout = $crate::paste::__item__! {
    [<$StructName _Layout>]
    $(<$($lt ,)* $($($generics),+)?>)?
    };
    #[inline]
    fn is_valid (it: &'_ Self::CLayout)
    -> bool
    {
    let _ = it;
    true $(
    && (
    $crate::core::mem::size_of::<
    <$field_ty as $crate::layout::ReprC>::CLayout
    >() == 0
    ||
    <$field_ty as $crate::layout::ReprC>::is_valid(
    &it.$field_name
    )
    )
    )*
    }
    }
    $crate::paste::item! {
    #[allow(nonstandard_style, trivial_bounds)]
    mod [< __ $StructName _safer_ffi_mod >] {
    #[allow(unused_imports)]
    use super::*;
    $crate::layout::CType! {
    @doc_meta(
    $(#[doc = $prev_doc])*
    $(#[$($meta)*])*
    )
    #[repr(C)]
    #[allow(missing_debug_implementations)]
    // $(#[$meta])*
    pub
    struct $StructName
    [$($($lt ,)* $($($generics),+)?)?]
    where {
    $(
    $field_ty : $crate::layout::ReprC,
    )*
    $(
    $($(
    $generics : $crate::layout::ReprC,
    )+)?
    $($($bounds)*)?
    )?
    } {
    $(
    $(#[$($field_meta)*])*
    pub
    $field_name :
    <$field_ty as $crate::layout::ReprC>::CLayout
    ,
    )*
    }
    }
    }
    }
    const _: () = {
    $crate::paste::item! {
    use [< __ $StructName _safer_ffi_mod >]::*;
    }
    impl $(<$($lt ,)* $($($generics),+)?>)? $crate::core::marker::Copy
    for $StructName $(<$($lt ,)* $($($generics),+)?>)?
    where
    $(
    $field_ty : $crate::layout::ReprC,
    )*
    $(
    $($(
    $generics : $crate::layout::ReprC,
    )+)?
    $($($bounds)*)?
    )?
    {}
    impl $(<$($lt ,)* $($($generics),+)?>)? $crate::core::clone::Clone
    for $StructName $(<$($lt ,)* $($($generics),+)?>)?
    where
    $(
    $field_ty : $crate::layout::ReprC,
    )*
    $(
    $($(
    $generics : $crate::layout::ReprC,
    )+)?
    $($($bounds)*)?
    )?
    {
    #[inline]
    fn clone (self: &'_ Self)
    -> Self
    {
    *self
    }
    }
    };
    );
    replacing struct with enum in the input part of the macro rule, and then you'll just have to change the definition of fn is_valid inside it to true (remove the "each field needs to be valid" logic that follows, since for a union safer-ffi cannot know which variant is active (if you want to push it further, you can actually leave that logic be, but replacing true with false and the && with ||)).

DBLouis added a commit to DBLouis/safer_ffi that referenced this issue May 11, 2021
DBLouis added a commit to DBLouis/safer_ffi that referenced this issue May 11, 2021
DBLouis added a commit to DBLouis/safer_ffi that referenced this issue May 11, 2021
DBLouis added a commit to DBLouis/safer_ffi that referenced this issue May 12, 2021
DBLouis added a commit to DBLouis/safer_ffi that referenced this issue Jun 30, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants