Skip to content

Commit 04a3869

Browse files
committed
Merge branch 'phantom_deser'
2 parents 1cdb2d9 + 4893940 commit 04a3869

File tree

5 files changed

+343
-67
lines changed

5 files changed

+343
-67
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
* ε-copy deserialization of primitive types will return an error on EOF
2121
instead of panicking.
2222

23+
* Since the beginning, associated (de)serialization types of zero-copy
24+
types where built by the derive code using associated (de)serialization
25+
types of their generic type parameters, but this is not correct and does
26+
not always work, as the associated (de)serialization type of zero-copy
27+
type is just `Self`.
28+
2329
## [0.8.0] - 2025-03-03
2430

2531
### New

epserde-derive/src/lib.rs

Lines changed: 136 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,16 @@ fn check_attrs(input: &DeriveInput) -> (bool, bool, bool) {
210210
(is_repr_c, is_zero_copy, is_deep_copy)
211211
}
212212

213+
/// Check if a type is PhantomDeserData
214+
fn is_phantom_deser_data(ty: &syn::Type) -> bool {
215+
if let syn::Type::Path(type_path) = ty {
216+
if let Some(segment) = type_path.path.segments.last() {
217+
return segment.ident == "PhantomDeserData";
218+
}
219+
}
220+
false
221+
}
222+
213223
/// Generate an ε-serde implementation for custom types.
214224
///
215225
/// It generates implementations for the traits `CopyType`,
@@ -291,8 +301,9 @@ pub fn epserde_derive(input: TokenStream) -> TokenStream {
291301
let mut methods: Vec<proc_macro2::TokenStream> = vec![];
292302

293303
s.fields.iter().for_each(|field| {
294-
let ty = &field.ty;
295-
if generics_names_raw.contains(&ty.to_token_stream().to_string()) {
304+
if is_phantom_deser_data(&field.ty) {
305+
methods.push(syn::parse_quote!(_deserialize_eps_inner_special));
306+
} else if generics_names_raw.contains(&field.ty.to_token_stream().to_string()) {
296307
methods.push(syn::parse_quote!(_deserialize_eps_inner));
297308
} else {
298309
methods.push(syn::parse_quote!(_deserialize_full_inner));
@@ -369,62 +380,11 @@ pub fn epserde_derive(input: TokenStream) -> TokenStream {
369380
})
370381
.collect::<Vec<_>>();
371382

372-
// If there are bounded type parameters which are fields of the
373-
// struct, we need to impose the same bounds on the SerType and on
374-
// the DeserType.
375-
derive_input.generics.params.iter().for_each(|param| {
376-
if let GenericParam::Type(t) = param {
377-
let ty = &t.ident;
378-
379-
// We are just interested in types with bounds that are
380-
// types of fields of the struct.
381-
//
382-
// Note that types_with_generics contains also field types
383-
// *containing* a type parameter, but that just slows down
384-
// the search.
385-
if ! t.bounds.is_empty() &&
386-
types_with_generics.iter().any(|x| *ty == x.to_token_stream().to_string()) {
387-
388-
// Add a lifetime so we express bounds on DeserType
389-
let mut lifetimes = Punctuated::new();
390-
lifetimes.push(GenericParam::Lifetime(LifetimeParam {
391-
attrs: vec![],
392-
lifetime: syn::Lifetime::new("'epserde_desertype", proc_macro2::Span::call_site()),
393-
colon_token: None,
394-
bounds: Punctuated::new(),
395-
}));
396-
// Add the type bounds to the DeserType
397-
where_clause_des
398-
.predicates
399-
.push(WherePredicate::Type(PredicateType {
400-
lifetimes: Some(BoundLifetimes {
401-
for_token: token::For::default(),
402-
lt_token: token::Lt::default(),
403-
lifetimes,
404-
gt_token: token::Gt::default(),
405-
}),
406-
bounded_ty: syn::parse_quote!(
407-
<#ty as epserde::deser::DeserializeInner>::DeserType<'epserde_desertype>
408-
),
409-
colon_token: token::Colon::default(),
410-
bounds: t.bounds.clone(),
411-
}));
412-
// Add the type bounds to the SerType
413-
where_clause_ser
414-
.predicates
415-
.push(WherePredicate::Type(PredicateType {
416-
lifetimes: None,
417-
bounded_ty: syn::parse_quote!(
418-
<#ty as epserde::ser::SerializeInner>::SerType
419-
),
420-
colon_token: token::Colon::default(),
421-
bounds: t.bounds.clone(),
422-
}));
423-
}
424-
}
425-
});
426-
427383
if is_zero_copy {
384+
// In zero-copy types we do not need to add bounds to
385+
// the associated SerType/DeserType, as generics are not
386+
// replaced with their SerType/DeserType.
387+
428388
quote! {
429389
#[automatically_derived]
430390
impl<#impl_generics> epserde::traits::CopyType for #name<#concat_generics> #where_clause {
@@ -433,7 +393,7 @@ pub fn epserde_derive(input: TokenStream) -> TokenStream {
433393

434394
#[automatically_derived]
435395
impl<#generics_serialize> epserde::ser::SerializeInner for #name<#concat_generics> #where_clause_ser {
436-
type SerType = #name<#(#ser_type_generics),*>;
396+
type SerType = Self;
437397
// Compute whether the type could be zero copy
438398
const IS_ZERO_COPY: bool = #is_repr_c #(
439399
&& <#fields_types>::IS_ZERO_COPY
@@ -463,7 +423,7 @@ pub fn epserde_derive(input: TokenStream) -> TokenStream {
463423
epserde::deser::helpers::deserialize_full_zero::<Self>(backend)
464424
}
465425

466-
type DeserType<'epserde_desertype> = &'epserde_desertype #name<#concat_generics>;
426+
type DeserType<'epserde_desertype> = &'epserde_desertype Self;
467427

468428
unsafe fn _deserialize_eps_inner<'deserialize_eps_inner_lifetime>(
469429
backend: &mut epserde::deser::SliceWithPos<'deserialize_eps_inner_lifetime>,
@@ -474,6 +434,62 @@ pub fn epserde_derive(input: TokenStream) -> TokenStream {
474434
}
475435
}
476436
} else {
437+
// If there are bounded type parameters which are fields of the
438+
// struct, we need to impose the same bounds on the SerType and on
439+
// the DeserType.
440+
derive_input.generics.params.iter().for_each(|param| {
441+
if let GenericParam::Type(t) = param {
442+
let ty = &t.ident;
443+
444+
// We are just interested in types with bounds that are
445+
// types of fields of the struct.
446+
//
447+
// Note that types_with_generics contains also field types
448+
// *containing* a type parameter, but that just slows down
449+
// the search.
450+
if ! t.bounds.is_empty() &&
451+
types_with_generics.iter().any(|x| *ty == x.to_token_stream().to_string()) {
452+
453+
let mut lifetimes = Punctuated::new();
454+
// Add a lifetime so we express bounds on DeserType
455+
lifetimes.push(GenericParam::Lifetime(LifetimeParam {
456+
attrs: vec![],
457+
lifetime: syn::Lifetime::new("'epserde_desertype", proc_macro2::Span::call_site()),
458+
colon_token: None,
459+
bounds: Punctuated::new(),
460+
}));
461+
// Add the type bounds to the DeserType
462+
where_clause_des
463+
.predicates
464+
.push(WherePredicate::Type(PredicateType {
465+
lifetimes: Some(BoundLifetimes {
466+
for_token: token::For::default(),
467+
lt_token: token::Lt::default(),
468+
lifetimes,
469+
gt_token: token::Gt::default(),
470+
}),
471+
bounded_ty: syn::parse_quote!(
472+
<#ty as epserde::deser::DeserializeInner>::DeserType<'epserde_desertype>
473+
),
474+
colon_token: token::Colon::default(),
475+
bounds: t.bounds.clone(),
476+
}));
477+
478+
// Add the type bounds to the SerType
479+
where_clause_ser
480+
.predicates
481+
.push(WherePredicate::Type(PredicateType {
482+
lifetimes: None,
483+
bounded_ty: syn::parse_quote!(
484+
<#ty as epserde::ser::SerializeInner>::SerType
485+
),
486+
colon_token: token::Colon::default(),
487+
bounds: t.bounds.clone(),
488+
}));
489+
}
490+
}
491+
});
492+
477493
quote! {
478494
#[automatically_derived]
479495
impl<#impl_generics> epserde::traits::CopyType for #name<#concat_generics> #where_clause {
@@ -608,7 +624,9 @@ pub fn epserde_derive(input: TokenStream) -> TokenStream {
608624
bounds: bounds_des,
609625
}));
610626

611-
if generics_names_raw.contains(&ty.to_token_stream().to_string()) {
627+
if is_phantom_deser_data(&ty) {
628+
methods.push(syn::parse_quote!(_deserialize_eps_inner_special));
629+
} else if generics_names_raw.contains(&ty.to_token_stream().to_string()) {
612630
methods.push(syn::parse_quote!(_deserialize_eps_inner));
613631
} else {
614632
methods.push(syn::parse_quote!(_deserialize_full_inner));
@@ -760,7 +778,7 @@ pub fn epserde_derive(input: TokenStream) -> TokenStream {
760778
}
761779
#[automatically_derived]
762780
impl<#generics_serialize> epserde::ser::SerializeInner for #name<#concat_generics> #where_clause_ser {
763-
type SerType = #name<#(#ser_type_generics,)*>;
781+
type SerType = Self;
764782

765783
// Compute whether the type could be zero copy
766784
const IS_ZERO_COPY: bool = #is_repr_c #(
@@ -788,7 +806,7 @@ pub fn epserde_derive(input: TokenStream) -> TokenStream {
788806
epserde::deser::helpers::deserialize_full_zero::<Self>(backend)
789807
}
790808

791-
type DeserType<'epserde_desertype> = &'epserde_desertype #name<#concat_generics>;
809+
type DeserType<'epserde_desertype> = &'epserde_desertype Self;
792810

793811
unsafe fn _deserialize_eps_inner<'deserialize_eps_inner_lifetime>(
794812
backend: &mut epserde::deser::SliceWithPos<'deserialize_eps_inner_lifetime>,
@@ -799,6 +817,62 @@ pub fn epserde_derive(input: TokenStream) -> TokenStream {
799817
}
800818
}
801819
} else {
820+
// If there are bounded type parameters which are fields of the
821+
// struct, we need to impose the same bounds on the SerType and on
822+
// the DeserType.
823+
derive_input.generics.params.iter().for_each(|param| {
824+
if let GenericParam::Type(t) = param {
825+
let ty = &t.ident;
826+
827+
// We are just interested in types with bounds that are
828+
// types of fields of the struct.
829+
//
830+
// Note that types_with_generics contains also field types
831+
// *containing* a type parameter, but that just slows down
832+
// the search.
833+
if ! t.bounds.is_empty() &&
834+
types_with_generics.iter().any(|x| *ty == x.to_token_stream().to_string()) {
835+
836+
let mut lifetimes = Punctuated::new();
837+
// Add a lifetime so we express bounds on DeserType
838+
lifetimes.push(GenericParam::Lifetime(LifetimeParam {
839+
attrs: vec![],
840+
lifetime: syn::Lifetime::new("'epserde_desertype", proc_macro2::Span::call_site()),
841+
colon_token: None,
842+
bounds: Punctuated::new(),
843+
}));
844+
// Add the type bounds to the DeserType
845+
where_clause_des
846+
.predicates
847+
.push(WherePredicate::Type(PredicateType {
848+
lifetimes: Some(BoundLifetimes {
849+
for_token: token::For::default(),
850+
lt_token: token::Lt::default(),
851+
lifetimes,
852+
gt_token: token::Gt::default(),
853+
}),
854+
bounded_ty: syn::parse_quote!(
855+
<#ty as epserde::deser::DeserializeInner>::DeserType<'epserde_desertype>
856+
),
857+
colon_token: token::Colon::default(),
858+
bounds: t.bounds.clone(),
859+
}));
860+
861+
// Add the type bounds to the SerType
862+
where_clause_ser
863+
.predicates
864+
.push(WherePredicate::Type(PredicateType {
865+
lifetimes: None,
866+
bounded_ty: syn::parse_quote!(
867+
<#ty as epserde::ser::SerializeInner>::SerType
868+
),
869+
colon_token: token::Colon::default(),
870+
bounds: t.bounds.clone(),
871+
}));
872+
}
873+
}
874+
});
875+
802876
quote! {
803877
#[automatically_derived]
804878
impl<#impl_generics> epserde::traits::CopyType for #name<#concat_generics> #where_clause {

epserde/src/lib.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,17 @@
1111
#[cfg(all(feature = "alloc", not(feature = "std")))]
1212
extern crate alloc;
1313

14+
use core::{hash::Hash, marker::PhantomData, mem::transmute};
15+
1416
#[cfg(feature = "derive")]
1517
pub use epserde_derive::{Epserde, TypeInfo};
1618

19+
use crate::{
20+
deser::{DeserializeInner, ReadWithPos, SliceWithPos},
21+
ser::{SerializeInner, WriteWithNames},
22+
traits::{AlignHash, CopyType, MaxSizeOf, TypeHash, Zero},
23+
};
24+
1725
pub mod deser;
1826
pub mod impls;
1927
pub mod ser;
@@ -55,6 +63,72 @@ pub fn pad_align_to(value: usize, align_to: usize) -> usize {
5563
value.wrapping_neg() & (align_to - 1)
5664
}
5765

66+
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
67+
pub struct PhantomDeserData<T: ?Sized>(pub PhantomData<T>);
68+
69+
impl<T: ?Sized + DeserializeInner> PhantomDeserData<T> {
70+
#[inline(always)]
71+
pub unsafe fn _deserialize_eps_inner_special<'a>(
72+
_backend: &mut SliceWithPos<'a>,
73+
) -> deser::Result<PhantomDeserData<T::DeserType<'a>>> {
74+
// SAFETY: types are zero-length
75+
Ok(unsafe {
76+
transmute::<
77+
<PhantomDeserData<T> as DeserializeInner>::DeserType<'a>,
78+
PhantomDeserData<T::DeserType<'a>>,
79+
>(PhantomDeserData(PhantomData))
80+
})
81+
}
82+
}
83+
84+
impl<T: ?Sized> CopyType for PhantomDeserData<T> {
85+
type Copy = Zero;
86+
}
87+
88+
impl<T: ?Sized> MaxSizeOf for PhantomDeserData<T> {
89+
fn max_size_of() -> usize {
90+
0
91+
}
92+
}
93+
94+
impl<T: ?Sized + TypeHash> TypeHash for PhantomDeserData<T> {
95+
#[inline(always)]
96+
fn type_hash(hasher: &mut impl core::hash::Hasher) {
97+
"PhantomDeserData".hash(hasher);
98+
T::type_hash(hasher);
99+
}
100+
}
101+
102+
impl<T: ?Sized> AlignHash for PhantomDeserData<T> {
103+
#[inline(always)]
104+
fn align_hash(_hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {}
105+
}
106+
107+
impl<T: ?Sized> SerializeInner for PhantomDeserData<T> {
108+
type SerType = Self;
109+
const IS_ZERO_COPY: bool = true;
110+
const ZERO_COPY_MISMATCH: bool = false;
111+
112+
#[inline(always)]
113+
unsafe fn _serialize_inner(&self, _backend: &mut impl WriteWithNames) -> ser::Result<()> {
114+
Ok(())
115+
}
116+
}
117+
118+
impl<T: ?Sized + DeserializeInner> DeserializeInner for PhantomDeserData<T> {
119+
#[inline(always)]
120+
unsafe fn _deserialize_full_inner(_backend: &mut impl ReadWithPos) -> deser::Result<Self> {
121+
Ok(PhantomDeserData(PhantomData))
122+
}
123+
type DeserType<'a> = PhantomDeserData<T::DeserType<'a>>;
124+
#[inline(always)]
125+
unsafe fn _deserialize_eps_inner<'a>(
126+
_backend: &mut SliceWithPos<'a>,
127+
) -> deser::Result<Self::DeserType<'a>> {
128+
Ok(PhantomDeserData(PhantomData))
129+
}
130+
}
131+
58132
#[test]
59133

60134
fn test_pad_align_to() {

0 commit comments

Comments
 (0)