From e0fa79de2f5ec2cac8f3b7bc6758f5d27b7fe093 Mon Sep 17 00:00:00 2001 From: Nelson Earle Date: Tue, 19 Nov 2024 13:20:11 -0600 Subject: [PATCH 1/6] add test --- framework_crates/bones_schema/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework_crates/bones_schema/src/lib.rs b/framework_crates/bones_schema/src/lib.rs index 5798335f35..887587543b 100644 --- a/framework_crates/bones_schema/src/lib.rs +++ b/framework_crates/bones_schema/src/lib.rs @@ -63,6 +63,11 @@ mod test { Set(T), } + #[derive(HasSchema, Clone, Default)] + #[schema_module(crate)] + #[repr(C)] + struct WrapperWithDefault(T); + #[derive(HasSchema, Clone, Copy, Debug, PartialEq, Eq, Default)] #[schema_module(crate)] #[repr(u8)] From 430bdad66762a3da57fe92b1add3bede71ba1414 Mon Sep 17 00:00:00 2001 From: Nelson Earle Date: Mon, 18 Nov 2024 23:58:29 -0600 Subject: [PATCH 2/6] fix(schema): syntax error --- framework_crates/bones_schema/macros/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework_crates/bones_schema/macros/src/lib.rs b/framework_crates/bones_schema/macros/src/lib.rs index 95dd82f7f8..a54ee8e835 100644 --- a/framework_crates/bones_schema/macros/src/lib.rs +++ b/framework_crates/bones_schema/macros/src/lib.rs @@ -282,7 +282,7 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream { let register_schema = if input.generic_params().is_some() { quote! { - static S: OnceLock>> = OnceLock::new(); + static S: OnceLock>> = OnceLock::new(); let schema = { S.get_or_init(Default::default) .read() @@ -377,11 +377,11 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream { quote! { unsafe impl #sync_send_generic_params #schema_mod::HasSchema for #name #generic_params { fn schema() -> &'static #schema_mod::Schema { - use ::std::sync::{OnceLock}; + use ::std::sync::OnceLock; use ::std::any::TypeId; use bones_utils::HashMap; use parking_lot::RwLock; - static S: OnceLock>> = OnceLock::new(); + static S: OnceLock>> = OnceLock::new(); let schema = { S.get_or_init(Default::default) .read() From 28ad040586131eb8a86d152484fe65413af930ac Mon Sep 17 00:00:00 2001 From: Nelson Earle Date: Tue, 19 Nov 2024 13:20:16 -0600 Subject: [PATCH 3/6] feat(schema): support generics --- .../bones_schema/macros/src/lib.rs | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/framework_crates/bones_schema/macros/src/lib.rs b/framework_crates/bones_schema/macros/src/lib.rs index a54ee8e835..27d19e943a 100644 --- a/framework_crates/bones_schema/macros/src/lib.rs +++ b/framework_crates/bones_schema/macros/src/lib.rs @@ -1,7 +1,7 @@ use proc_macro::TokenStream; use proc_macro2::{Punct, Spacing, TokenStream as TokenStream2, TokenTree as TokenTree2}; -use quote::{format_ident, quote, quote_spanned, spanned::Spanned}; -use venial::{GenericBound, StructFields}; +use quote::{format_ident, quote, quote_spanned, spanned::Spanned, TokenStreamExt}; +use venial::StructFields; /// Helper macro to bail out of the macro with a compile error. macro_rules! throw { @@ -350,6 +350,14 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream { } })(); + if match &input { + venial::Declaration::Struct(s) => s.where_clause.is_some(), + venial::Declaration::Enum(e) => e.where_clause.is_some(), + _ => false, + } { + throw!(input, "Where clauses are not supported."); + } + let schema_register = quote! { #schema_mod::registry::SCHEMA_REGISTRY.register(#schema_mod::SchemaData { name: stringify!(#name).into(), @@ -366,16 +374,26 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream { }; if let Some(generic_params) = input.generic_params() { - let mut sync_send_generic_params = generic_params.clone(); - for (param, _) in sync_send_generic_params.params.iter_mut() { - let clone_bound = if !no_clone { quote!(+ Clone) } else { quote!() }; - param.bound = Some(GenericBound { - tk_colon: Punct::new(':', Spacing::Joint), - tokens: quote!(HasSchema #clone_bound ).into_iter().collect(), - }); + let mut impl_bounds = TokenStream2::new(); + for (param, comma) in generic_params.params.iter() { + let name = ¶m.name; + impl_bounds.extend(quote!(#name : HasSchema + Clone)); + if let Some(bound) = ¶m.bound { + impl_bounds.append(Punct::new('+', Spacing::Alone)); + impl_bounds.extend(bound.tokens.iter().cloned()); + } + impl_bounds.append(comma.clone()); } + + let struct_params = generic_params + .params + .items() + .map(|param| ¶m.name) + .map(|name| quote!(#name,)) + .collect::(); + quote! { - unsafe impl #sync_send_generic_params #schema_mod::HasSchema for #name #generic_params { + unsafe impl<#impl_bounds> #schema_mod::HasSchema for #name<#struct_params> { fn schema() -> &'static #schema_mod::Schema { use ::std::sync::OnceLock; use ::std::any::TypeId; From 046a493ceeeaab14a6d1e9ea897b0d09538eb5e8 Mon Sep 17 00:00:00 2001 From: Nelson Earle Date: Tue, 19 Nov 2024 00:18:35 -0600 Subject: [PATCH 4/6] feat(schema): remove need for user crate to depend on `parking_lot` --- framework_crates/bones_schema/macros/src/lib.rs | 14 ++++---------- framework_crates/bones_schema/src/registry.rs | 13 +++++++++++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/framework_crates/bones_schema/macros/src/lib.rs b/framework_crates/bones_schema/macros/src/lib.rs index 27d19e943a..7c368ccf99 100644 --- a/framework_crates/bones_schema/macros/src/lib.rs +++ b/framework_crates/bones_schema/macros/src/lib.rs @@ -282,9 +282,8 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream { let register_schema = if input.generic_params().is_some() { quote! { - static S: OnceLock>> = OnceLock::new(); let schema = { - S.get_or_init(Default::default) + #schema_mod::registry::GENERIC_SCHEMA_CACHE .read() .get(&TypeId::of::()) .copied() @@ -292,7 +291,7 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream { schema.unwrap_or_else(|| { let schema = compute_schema(); - S.get_or_init(Default::default) + #schema_mod::registry::GENERIC_SCHEMA_CACHE .write() .insert(TypeId::of::(), schema); @@ -395,13 +394,9 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream { quote! { unsafe impl<#impl_bounds> #schema_mod::HasSchema for #name<#struct_params> { fn schema() -> &'static #schema_mod::Schema { - use ::std::sync::OnceLock; use ::std::any::TypeId; - use bones_utils::HashMap; - use parking_lot::RwLock; - static S: OnceLock>> = OnceLock::new(); let schema = { - S.get_or_init(Default::default) + #schema_mod::registry::GENERIC_SCHEMA_CACHE .read() .get(&TypeId::of::()) .copied() @@ -409,13 +404,12 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream { schema.unwrap_or_else(|| { let schema = #schema_register; - S.get_or_init(Default::default) + #schema_mod::registry::GENERIC_SCHEMA_CACHE .write() .insert(TypeId::of::(), schema); schema }) - } } } diff --git a/framework_crates/bones_schema/src/registry.rs b/framework_crates/bones_schema/src/registry.rs index 76b6dcd7e3..ab7c2f6f72 100644 --- a/framework_crates/bones_schema/src/registry.rs +++ b/framework_crates/bones_schema/src/registry.rs @@ -2,11 +2,16 @@ use std::{ alloc::Layout, - sync::atomic::{AtomicU32, Ordering::SeqCst}, + any::TypeId, + sync::{ + atomic::{AtomicU32, Ordering::SeqCst}, + LazyLock, + }, }; use append_only_vec::AppendOnlyVec; -use bones_utils::Deref; +use bones_utils::{Deref, HashMap}; +use parking_lot::RwLock; use crate::prelude::*; @@ -143,6 +148,10 @@ pub static SCHEMA_REGISTRY: SchemaRegistry = SchemaRegistry { schemas: AppendOnlyVec::new(), }; +#[doc(hidden)] +pub static GENERIC_SCHEMA_CACHE: LazyLock>> = + LazyLock::new(Default::default); + #[cfg(test)] mod test { use bones_utils::default; From df3e274c6d68e3f08ced7ae112ce2dc93f8dce52 Mon Sep 17 00:00:00 2001 From: Nelson Earle Date: Tue, 19 Nov 2024 20:47:29 -0600 Subject: [PATCH 5/6] fix(schema): add variant name to `GENERIC_SCHEMA_CACHE` to differentiate enum variants --- framework_crates/bones_schema/macros/src/lib.rs | 8 ++++---- framework_crates/bones_schema/src/registry.rs | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/framework_crates/bones_schema/macros/src/lib.rs b/framework_crates/bones_schema/macros/src/lib.rs index 7c368ccf99..0929a32ebe 100644 --- a/framework_crates/bones_schema/macros/src/lib.rs +++ b/framework_crates/bones_schema/macros/src/lib.rs @@ -285,7 +285,7 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream { let schema = { #schema_mod::registry::GENERIC_SCHEMA_CACHE .read() - .get(&TypeId::of::()) + .get(&(TypeId::of::(), #variant_schema_name)) .copied() }; schema.unwrap_or_else(|| { @@ -293,7 +293,7 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream { #schema_mod::registry::GENERIC_SCHEMA_CACHE .write() - .insert(TypeId::of::(), schema); + .insert((TypeId::of::(), #variant_schema_name), schema); schema }) @@ -398,7 +398,7 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream { let schema = { #schema_mod::registry::GENERIC_SCHEMA_CACHE .read() - .get(&TypeId::of::()) + .get(&(TypeId::of::(), stringify!(#name))) .copied() }; schema.unwrap_or_else(|| { @@ -406,7 +406,7 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream { #schema_mod::registry::GENERIC_SCHEMA_CACHE .write() - .insert(TypeId::of::(), schema); + .insert((TypeId::of::(), stringify!(name)), schema); schema }) diff --git a/framework_crates/bones_schema/src/registry.rs b/framework_crates/bones_schema/src/registry.rs index ab7c2f6f72..da589d79e7 100644 --- a/framework_crates/bones_schema/src/registry.rs +++ b/framework_crates/bones_schema/src/registry.rs @@ -149,8 +149,9 @@ pub static SCHEMA_REGISTRY: SchemaRegistry = SchemaRegistry { }; #[doc(hidden)] -pub static GENERIC_SCHEMA_CACHE: LazyLock>> = - LazyLock::new(Default::default); +pub static GENERIC_SCHEMA_CACHE: LazyLock< + RwLock>, +> = LazyLock::new(Default::default); #[cfg(test)] mod test { From 6c838c795347926716edac818cea48c2f2170df0 Mon Sep 17 00:00:00 2001 From: Nelson Earle Date: Sat, 23 Nov 2024 16:58:39 -0600 Subject: [PATCH 6/6] fix(schema): don't add `Clone` bound to the `impl HasSchema` when `no_clone` --- framework_crates/bones_schema/macros/src/lib.rs | 8 ++++++-- framework_crates/bones_schema/src/lib.rs | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/framework_crates/bones_schema/macros/src/lib.rs b/framework_crates/bones_schema/macros/src/lib.rs index 0929a32ebe..efda2c13d5 100644 --- a/framework_crates/bones_schema/macros/src/lib.rs +++ b/framework_crates/bones_schema/macros/src/lib.rs @@ -1,5 +1,5 @@ use proc_macro::TokenStream; -use proc_macro2::{Punct, Spacing, TokenStream as TokenStream2, TokenTree as TokenTree2}; +use proc_macro2::{Ident, Punct, Spacing, TokenStream as TokenStream2, TokenTree as TokenTree2}; use quote::{format_ident, quote, quote_spanned, spanned::Spanned, TokenStreamExt}; use venial::StructFields; @@ -376,7 +376,11 @@ pub fn derive_has_schema(input: TokenStream) -> TokenStream { let mut impl_bounds = TokenStream2::new(); for (param, comma) in generic_params.params.iter() { let name = ¶m.name; - impl_bounds.extend(quote!(#name : HasSchema + Clone)); + impl_bounds.extend(quote!(#name : HasSchema)); + if !no_clone { + impl_bounds.append(Punct::new('+', Spacing::Alone)); + impl_bounds.append(Ident::new("Clone", input.__span())); + } if let Some(bound) = ¶m.bound { impl_bounds.append(Punct::new('+', Spacing::Alone)); impl_bounds.extend(bound.tokens.iter().cloned()); diff --git a/framework_crates/bones_schema/src/lib.rs b/framework_crates/bones_schema/src/lib.rs index 887587543b..4a8eef3a75 100644 --- a/framework_crates/bones_schema/src/lib.rs +++ b/framework_crates/bones_schema/src/lib.rs @@ -168,5 +168,22 @@ mod test { assert_eq!(A::schema().layout(), B::schema().layout()); assert_eq!(C::schema().layout(), D::schema().layout()); } + + #[test] + fn generic_no_clone() { + #[derive(HasSchema, Default)] + #[schema(no_clone)] + #[schema_module(crate)] + #[repr(C)] + struct Meta(T); + + #[derive(HasSchema, Default)] + #[schema(no_clone)] + #[schema_module(crate)] + #[repr(C)] + struct NotClonable; + + _ = Meta::::schema(); + } } }