Skip to content

Commit 9ac19fa

Browse files
authored
fix: serde and packable on empty (#18584)
Fixes #17586 Forgot to handle the empty struct case when implementing the derivation functionality. This PR fixes it.
2 parents 0e78413 + b4d0079 commit 9ac19fa

File tree

1 file changed

+60
-15
lines changed
  • noir-projects/noir-protocol-circuits/crates/types/src/meta

1 file changed

+60
-15
lines changed

noir-projects/noir-protocol-circuits/crates/types/src/meta/mod.nr

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -228,14 +228,20 @@ pub(crate) comptime fn derive_deserialize(s: TypeDefinition) -> Quoted {
228228
let generics_declarations = get_generics_declarations(s);
229229
let where_deserialize_clause = get_where_trait_clause(s, quote {Deserialize});
230230

231-
// The following will give us <type_of_struct_member_1 as Deserialize>::N + <type_of_struct_member_2 as Deserialize>::N + ...
232-
let right_hand_side_of_definition_of_n = params
233-
.map(|(_, param_type, _): (Quoted, Type, Quoted)| {
234-
quote {
231+
// The following will give us:
232+
// <type_of_struct_member_1 as Deserialize>::N + <type_of_struct_member_2 as Deserialize>::N + ...
233+
// (or 0 if the struct has no members)
234+
let right_hand_side_of_definition_of_n = if params.len() > 0 {
235+
params
236+
.map(|(_, param_type, _): (Quoted, Type, Quoted)| {
237+
quote {
235238
<$param_type as $crate::traits::Deserialize>::N
236239
}
237-
})
238-
.join(quote {+});
240+
})
241+
.join(quote {+})
242+
} else {
243+
quote {0}
244+
};
239245

240246
// For structs containing a single member, we can enhance performance by directly deserializing the input array,
241247
// bypassing the need for loop-based array construction. While this optimization yields significant benefits in
@@ -269,11 +275,15 @@ pub(crate) comptime fn derive_deserialize(s: TypeDefinition) -> Quoted {
269275

270276
Self { $struct_members }
271277
}
272-
} else {
278+
} else if params.len() == 1 {
273279
let param_name = params[0].0;
274280
quote {
275281
Self { $param_name: $crate::traits::Deserialize::deserialize(serialized) }
276282
}
283+
} else {
284+
quote {
285+
Self {}
286+
}
277287
};
278288

279289
quote {
@@ -365,14 +375,20 @@ pub comptime fn derive_packable(s: TypeDefinition) -> Quoted {
365375
let generics_declarations = get_generics_declarations(s);
366376
let where_packable_clause = get_where_trait_clause(s, quote {Packable});
367377

368-
// The following will give us <type_of_struct_member_1 as Packable>::N + <type_of_struct_member_2 as Packable>::N + ...
369-
let right_hand_side_of_definition_of_n = params
370-
.map(|(_, param_type, _): (Quoted, Type, Quoted)| {
371-
quote {
378+
// The following will give us:
379+
// <type_of_struct_member_1 as Packable>::N + <type_of_struct_member_2 as Packable>::N + ...
380+
// (or 0 if the struct has no members)
381+
let right_hand_side_of_definition_of_n = if params.len() > 0 {
382+
params
383+
.map(|(_, param_type, _): (Quoted, Type, Quoted)| {
384+
quote {
372385
<$param_type as $crate::traits::Packable>::N
373386
}
374-
})
375-
.join(quote {+});
387+
})
388+
.join(quote {+})
389+
} else {
390+
quote {0}
391+
};
376392

377393
// For structs containing a single member, we can enhance performance by directly returning the packed member,
378394
// bypassing the need for loop-based array construction. While this optimization yields significant benefits in
@@ -404,11 +420,15 @@ pub comptime fn derive_packable(s: TypeDefinition) -> Quoted {
404420

405421
result
406422
}
407-
} else {
423+
} else if params.len() == 1 {
408424
let param_name = params[0].0;
409425
quote {
410426
$crate::traits::Packable::pack(self.$param_name)
411427
}
428+
} else {
429+
quote {
430+
[0; Self::N]
431+
}
412432
};
413433

414434
// For structs containing a single member, we can enhance performance by directly unpacking the input array,
@@ -443,11 +463,15 @@ pub comptime fn derive_packable(s: TypeDefinition) -> Quoted {
443463
$unpacking_of_struct_members
444464
Self { $struct_members }
445465
}
446-
} else {
466+
} else if params.len() == 1 {
447467
let param_name = params[0].0;
448468
quote {
449469
Self { $param_name: $crate::traits::Packable::unpack(packed) }
450470
}
471+
} else {
472+
quote {
473+
Self {}
474+
}
451475
};
452476

453477
quote {
@@ -472,6 +496,9 @@ pub comptime fn derive_packable(s: TypeDefinition) -> Quoted {
472496
mod test {
473497
use crate::traits::{Deserialize, Packable, Serialize};
474498

499+
#[derive(Deserialize, Eq, Packable, Serialize)]
500+
pub struct Empty {}
501+
475502
#[derive(Deserialize, Eq, Packable, Serialize)]
476503
pub struct Smol {
477504
a: Field,
@@ -498,6 +525,24 @@ mod test {
498525
pub length: u32,
499526
}
500527

528+
#[test]
529+
fn serde_on_empty() {
530+
let original = Empty {};
531+
let serialized = original.serialize();
532+
assert_eq(serialized, [], "Serialized does not match empty array");
533+
let deserialized = Empty::deserialize(serialized);
534+
assert_eq(deserialized, original, "Deserialized does not match original");
535+
}
536+
537+
#[test]
538+
fn packable_on_empty() {
539+
let original = Empty {};
540+
let packed = original.pack();
541+
assert_eq(packed, [], "Packed does not match empty array");
542+
let unpacked = Empty::unpack(packed);
543+
assert_eq(unpacked, original, "Unpacked does not match original");
544+
}
545+
501546
#[test]
502547
fn serde_on_smol() {
503548
let smol = Smol { a: 1, b: 2 };

0 commit comments

Comments
 (0)