From f5d4d7dbd7e51117df4dbb7243ae171bc1032987 Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Mon, 15 Jul 2024 20:57:17 +0200 Subject: [PATCH 1/5] Add ability to emit type aliases to C headers --- src/headers/_mod.rs | 2 -- src/headers/languages/c.rs | 29 +++++++++++++++++++++++++++++ src/headers/languages/mod.rs | 20 +++++++++++++++++++- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/headers/_mod.rs b/src/headers/_mod.rs index e4b2ae55f1..8bcd974bee 100644 --- a/src/headers/_mod.rs +++ b/src/headers/_mod.rs @@ -117,8 +117,6 @@ mod languages; pub use definer::{Definer, HashSetDefiner}; mod definer; - - match_! {( /// Sets up the name of the `ifndef` guard of the header file. /// diff --git a/src/headers/languages/c.rs b/src/headers/languages/c.rs index 710998e212..e5df5c82a7 100644 --- a/src/headers/languages/c.rs +++ b/src/headers/languages/c.rs @@ -30,6 +30,35 @@ impl HeaderLanguage for C { Ok(()) } + fn supports_type_aliases(self: &'_ C) + -> Option<&'_ dyn HeaderLanguageSupportingTypeAliases> + { + return Some(self); + // where + impl HeaderLanguageSupportingTypeAliases for C { + fn emit_type_alias( + self: &'_ Self, + ctx: &'_ mut dyn Definer, + docs: Docs<'_>, + self_ty: &'_ dyn PhantomCType, + inner_ty: &'_ dyn PhantomCType, + ) -> io::Result<()> + { + let ref indent = Indentation::new(4 /* ctx.indent_width() */); + mk_out!(indent, ctx.out()); + self.emit_docs(ctx, docs, indent)?; + let ref aliaser = self_ty.name(self); + let ref aliasee = inner_ty.name(self); + out!(( + "typedef {aliasee} {aliaser};" + )); + + out!("\n"); + Ok(()) + } + } + } + fn emit_simple_enum ( self: &'_ Self, ctx: &'_ mut dyn Definer, diff --git a/src/headers/languages/mod.rs b/src/headers/languages/mod.rs index a151b7d8e7..81b7c7e2fd 100644 --- a/src/headers/languages/mod.rs +++ b/src/headers/languages/mod.rs @@ -64,10 +64,16 @@ type Docs<'lt> = &'lt [&'lt str]; pub trait HeaderLanguage : UpcastAny { - fn language_name(&self) -> &'static str { + fn language_name(self: &'_ Self) -> &'static str { ::core::any::type_name::() } + fn supports_type_aliases(self: &'_ Self) + -> Option<&'_ dyn HeaderLanguageSupportingTypeAliases> + { + None + } + fn emit_simple_enum ( self: &'_ Self, ctx: &'_ mut dyn Definer, @@ -128,6 +134,18 @@ trait HeaderLanguage : UpcastAny { } } +pub +trait HeaderLanguageSupportingTypeAliases : HeaderLanguage { + fn emit_type_alias( + self: &'_ Self, + ctx: &'_ mut dyn Definer, + docs: Docs<'_>, + self_ty: &'_ dyn PhantomCType, + inner_ty: &'_ dyn PhantomCType, + ) -> io::Result<()> + ; +} + pub struct EnumVariant<'lt> { pub From bade6ec6b15c456a8de79bb0835b4d0e75e373a5 Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Mon, 15 Jul 2024 21:03:26 +0200 Subject: [PATCH 2/5] =?UTF-8?q?Add=20support=20for=20`#[derive=5FReprC(ren?= =?UTF-8?q?ame=E2=80=A6]][repr(transparent)]`=20C=20type=20aliases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/proc_macro/derives/c_type/struct_.rs | 243 +++++++++++++++++++---- src/proc_macro/derives/repr_c/struct_.rs | 111 ++++++++--- 2 files changed, 290 insertions(+), 64 deletions(-) diff --git a/src/proc_macro/derives/c_type/struct_.rs b/src/proc_macro/derives/c_type/struct_.rs index 9515fff1b9..f4d5a28c57 100644 --- a/src/proc_macro/derives/c_type/struct_.rs +++ b/src/proc_macro/derives/c_type/struct_.rs @@ -12,8 +12,25 @@ fn derive ( fields: &'_ Fields, ) -> Result { - if matches!(fields, Fields::Unnamed { .. } | Fields::Unit { .. }) { - bail!("only braced structs are supported"); + if let Some(repr) = attrs.iter().find_map(|attr| { + bool::then( + attr.path.is_ident("repr"), + || attr.parse_args::().ok() + ).flatten() + }) + { + if repr.to_string() == "transparent" { + return derive_transparent( + args, + attrs, + pub_, + StructName, + generics, + fields, + ); + } + } else { + bail!("Missing `#[repr]`!"); } #[apply(let_quote!)] @@ -30,6 +47,10 @@ fn derive ( let mut ret = quote!(); + if matches!(fields, Fields::Unnamed { .. } | Fields::Unit { .. }) { + bail!("only braced structs are supported"); + } + if cfg!(feature = "js") && args.js.is_some() { // invoke the legacy `CType!` macro which is the one currently featuring // the js FFI glue generating logic. @@ -127,66 +148,206 @@ fn derive ( } ret.extend({ - let (intro_generics, fwd_generics, where_clauses) = - generics.split_for_impl() - ; + let (intro_generics, fwd_generics, where_clauses) = &generics.split_for_impl(); + + let trivial_impls = trivial_impls(intro_generics, fwd_generics, where_clauses, StructName); quote!( + unsafe impl #intro_generics - #ඞ::Clone + #CType for #StructName #fwd_generics #where_clauses { - #[inline] - fn clone (self: &'_ Self) - -> Self + #impl_body + } + + #trivial_impls + ) + }); + + // #[cfg(feature = "js")] { + // ret.extend(super::js::handle(/* … */)?); + // } + + Ok(ret) +} + +pub(in crate) +fn derive_transparent ( + args: Args, + attrs: &'_ [Attribute], + _pub: &'_ Visibility, + StructName_Layout @ _: &'_ Ident, + generics: &'_ Generics, + fields: &'_ Fields, +) -> Result +{ + // Example input: + #[cfg(any())] + #[derive_CType(js, rename = "dittoffi_string")] + #[repr(transparent)] + struct FfiString_Layout( + CLayoutOf, + ) + where + char_p::Box : ReprC, + ; + + #[apply(let_quote)] + use ::safer_ffi::ඞ; + + let mut ret = quote!(); + + let Args { rename, .. } = &args; + + let docs = utils::extract_docs(attrs)?; + + let CFieldTy @ _ = match fields.iter().next() { + | Some(f) => &f.ty, + | None => bail! { + "`#[repr(transparent)]` requires at least one field" => fields, + }, + }; + + let (intro_generics, fwd_generics, where_clauses) = &generics.split_for_impl(); + + ret.extend(quote!( + unsafe + impl #intro_generics + #ඞ::CType + for + #StructName_Layout #fwd_generics + #where_clauses + { + type OPAQUE_KIND = <#CFieldTy as #ඞ::CType>::OPAQUE_KIND; + + ::safer_ffi::__cfg_headers__! { + fn short_name () + -> #ඞ::String { - *self + #ඞ::String::from(#rename) } - } - impl #intro_generics - #ඞ::Copy - for - #StructName #fwd_generics - #where_clauses - {} + #[allow(nonstandard_style)] + fn define_self__impl ( + language: &'_ dyn #ඞ::HeaderLanguage, + definer: &'_ mut dyn #ඞ::Definer, + ) -> #ඞ::io::Result<()> + { + <#CFieldTy as #ඞ::CType>::define_self(language, definer)?; + if let #ඞ::Some(language) = language.supports_type_aliases() { + language.emit_type_alias( + definer, + &[#(#docs),*], + &#ඞ::PhantomData::, + &#ඞ::PhantomData::<#CFieldTy>, + )?; + } - unsafe - impl #intro_generics - #CType - for - #StructName #fwd_generics - #where_clauses - { - #impl_body + Ok(()) + } + + fn name ( + language: &'_ dyn #ඞ::HeaderLanguage, + ) -> String + { + if let #ඞ::Some(language) = language.supports_type_aliases() { + #ඞ::std::format!("{}_t", Self::short_name()) + } else { + <#CFieldTy as #ඞ::CType>::name(language) + } + } } + } + )); - // If it is CType, it trivially is ReprC. - unsafe + ret.extend(trivial_impls(intro_generics, fwd_generics, where_clauses, StructName_Layout)); + + if cfg!(feature = "js") && args.js.is_some() { + #[apply(let_quote)] + use ::safer_ffi::js; + + ret.extend(quote!( impl #intro_generics - #ReprC + #js::ReprNapi for - #StructName #fwd_generics + #StructName_Layout #fwd_generics #where_clauses { - type CLayout = Self; + type NapiValue = <#CFieldTy as #js::ReprNapi>::NapiValue; - #[inline] - fn is_valid ( - _: &'_ Self::CLayout, - ) -> #ඞ::bool + fn to_napi_value ( + self: Self, + env: &'_ #js::Env, + ) -> #js::Result< Self::NapiValue > { - true + <#CFieldTy as #js::ReprNapi>::to_napi_value(self.0, env) } - } - ) - }); - // #[cfg(feature = "js")] { - // ret.extend(super::js::handle(/* … */)?); - // } + fn from_napi_value ( + env: &'_ #js::Env, + napi_value: Self::NapiValue, + ) -> #js::Result + { + <#CFieldTy as #js::ReprNapi>::from_napi_value(env, napi_value).map(Self) + } + } + )); + } Ok(ret) } + +fn trivial_impls( + intro_generics: &dyn ToTokens, + fwd_generics: &dyn ToTokens, + where_clauses: &dyn ToTokens, + StructName @ _: &dyn ToTokens, +) -> TokenStream2 { + #[apply(let_quote)] + use ::safer_ffi::ඞ; + + quote!( + impl #intro_generics + #ඞ::Clone + for + #StructName #fwd_generics + #where_clauses + { + #[inline] + fn clone (self: &'_ Self) + -> Self + { + *self + } + } + + impl #intro_generics + #ඞ::Copy + for + #StructName #fwd_generics + #where_clauses + {} + + // If it is CType, it trivially is ReprC. + unsafe + impl #intro_generics + #ඞ::ReprC + for + #StructName #fwd_generics + #where_clauses + { + type CLayout = Self; + + #[inline] + fn is_valid ( + _: &'_ Self::CLayout, + ) -> #ඞ::bool + { + true + } + } + ) +} diff --git a/src/proc_macro/derives/repr_c/struct_.rs b/src/proc_macro/derives/repr_c/struct_.rs index 1b98bc7315..b859956318 100644 --- a/src/proc_macro/derives/repr_c/struct_.rs +++ b/src/proc_macro/derives/repr_c/struct_.rs @@ -116,15 +116,12 @@ fn derive ( | None => format_ident!("_{}", i), } }); - let each_docs = fields.iter().map(|f| { - f .attrs - .iter() - .filter(|attr| attr.path.is_ident("doc")) - .vec() + let each_field_docs = fields.iter().map(|f| { + docs_of(&f.attrs).vec() }); parse_quote!({ #( - #(#each_docs)* + #(#each_field_docs)* pub #each_field_name: #CLayoutOf<#EachFieldTy> ),* @@ -227,7 +224,7 @@ pub(in crate) fn derive_transparent ( args: Args, attrs: &'_ mut Vec, - _vis: &'_ Visibility, + pub_: &'_ Visibility, StructName @ _: &'_ Ident, generics: &'_ Generics, fields: &'_ Fields, @@ -238,13 +235,6 @@ fn derive_transparent ( let mut ret = quote!(); - if let Some(js) = &args.js { - ret.extend(utils::compile_warning( - js, - "`js` annotation is ignored for `repr(transparent)`", - )); - } - let FieldTy = match fields.iter().next() { | Some(f) => &f.ty, | None => bail! { @@ -261,12 +251,84 @@ fn derive_transparent ( ; }); - // Forward ReprC to point to the `CLayoutOf` its first type. - ret.extend({ - let (intro_generics, fwd_generics, where_clauses) = - impl_generics.split_for_impl() - ; - quote!( + let (intro_generics, fwd_generics, where_clauses) = + impl_generics.split_for_impl() + ; + + let inner; + if let Some(rename) = &args.rename { + // define the CType + ret.extend({ + let ref StructName_Layout @ _ = format_ident!("{}_Layout", StructName); + + let c_type_def = ItemStruct { + attrs: + docs_of(attrs) + .cloned() + .chain([ + parse_quote!( + #[repr(transparent)] + ), + parse_quote!( + #[allow(nonstandard_style)] + ), + ]) + .collect() + , + vis: { + let pub_ = crate::respan( + pub_.span().resolved_at(Span::mixed_site()), + pub_.to_token_stream(), + ); + parse_quote!(#pub_) + }, + struct_token: parse_quote!(struct), + ident: StructName_Layout.clone(), + generics: impl_generics.clone(), + fields: Fields::Unnamed(parse_quote!(( + #ඞ::CLayoutOf<#FieldTy> + ))), + semi_token: Some(parse_quote!( + ; + )), + }; + + // allow using `#()*` as `#()?`. + let js = args.js.as_ref().map_or(&[][..], ::core::slice::from_ref); + + let derive_output = crate::derives::c_type::derive( + quote!( + #(#js, )* + rename = #rename, + ), + c_type_def.to_token_stream(), + )?; + + quote!( + #derive_output + + unsafe + impl #intro_generics + #ඞ::ReprC + for + #StructName #fwd_generics + #where_clauses + { + type CLayout = #StructName_Layout; + + #[inline] + fn is_valid (it: &'_ Self::CLayout) + -> #ඞ::bool + { + <#FieldTy as #ඞ::ReprC>::is_valid(&it.0) + } + } + ) + }); + inner = quote!(&it.0); + } else { + // Forward ReprC to point to the `CLayoutOf` its first type. + ret.extend(quote!( unsafe impl #intro_generics #ඞ::ReprC @@ -283,8 +345,11 @@ fn derive_transparent ( <#FieldTy as #ඞ::ReprC>::is_valid(it) } } - ) - }); + )); + inner = quote!(it); + } + + // let mut ret = debug_macro(ret); // add niche where applicable. ret.extend({ @@ -315,7 +380,7 @@ fn derive_transparent ( #FieldTy as #ඞ::__HasNiche__ - >::is_niche(it) + >::is_niche(#inner) } } ) From 32b7c3cb0444955eaf284804cc21ca2a60d2eebb Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Mon, 15 Jul 2024 21:05:23 +0200 Subject: [PATCH 3/5] Add FFI example/test usage --- ffi_tests/generated.cffi | 3 +++ ffi_tests/generated.cs | 5 +++++ ffi_tests/generated.h | 7 +++++++ ffi_tests/src/lib.rs | 15 +++++++++++++++ ffi_tests/tests/c/main.c | 2 ++ ffi_tests/tests/csharp/Tests.cs | 4 ++++ 6 files changed, 36 insertions(+) diff --git a/ffi_tests/generated.cffi b/ffi_tests/generated.cffi index e9bbb3a84e..3cfac5c075 100644 --- a/ffi_tests/generated.cffi +++ b/ffi_tests/generated.cffi @@ -115,6 +115,9 @@ int32_t const * max ( slice_ref_int32_t xs); +void * +my_renamed_ptr_api (void); + foo_t * new_foo (void); diff --git a/ffi_tests/generated.cs b/ffi_tests/generated.cs index c2c8cd5e5a..e5fe001d13 100644 --- a/ffi_tests/generated.cs +++ b/ffi_tests/generated.cs @@ -244,6 +244,11 @@ public unsafe partial class Ffi { slice_ref_int32_t xs); } +public unsafe partial class Ffi { + [DllImport(RustLib, ExactSpelling = true)] public static unsafe extern + void * my_renamed_ptr_api (); +} + public unsafe partial class Ffi { [DllImport(RustLib, ExactSpelling = true)] public static unsafe extern foo_t * new_foo (); diff --git a/ffi_tests/generated.h b/ffi_tests/generated.h index cb6bf0eb5e..90e1fd98e2 100644 --- a/ffi_tests/generated.h +++ b/ffi_tests/generated.h @@ -229,6 +229,13 @@ int32_t const * max ( slice_ref_int32_t xs); +/** */ +typedef void * my_renamed_ptr_t; + +/** */ +my_renamed_ptr_t +my_renamed_ptr_api (void); + /** */ foo_t * new_foo (void); diff --git a/ffi_tests/src/lib.rs b/ffi_tests/src/lib.rs index ec7c3a7648..fedc66b9ea 100644 --- a/ffi_tests/src/lib.rs +++ b/ffi_tests/src/lib.rs @@ -189,6 +189,21 @@ pub struct MyPtr { bar: (), } +#[derive_ReprC(rename = "my_renamed_ptr")] +#[repr(transparent)] +pub struct MyRenamedPtr { + foo: ::core::ptr::NonNull<()>, + bar: (), +} + +#[ffi_export] +fn my_renamed_ptr_api() -> MyRenamedPtr { + MyRenamedPtr { + foo: ::core::ptr::NonNull::new(0xbad000 as _).unwrap(), + bar: () + } +} + macro_rules! docs {() => ( "Hello, `World`!" )} diff --git a/ffi_tests/tests/c/main.c b/ffi_tests/tests/c/main.c index 02c9d8fe08..4258964bd9 100644 --- a/ffi_tests/tests/c/main.c +++ b/ffi_tests/tests/c/main.c @@ -128,6 +128,8 @@ int main ( }); while (X > 0); + assert(my_renamed_ptr_api() == (void *)0xbad000); + puts("C: [ok]"); return EXIT_SUCCESS; diff --git a/ffi_tests/tests/csharp/Tests.cs b/ffi_tests/tests/csharp/Tests.cs index 63f5a3b570..51435e588f 100644 --- a/ffi_tests/tests/csharp/Tests.cs +++ b/ffi_tests/tests/csharp/Tests.cs @@ -132,6 +132,10 @@ static void Main(string[] _) Trace.Assert(Ffi.returns_a_fn_ptr()(0x42) == 0x4200); } + unsafe { + Trace.Assert(Ffi.my_renamed_ptr_api() == (void *)0xbad000); + } + Console.WriteLine("C#: [ok]"); } } From c2422edb6360ed5a4b1fab1d61efd478acdd7455 Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Mon, 15 Jul 2024 21:06:01 +0200 Subject: [PATCH 4/5] Add js example/test usage --- js_tests/src/lib.rs | 15 ++++++++++++++ js_tests/tests/tests.mjs | 44 +++++++++++++++++++++------------------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/js_tests/src/lib.rs b/js_tests/src/lib.rs index e1ffe4a4ad..1b6e916d9a 100644 --- a/js_tests/src/lib.rs +++ b/js_tests/src/lib.rs @@ -359,3 +359,18 @@ fn sleep (ms: u32) let _ = wasm_bindgen_futures::JsFuture::from(sleep(ms)).await; } } + +#[derive_ReprC(js, rename = "my_renamed_ptr")] +#[repr(transparent)] +pub struct MyRenamedPtr { + pub foo: ::core::ptr::NonNull<()>, + pub bar: (), +} + +#[ffi_export(js)] +fn my_renamed_ptr_api() -> MyRenamedPtr { + MyRenamedPtr { + foo: ::core::ptr::NonNull::new(0xbad000 as _).unwrap(), + bar: (), + } +} diff --git a/js_tests/tests/tests.mjs b/js_tests/tests/tests.mjs index 9e2ea755c4..9d0fc52340 100644 --- a/js_tests/tests/tests.mjs +++ b/js_tests/tests/tests.mjs @@ -284,29 +284,31 @@ export async function run_tests({ ffi, performance, assert, is_web }) { ) } - if (!is_web) { - assert.equal(ffi.getDeadlockTimeout(), 5000); - - let error; - try { - ffi.setDeadlockTimeout("huehuehue"); - } catch (e) { - error = e; + if (!is_web) { + assert.equal(ffi.getDeadlockTimeout(), 5000); + + let error; + try { + ffi.setDeadlockTimeout("huehuehue"); + } catch (e) { + error = e; + } + assert.equal(error?.message, "Expected a positive number"); + + ffi.setDeadlockTimeout(500); + + assert.equal(ffi.getDeadlockTimeout(), 500); + + error = null; + try { + ffi.setDeadlockTimeout(0); + } catch (e) { + error = e; + } + assert.equal(error?.message, "Deadlock timeout can only be set once"); } - assert.equal(error?.message, "Expected a positive number"); - - ffi.setDeadlockTimeout(500); - assert.equal(ffi.getDeadlockTimeout(), 500); - - error = null; - try { - ffi.setDeadlockTimeout(0); - } catch (e) { - error = e; - } - assert.equal(error?.message, "Deadlock timeout can only be set once"); - } + assert.equal(ffi.my_renamed_ptr_api().addr, 0xbad000); console.log('Js tests passed successfully ✅'); } From 79f0dbd68850fad23830730516c97f903b55e704 Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Mon, 15 Jul 2024 21:23:36 +0200 Subject: [PATCH 5/5] Version 0.1.10 release candidate --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- ffi_tests/Cargo.lock | 4 ++-- js_tests/Cargo.lock | 4 ++-- src/proc_macro/Cargo.toml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 146efc5320..96e7430a41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -622,7 +622,7 @@ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "safer-ffi" -version = "0.1.9" +version = "0.1.10-rc1" dependencies = [ "async-compat", "cratesio-placeholder-package", @@ -654,7 +654,7 @@ dependencies = [ [[package]] name = "safer_ffi-proc_macros" -version = "0.1.9" +version = "0.1.10-rc1" dependencies = [ "macro_rules_attribute", "prettyplease", diff --git a/Cargo.toml b/Cargo.toml index 3b18fc0f76..dea8b9fd6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ path = "src/_lib.rs" [package] name = "safer-ffi" -version = "0.1.9" # Keep in sync +version = "0.1.10-rc1" # Keep in sync authors = [ "Daniel Henry-Mantilla ", ] @@ -179,7 +179,7 @@ version = "0.0.3" [dependencies.safer_ffi-proc_macros] path = "src/proc_macro" -version = "=0.1.9" # Keep in sync +version = "=0.1.10-rc1" # Keep in sync [workspace] members = [ diff --git a/ffi_tests/Cargo.lock b/ffi_tests/Cargo.lock index 72af13c357..dc956a99d2 100644 --- a/ffi_tests/Cargo.lock +++ b/ffi_tests/Cargo.lock @@ -331,7 +331,7 @@ checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "safer-ffi" -version = "0.1.9" +version = "0.1.10-rc1" dependencies = [ "futures", "inventory", @@ -348,7 +348,7 @@ dependencies = [ [[package]] name = "safer_ffi-proc_macros" -version = "0.1.9" +version = "0.1.10-rc1" dependencies = [ "macro_rules_attribute", "prettyplease", diff --git a/js_tests/Cargo.lock b/js_tests/Cargo.lock index 085a230ab7..f1d701c301 100644 --- a/js_tests/Cargo.lock +++ b/js_tests/Cargo.lock @@ -629,7 +629,7 @@ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "safer-ffi" -version = "0.1.9" +version = "0.1.10-rc1" dependencies = [ "cratesio-placeholder-package", "inventory", @@ -654,7 +654,7 @@ dependencies = [ [[package]] name = "safer_ffi-proc_macros" -version = "0.1.9" +version = "0.1.10-rc1" dependencies = [ "macro_rules_attribute", "prettyplease", diff --git a/src/proc_macro/Cargo.toml b/src/proc_macro/Cargo.toml index 68fa909c23..2fc10df7c1 100644 --- a/src/proc_macro/Cargo.toml +++ b/src/proc_macro/Cargo.toml @@ -4,7 +4,7 @@ proc-macro = true [package] name = "safer_ffi-proc_macros" -version = "0.1.9" # Keep in sync +version = "0.1.10-rc1" # Keep in sync authors = ["Daniel Henry-Mantilla "] edition = "2021"