1
1
use darling:: { Error , FromMeta , FromGenerics , FromTypeParam , FromDeriveInput , FromVariant , FromField } ;
2
2
use proc_macro2:: { Ident , TokenStream , Span } ;
3
- use syn:: { Generics , Type , GenericParam , TypeParam , Lifetime , Path , parse_quote , PathArguments , GenericArgument , TypePath , Meta , LifetimeDef } ;
3
+ use syn:: { GenericArgument , GenericParam , Generics , Lifetime , LifetimeDef , LitStr , Meta , Path , PathArguments , Type , TypeParam , TypePath , parse_quote } ;
4
4
use darling:: util:: { SpannedValue } ;
5
5
use quote:: { quote_spanned, quote, format_ident, ToTokens } ;
6
6
use darling:: ast:: { Style , Data } ;
@@ -189,7 +189,8 @@ struct TraceField {
189
189
/// to be traced.
190
190
#[ darling( default ) ]
191
191
unsafe_skip_trace : bool ,
192
-
192
+ #[ darling( default , rename = "serde" ) ]
193
+ serde_opts : Option < SerdeFieldOpts > ,
193
194
#[ darling( forward_attrs( serde) ) ]
194
195
attrs : Vec < syn:: Attribute >
195
196
}
@@ -249,7 +250,64 @@ impl TraceVariant {
249
250
}
250
251
}
251
252
252
- #[ derive( FromDeriveInput ) ]
253
+ /// Custom `#[serde(bound(deserialize = ""))]
254
+ #[ derive( Debug , Clone , FromMeta ) ]
255
+ struct CustomSerdeBounds {
256
+ /// The custom deserialize bound
257
+ deserialize : LitStr
258
+ }
259
+
260
+ /// Options for `#[zerogc(serde)]` on a type
261
+ #[ derive( Debug , Clone , Default , FromMeta ) ]
262
+ struct SerdeTypeOpts {
263
+ /// Delegate directly to the `Deserialize` implementation,
264
+ /// without generating a wrapper.
265
+ ///
266
+ /// Effectively calls `zerogc::derive_delegating_deserialize!`
267
+ ///
268
+ /// Requires `Self: serde::Deserialize`
269
+ ///
270
+ /// If this is present,
271
+ /// then all other options are ignored.
272
+ #[ darling( default ) ]
273
+ delegate : bool ,
274
+ /// Override the inferred bounds
275
+ ///
276
+ /// Equivalent to `#[serde(bound(....))]`
277
+ #[ darling( default , rename = "bound" ) ]
278
+ custom_bounds : Option < CustomSerdeBounds > ,
279
+ }
280
+
281
+ #[ derive( Debug , Clone , Default , FromMeta ) ]
282
+ struct SerdeFieldOpts {
283
+ /// Delegate to the `serde::Deserialize`
284
+ /// implementation instead of using `GcDeserialize`
285
+ ///
286
+ /// If this option is present,
287
+ /// then all other options are ignored.
288
+ #[ darling( default ) ]
289
+ delegate : bool ,
290
+ /// Override the inferred bounds for the field.
291
+ #[ darling( default , rename = "bound" ) ]
292
+ custom_bounds : Option < CustomSerdeBounds > ,
293
+ /// Deserialize this field using a custom
294
+ /// deserialization function.
295
+ ///
296
+ /// Equivalent to `#[serde(deserialize_with = "...")]`
297
+ #[ darling( default ) ]
298
+ deserialize_with : Option < LitStr > ,
299
+ /// Skip deserializing this field.
300
+ ///
301
+ /// Equivalent to `#[serde(skip_deserializing)]`.
302
+ ///
303
+ /// May choose to override the default with a
304
+ /// regular `#[serde(default = "...")]`
305
+ /// (but not with the #[zerogc(serde(...))])` syntax)
306
+ #[ darling( default ) ]
307
+ skip_deserializing : bool
308
+ }
309
+
310
+ #[ derive( Debug , FromDeriveInput ) ]
253
311
#[ darling( attributes( zerogc) ) ]
254
312
pub struct TraceDeriveInput {
255
313
pub ident : Ident ,
@@ -275,6 +333,8 @@ pub struct TraceDeriveInput {
275
333
/// If the type should implement `TraceImmutable` in addition to `Trace
276
334
#[ darling( default , rename = "immutable" ) ]
277
335
wants_immutable_trace : bool ,
336
+ #[ darling( default , rename = "serde" ) ]
337
+ serde_opts : Option < SerdeTypeOpts > ,
278
338
#[ darling( forward_attrs( serde) ) ]
279
339
attrs : Vec < syn:: Attribute >
280
340
}
@@ -437,14 +497,17 @@ impl TraceDeriveInput {
437
497
generics, TraceDeriveKind :: Deserialize , gc_lifetime,
438
498
& mut |kind, initial, id, gc_lt| {
439
499
assert ! ( matches!( kind, TraceDeriveKind :: Deserialize ) ) ;
500
+ let type_opts = self . serde_opts . clone ( ) . unwrap_or_default ( ) ;
440
501
let mut generics = initial. unwrap ( ) ;
441
502
let id_is_generic = generics. type_params ( )
442
503
. any ( |param| id. is_ident ( & param. ident ) ) ;
443
504
generics. params . push ( parse_quote ! ( ' deserialize) ) ;
444
505
let requirement = quote ! ( for <' deser2> zerogc:: serde:: GcDeserialize :: <#gc_lt, ' deser2, #id>) ;
445
- for target in self . generics . regular_type_params ( ) {
446
- let target = & target. ident ;
447
- generics. make_where_clause ( ) . predicates . push ( parse_quote ! ( #target: #requirement) ) ;
506
+ if !type_opts. delegate {
507
+ for target in self . generics . regular_type_params ( ) {
508
+ let target = & target. ident ;
509
+ generics. make_where_clause ( ) . predicates . push ( parse_quote ! ( #target: #requirement) ) ;
510
+ }
448
511
}
449
512
let ty_generics = self . generics . original . split_for_impl ( ) . 1 ;
450
513
let ( impl_generics, _, where_clause) = generics. split_for_impl ( ) ;
@@ -454,13 +517,32 @@ impl TraceDeriveInput {
454
517
let named = f. ident . as_ref ( ) . map ( |name| quote ! ( #name: ) ) ;
455
518
let ty = & f. ty ;
456
519
let forwarded_attrs = & f. attrs ;
457
- let bound = format ! (
458
- "{}: for<'deserialize> zerogc::serde::GcDeserialize<{}, 'deserialize, {}>" , ty. to_token_stream( ) ,
459
- gc_lt. to_token_stream( ) , id. to_token_stream( )
460
- ) ;
520
+ let serde_opts = f. serde_opts . clone ( ) . unwrap_or_default ( ) ;
521
+ let serde_attr = if serde_opts. delegate {
522
+ quote ! ( )
523
+ } else {
524
+ let deserialize_with = serde_opts. deserialize_with . as_ref ( ) . map_or_else (
525
+ || String :: from ( "deserialize_hack" ) ,
526
+ |with| with. value ( )
527
+ ) ;
528
+ let custom_bound = if serde_opts. skip_deserializing || serde_opts. deserialize_with . is_some ( ) {
529
+ quote ! ( )
530
+ } else {
531
+ let bound = serde_opts. custom_bounds
532
+ . as_ref ( ) . map_or_else (
533
+ || format ! (
534
+ "{}: for<'deserialize> zerogc::serde::GcDeserialize<{}, 'deserialize, {}>" , ty. to_token_stream( ) ,
535
+ gc_lt. to_token_stream( ) , id. to_token_stream( )
536
+ ) ,
537
+ |bounds| bounds. deserialize . value ( )
538
+ ) ;
539
+ quote ! ( , bound( deserialize = #bound) )
540
+ } ;
541
+ quote ! ( # [ serde( deserialize_with = #deserialize_with #custom_bound) ] )
542
+ } ;
461
543
quote ! {
462
544
#( #forwarded_attrs) *
463
- # [ serde ( deserialize_with = "deserialize_hack" , bound ( deserialize = #bound ) ) ]
545
+ #serde_attr
464
546
#named #ty
465
547
}
466
548
} ;
@@ -499,29 +581,46 @@ impl TraceDeriveInput {
499
581
let id_decl = if id_is_generic {
500
582
Some ( quote ! ( #id: zerogc:: CollectorId , ) )
501
583
} else { None } ;
502
- Ok ( quote ! {
503
- impl #impl_generics zerogc:: serde:: GcDeserialize <#gc_lt, ' deserialize, #id> for #target_type #ty_generics #where_clause {
504
- fn deserialize_gc<D : serde:: Deserializer <' deserialize>>( ctx: & #gc_lt <<#id as zerogc:: CollectorId >:: System as zerogc:: GcSystem >:: Context , deserializer: D ) -> Result <Self , D :: Error > {
505
- use serde:: Deserializer ;
506
- let _guard = unsafe { zerogc:: serde:: hack:: set_context( ctx) } ;
507
- unsafe {
508
- debug_assert_eq!( _guard. get_unchecked( ) as * const _, ctx as * const _) ;
509
- }
510
- /// Hack function to deserialize via `serde::hack`, with the appropriate `Id` type
511
- ///
512
- /// Needed because the actual function is unsafe
513
- #[ track_caller]
514
- fn deserialize_hack<' gc, ' de, #id_decl D : serde:: de:: Deserializer <' de>, T : zerogc:: serde:: GcDeserialize <#gc_lt, ' de, #id>>( deser: D ) -> Result <T , D :: Error > {
515
- unsafe { zerogc:: serde:: hack:: unchecked_deserialize_hack:: <' gc, ' de, D , #id, T >( deser) }
584
+ if !type_opts. delegate && !original_generics. lifetimes ( ) . any ( |lt| lt. lifetime == * gc_lt) {
585
+ return Err ( Error :: custom ( "No 'gc lifetime found during #[derive(GcDeserialize)]. Consider #[zerogc(serde(delegate))] or a PhantomData." ) )
586
+ }
587
+ if type_opts. delegate {
588
+ Ok ( quote ! {
589
+ impl #impl_generics zerogc:: serde:: GcDeserialize <#gc_lt, ' deserialize, #id> for #target_type #ty_generics #where_clause {
590
+ fn deserialize_gc<D : serde:: Deserializer <' deserialize>>( _ctx: & #gc_lt <<#id as zerogc:: CollectorId >:: System as zerogc:: GcSystem >:: Context , deserializer: D ) -> Result <Self , D :: Error > {
591
+ <Self as serde:: Deserialize <' deserialize>>:: deserialize( deserializer)
592
+ }
593
+ }
594
+ } )
595
+ } else {
596
+ let custom_bound = type_opts. custom_bounds . as_ref ( ) . map ( |bounds| {
597
+ let de_bounds = bounds. deserialize . value ( ) ;
598
+ quote ! ( , bound( deserialize = #de_bounds) )
599
+ } ) ;
600
+ Ok ( quote ! {
601
+ impl #impl_generics zerogc:: serde:: GcDeserialize <#gc_lt, ' deserialize, #id> for #target_type #ty_generics #where_clause {
602
+ fn deserialize_gc<D : serde:: Deserializer <' deserialize>>( ctx: & #gc_lt <<#id as zerogc:: CollectorId >:: System as zerogc:: GcSystem >:: Context , deserializer: D ) -> Result <Self , D :: Error > {
603
+ use serde:: Deserializer ;
604
+ let _guard = unsafe { zerogc:: serde:: hack:: set_context( ctx) } ;
605
+ unsafe {
606
+ debug_assert_eq!( _guard. get_unchecked( ) as * const _, ctx as * const _) ;
607
+ }
608
+ /// Hack function to deserialize via `serde::hack`, with the appropriate `Id` type
609
+ ///
610
+ /// Needed because the actual function is unsafe
611
+ #[ track_caller]
612
+ fn deserialize_hack<' gc, ' de, #id_decl D : serde:: de:: Deserializer <' de>, T : zerogc:: serde:: GcDeserialize <#gc_lt, ' de, #id>>( deser: D ) -> Result <T , D :: Error > {
613
+ unsafe { zerogc:: serde:: hack:: unchecked_deserialize_hack:: <' gc, ' de, D , #id, T >( deser) }
614
+ }
615
+ # [ derive( serde:: Deserialize ) ]
616
+ # [ serde( remote = #remote_name #custom_bound) ]
617
+ #( #forward_attrs) *
618
+ #inner ;
619
+ HackRemoteDeserialize :: deserialize( deserializer)
516
620
}
517
- # [ derive( serde:: Deserialize ) ]
518
- # [ serde( remote = #remote_name) ]
519
- #( #forward_attrs) *
520
- #inner ;
521
- HackRemoteDeserialize :: deserialize( deserializer)
522
621
}
523
- }
524
- } )
622
+ } )
623
+ }
525
624
}
526
625
)
527
626
}
0 commit comments