Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -800,3 +800,8 @@
# Aztec-nr: user-defined 'offchain_receive' is not allowed
from = "/errors/7"
to = "/aztec-nr-api/nightly/noir_aztec/messages/processing/offchain/fn.receive.html"

[[redirects]]
# derive(Packable) used on a struct with sub-Field members that could be packed more efficiently
from = "/errors/8"
to = "/aztec-nr-api/nightly/protocol_types/traits/trait.Packable.html"
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,25 @@ comptime fn get_where_trait_clause(s: TypeDefinition, trait_name: Quoted) -> Quo
}
}

/// Recursively counts sub-Field slots for a type. Types that are smaller than a Field (booleans, integers) each occupy
/// one slot. Arrays multiply element slots by length. Anything else returns 0 since it is either a Field (and is
/// therefore optimally packed), or it is a generic struct with its own Packable implementation - if suboptimal, its
/// derivation will be the one to emit the warning.
comptime fn count_sub_field_slots(typ: Type) -> u32 {
if typ.is_bool() {
1
} else if typ.as_integer().is_some() {
1
} else if typ.as_array().is_some() {
let (elem_type, len_type) = typ.as_array().unwrap();
// Generic array lengths (e.g. [T; N]) can't be resolved at compile time - we assume the array will have at
// least one element
count_sub_field_slots(elem_type) * len_type.as_constant().unwrap_or(1)
} else {
0
}
}

/// Generates a [`Packable`](crate::traits::Packable) trait implementation for a given struct `s`.
///
/// # Arguments
Expand Down Expand Up @@ -172,6 +191,19 @@ pub comptime fn derive_packable(s: TypeDefinition) -> Quoted {
let generics_declarations = get_generics_declarations(s);
let where_packable_clause = get_where_trait_clause(s, quote {Packable});

// Warn when multiple sub-Field members could be packed together more efficiently.
// We use println because comptime `warn` doesn't exist yet (see noir-lang/noir#7714).
let mut total_sub_field_slots: u32 = 0;
for i in 0..params.len() {
total_sub_field_slots += count_sub_field_slots(params[i].1);
}
if total_sub_field_slots >= 2 {
let name = s.name();
println(
f"warning: #[derive(Packable)] on '{name}' is sub-optimal because multiple sub-Field members could be packed together. Consider implementing Packable manually: https://docs.aztec.network/errors/8",
);
}

// The following will give us:
// <type_of_struct_member_1 as Packable>::N + <type_of_struct_member_2 as Packable>::N + ...
// (or 0 if the struct has no members)
Expand Down Expand Up @@ -351,5 +383,4 @@ mod test {
let unpacked = HasArrayWithGenerics::unpack(packed);
assert(unpacked == struct_with_array_of_generics);
}

}
Loading