From b898cc666ffb36431398c9cae1fdf5911f3129d2 Mon Sep 17 00:00:00 2001 From: Pierre Avital Date: Sat, 22 Jun 2024 11:15:22 +0200 Subject: [PATCH] Start work on #[repr(C, uX)] enums --- stabby-macros/src/enums.rs | 87 ++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/stabby-macros/src/enums.rs b/stabby-macros/src/enums.rs index 894338a..5b2728e 100644 --- a/stabby-macros/src/enums.rs +++ b/stabby-macros/src/enums.rs @@ -53,6 +53,33 @@ impl syn::parse::Parse for Repr { } } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct FullRepr { + repr: Option, + is_c: bool, +} +impl syn::parse::Parse for FullRepr { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let mut this = FullRepr { + repr: None, + is_c: false, + }; + while !input.is_empty() { + match input.parse()? { + Repr::C => this.is_c = true, + repr => match this.repr { + None => this.repr = Some(repr), + Some(r) if repr == r => {} + _ => return Err(input.error("Determinants may only have one representation. You can use `#[repr(C, u8)]` to use a u8 as determinant while ensuring all variants have their data in C layout.")), + }, + } + if !input.is_empty() { + let _: syn::token::Comma = input.parse()?; + } + } + Ok(this) + } +} pub fn stabby( attrs: Vec, @@ -63,7 +90,7 @@ pub fn stabby( ) -> TokenStream { let st = crate::tl_mod(); let unbound_generics = &generics.params; - let mut repr = None; + let mut repr: Option = None; let repr_ident = quote::format_ident!("repr"); let mut new_attrs = Vec::with_capacity(attrs.len()); for a in attrs { @@ -77,6 +104,15 @@ pub fn stabby( new_attrs.push(a) } } + if matches!( + repr, + Some(FullRepr { + repr: Some(Repr::Stabby), + is_c: true + }) + ) { + panic!("#[repr(C)] and #[repr(stabby)] connot be combined") + } if data.variants.is_empty() { todo!("empty enums are not supported by stabby YET") } @@ -87,20 +123,29 @@ pub fn stabby( let mut report = Vec::new(); for variant in variants { match &variant.fields { + syn::Fields::Named(f) if matches!(repr, Some(FullRepr { is_c: true, .. })) => { + todo!("stabby support for named fields is undergoing implementation...") + } syn::Fields::Named(_) => { - panic!("stabby does not support named fields in enum variants") + panic!("stabby only supports named fields in #[repr(C, uX)] enums"); } syn::Fields::Unnamed(f) => { - assert_eq!( - f.unnamed.len(), - 1, - "stabby only supports one field per enum variant" - ); - has_non_empty_fields = true; - let f = f.unnamed.first().unwrap(); - let ty = &f.ty; - layout = quote!(#st::Union<#layout, core::mem::ManuallyDrop<#ty>>); - report.push((variant.ident.to_string(), ty)); + if f.unnamed.len() != 1 && matches!(repr, Some(FullRepr { is_c: true, .. })) { + todo!( + "stabby support for multiple fields per enum is restricted to named fields" + ) + } else { + assert_eq!( + f.unnamed.len(), + 1, + "stabby only supports multiple fields per enum variant in #[repr(C, uX)] enums" + ); + has_non_empty_fields = true; + let f = f.unnamed.first().unwrap(); + let ty = &f.ty; + layout = quote!(#st::Union<#layout, core::mem::ManuallyDrop<#ty>>); + report.push((variant.ident.to_string(), ty)); + } } syn::Fields::Unit => { report.push((variant.ident.to_string(), &unit)); @@ -108,10 +153,13 @@ pub fn stabby( } } let report = crate::report(&report); - let repr = match repr { + let reprstr = match repr + .as_ref() + .and_then(|r| if r.is_c { Some(Repr::C) } else { r.repr }) + { None | Some(Repr::Stabby) => { if !has_non_empty_fields { - panic!("Your enum doesn't have any field with values: use #[repr(C)] or #[repr(u*)] instead") + panic!("Your enum doesn't have any field with values: use #[repr(C)] or (preferably) #[repr(u*)] instead") } return repr_stabby( &new_attrs, @@ -135,13 +183,18 @@ pub fn stabby( Some(Repr::I64) => "i64", Some(Repr::Isize) => "isize", }; - let reprid = quote::format_ident!("{}", repr); + let reprid = quote::format_ident!("{}", reprstr); + let reprattr = if repr.map_or(false, |r| r.is_c) { + quote!(#[repr(C, #reprid)]) + } else { + quote!(#[repr(#reprid)]) + }; layout = quote!(#st::Tuple<#reprid, #layout>); let sident = format!("{ident}"); let (report, report_bounds) = report; quote! { #(#new_attrs)* - #[repr(#reprid)] + #reprattr #vis enum #ident #generics { #variants } @@ -159,7 +212,7 @@ pub fn stabby( module: #st::str::Str::new(core::module_path!()), fields: unsafe {#st::StableLike::new(#report)}, version: 0, - tyty: #st::report::TyTy::Enum(#st::str::Str::new(#repr)), + tyty: #st::report::TyTy::Enum(#st::str::Str::new(#reprstr)), }; const ID: u64 = #st::report::gen_id(Self::REPORT); }