mun_codegen_macros/lib.rs
1#![cfg(not(tarpaulin_include))]
2
3use proc_macro::TokenStream;
4use proc_macro2::Span;
5use quote::quote;
6use syn::{parse_macro_input, Data, DeriveInput, Ident, Index};
7
8/// This procedural macro implements the `AsValue` trait as well as several required other traits.
9/// All of these traits enable creating an `inkwell::values::StructValue` from a generic struct, as
10/// long as all fields of the struct also implement `AsValue`.
11#[proc_macro_derive(AsValue)]
12pub fn as_value_derive(input: TokenStream) -> TokenStream {
13 // Parse Phase
14 let derive_input = parse_macro_input!(input as DeriveInput);
15
16 // Get the typename of the struct we're working with
17 let ident = {
18 let ident = &derive_input.ident;
19 let generics = derive_input.generics;
20 quote! {
21 #ident #generics
22 }
23 };
24
25 match derive_input.data {
26 Data::Struct(struct_data) => {
27 // Generate a list of functions that return `false` if the struct field does not have an
28 // equivalent constant IR value.
29 let field_has_const_values = struct_data.fields.iter().map(|f| {
30 let ty = &f.ty;
31 quote! {
32 if !<#ty>::has_const_value() {
33 return false;
34 }
35 }
36 });
37
38 // Generate a list of struct fields' paddings.
39 //
40 // Expects:
41 // - type_context: &IrTypeContext
42 // - fn padded_size(align: usize, data_size: usize) -> usize
43 let field_padding_types = {
44 let field_sizes = struct_data.fields.iter().map(|f| {
45 let ty = &f.ty;
46 quote! {{
47 let ir_type = <#ty>::get_ir_type(type_context);
48 type_context.target_data.get_store_size(&ir_type) as usize
49 }}
50 });
51
52 let field_alignments = struct_data.fields.iter().map(|f| {
53 let ty = &f.ty;
54 quote! {{
55 let ir_type = <#ty>::get_ir_type(type_context);
56 type_context.target_data.get_preferred_alignment(&ir_type) as usize
57 }}
58 });
59
60 quote! {{
61 let mut total_size = 0;
62
63 let field_sizes = vec![ #(#field_sizes),* ];
64 let field_alignments = vec![ #(#field_alignments),* ];
65
66 let mut field_paddings: Vec<usize> = field_sizes
67 .iter()
68 .zip(field_alignments.iter())
69 .map(|(size, align)| {
70 let padded_size = padded_size(*align, total_size);
71 let padding = padded_size - total_size;
72 total_size = padded_size + size;
73 padding
74 })
75 .collect();
76
77 // Add padding for the end of the struct
78 let max_align = field_alignments.iter().max().cloned().unwrap_or(1);
79 let padded_size = padded_size(max_align, total_size);
80 field_paddings.push(padded_size - total_size);
81
82 field_paddings
83 }}
84 };
85
86 let field_padding_values = field_padding_types.clone();
87 let field_padding_bytes = field_padding_types.clone();
88
89 // Generate a list of where clauses that ensure that we can cast each field to an
90 // `inkwell::types::BasicTypeEnum`
91 let field_types = struct_data.fields.iter().map(|f| {
92 let ty = &f.ty;
93 quote! {
94 Into::<inkwell::types::BasicTypeEnum<'ink>>::into(<#ty>::get_ir_type(context))
95 }
96 });
97
98 // Generate a list of where clauses that ensure that we can cast each field to an
99 // `inkwell::values::BasicTypeValue`
100 let field_types_values = struct_data.fields.iter().enumerate().map(|(idx, f)| {
101 let idx = Index::from(idx);
102 let name = f.ident.as_ref().map(|i| quote! { #i }).unwrap_or_else(|| quote! { #idx });
103 quote! {
104 {
105 let value = crate::value::AsValueInto::<'ink, inkwell::values::BasicValueEnum<'ink>>::as_value_into(&self. #name, context);
106 value
107 }
108 }
109 });
110
111 // Generate a list of bytes and `inkwell::values::PointerValue`s for each field.
112 //
113 // Expects:
114 // - type_context: &IrTypeContext
115 // - fn padded_size(align: usize, data_size: usize) -> usize
116 // - field_padding: Vec<usize>
117 let field_bytes_and_ptrs = {
118 let field_bytes_and_ptrs = struct_data.fields.iter().enumerate().map(|(idx, f)| {
119 let idx = Index::from(idx);
120 let name = f
121 .ident
122 .as_ref()
123 .map(|i| quote! { #i })
124 .unwrap_or_else(|| quote! { #idx });
125 quote! {
126 self. #name .as_bytes_and_ptrs(type_context)
127 }
128 });
129
130 quote! {{
131 let field_bytes_and_ptrs = vec![ #(#field_bytes_and_ptrs),* ];
132 field_padding
133 .into_iter()
134 .map(|p| vec![BytesOrPtr::Bytes(vec![0u8; p])])
135 // Interleave padding and field types, resulting in:
136 // [align_padding1, type1, align_padding2, type2, rear_padding]
137 .interleave(field_bytes_and_ptrs.into_iter())
138 .flatten()
139 .collect::<Vec<_>>()
140 }}
141 };
142
143 // Generate Phase
144 (quote! {
145 impl<'ink> crate::value::ConcreteValueType<'ink> for #ident {
146 type Value = inkwell::values::StructValue<'ink>;
147 }
148
149 impl<'ink> crate::value::SizedValueType<'ink> for #ident {
150 fn get_ir_type(context: &crate::value::IrTypeContext<'ink, '_>) -> inkwell::types::StructType<'ink> {
151 // Check whether the IR struct type exists
152 let key = std::any::type_name::<#ident>();
153 match context.struct_types.borrow().get(&key) {
154 Some(value) => {
155 return *value;
156 }
157 None => (),
158 };
159
160 // Construct a new IR struct type
161 let struct_ty = context.context.opaque_struct_type(key);
162 context.struct_types.borrow_mut().insert(key, struct_ty);
163
164 /// Calculates the size of data after padding has been appended to its end,
165 /// based on its alignment.
166 fn padded_size(align: usize, data_size: usize) -> usize {
167 ((data_size + align - 1) / align) * align
168 }
169
170 // Aliasing to make sure that all procedurally generated macros can use the
171 // same variable name.
172 let type_context = context;
173
174 let field_types = vec![ #(#field_types),* ];
175 let field_padding = #field_padding_types;
176 let struct_fields: Vec<_> = field_padding
177 .into_iter()
178 // Choose a field's padding type based on the size of its alignment
179 // padding
180 .map(|p| {
181 let (ty, num_chunks) = if p % 8 == 0 {
182 (context.context.i64_type(), p / 8)
183 } else if p % 4 == 0 {
184 (context.context.i32_type(), p / 4)
185 } else if p % 2 == 0 {
186 (context.context.i16_type(), p / 2)
187 } else {
188 (context.context.i8_type(), p)
189 };
190
191 ty.array_type(num_chunks as u32).into()
192 })
193 // Interleave padding and field types, resulting in:
194 // [align_padding1, type1, align_padding2, type2, rear_padding]
195 .interleave(field_types.into_iter())
196 .collect();
197
198 struct_ty.set_body(&struct_fields, true);
199 struct_ty
200 }
201 }
202
203 impl<'ink> crate::value::PointerValueType<'ink> for #ident {
204 fn get_ptr_type(context: &crate::value::IrTypeContext<'ink, '_>, address_space: Option<inkwell::AddressSpace>) -> inkwell::types::PointerType<'ink> {
205 Self::get_ir_type(context).ptr_type(address_space.unwrap_or(inkwell::AddressSpace::Generic))
206 }
207 }
208
209 impl<'ink> crate::value::HasConstValue for #ident {
210 fn has_const_value() -> bool {
211 use crate::value::HasConstValue;
212 #(#field_has_const_values)*
213 true
214 }
215 }
216
217 impl<'ink> crate::value::AsBytesAndPtrs<'ink> for #ident {
218 fn as_bytes_and_ptrs(
219 &self,
220 context: &crate::value::IrTypeContext<'ink, '_>
221 ) -> Vec<crate::value::BytesOrPtr<'ink>> {
222 use crate::value::AsBytesAndPtrs;
223
224 fn padded_size(align: usize, data_size: usize) -> usize {
225 ((data_size + align - 1) / align) * align
226 }
227
228 // Aliasing to make sure that all procedurally generated macros can use the
229 // same variable name.
230 let type_context = context;
231 let field_padding = #field_padding_bytes;
232
233 #field_bytes_and_ptrs
234 }
235 }
236
237 impl<'ink> crate::value::AsValue<'ink, #ident> for #ident {
238 fn as_value(&self, context: &crate::value::IrValueContext<'ink, '_, '_>) -> crate::value::Value<'ink, Self> {
239 use crate::value::HasConstValue;
240
241 /// Calculates the size of data after padding has been appended to its end,
242 /// based on its alignment.
243 fn padded_size(align: usize, data_size: usize) -> usize {
244 ((data_size + align - 1) / align) * align
245 }
246
247 // Aliasing to make sure that all procedurally generated macros can use the
248 // same variable name.
249 let type_context = context.type_context;
250 let field_padding = #field_padding_values;
251
252 // If struct type can be constructed as a constant LLVM IR value
253 if <#ident>::has_const_value() {
254 // construct a named instance of that struct type
255 let struct_type = Self::get_ir_type(context.type_context);
256
257 let field_values = vec![ #(#field_types_values),* ];
258 let struct_fields: Vec<_> = field_padding
259 .into_iter()
260 // Choose a field's padding type based on the size of its alignment
261 // padding
262 .map(|p| {
263 let (ty, num_chunks) = if p % 8 == 0 {
264 (context.context.i64_type(), p / 8)
265 } else if p % 4 == 0 {
266 (context.context.i32_type(), p / 4)
267 } else if p % 2 == 0 {
268 (context.context.i16_type(), p / 2)
269 } else {
270 (context.context.i8_type(), p)
271 };
272
273 let chunks: Vec<_> = (0..num_chunks)
274 .map(|_| ty.const_int(0, false))
275 .collect();
276
277 ty.const_array(&chunks).into()
278 })
279 // Interleave padding and field types, resulting in:
280 // [align_padding1, type1, align_padding2, type2, rear_padding]
281 .interleave(field_values.into_iter())
282 .collect();
283
284 let value = struct_type.const_named_struct(&struct_fields);
285 // eprintln!("Done");
286 crate::value::Value::from_raw(value)
287 } else {
288 use crate::value::{AsBytesAndPtrs, BytesOrPtr};
289 use inkwell::values::BasicValueEnum;
290
291 // construct an anonymous struct type consisting of bytes and pointers
292 let field_bytes_and_ptrs = self
293 .as_bytes_and_ptrs(context.type_context)
294 .into_iter()
295 .fold(Vec::new(), |mut v, rhs| {
296 match rhs {
297 BytesOrPtr::Bytes(mut rhs) => {
298 if let Some(BytesOrPtr::Bytes(lhs)) = v.last_mut() {
299 lhs.append(&mut rhs);
300 } else {
301 v.push(BytesOrPtr::Bytes(rhs));
302 }
303 }
304 BytesOrPtr::UntypedPtr(p) => {
305 v.push(BytesOrPtr::UntypedPtr(p));
306 }
307 }
308 v
309 });
310
311 let byte_ty = <u8>::get_ir_type(context.type_context);
312
313 let field_values: Vec<BasicValueEnum> = field_bytes_and_ptrs
314 .into_iter()
315 .map(|f| match f {
316 BytesOrPtr::Bytes(b) => {
317 let bytes: Vec<_> = b
318 .into_iter()
319 .map(|b| byte_ty.const_int(u64::from(b), false))
320 .collect();
321
322 byte_ty.const_array(&bytes).into()
323 }
324 BytesOrPtr::UntypedPtr(ptr) => ptr.into(),
325 })
326 .collect();
327
328 let value = context.context.const_struct(&field_values, true);
329 Value::from_raw(value)
330 }
331 }
332 }
333
334 impl<'ink> crate::value::AddressableType<'ink, #ident> for #ident {}
335 }).into()
336 }
337 Data::Union(_) => {
338 unimplemented!("#[derive(AsValue)] is not defined for unions!");
339 }
340 Data::Enum(enum_data) => {
341 // Only allow these types in the `repr` attribute
342 const SUPPORTED_TAG_SIZES: &[&str] =
343 &["u8", "u16", "u32", "u64", "i8", "i16", "i32", "i64"];
344
345 // Check whether the enum has a `repr` attribute
346 let repr_ty = derive_input.attrs.iter().find_map(|a| {
347 a.parse_meta().map_or(None, |m| {
348 if let syn::Meta::List(list) = m {
349 let op = list
350 .path
351 .segments
352 .iter()
353 .next()
354 .map(|s| s.ident.to_string());
355
356 if op == Some("repr".to_string()) {
357 return list.nested.iter().next().and_then(|n| {
358 if let syn::NestedMeta::Meta(m) = n {
359 m.path().segments.iter().next().map(|s| s.ident.clone())
360 } else {
361 None
362 }
363 });
364 }
365 }
366
367 None
368 })
369 });
370
371 // Use the `repr` attribute as tag type.
372 let repr_ty = if let Some(ident) = repr_ty {
373 let repr_ty = ident.to_string();
374 if !SUPPORTED_TAG_SIZES.contains(&repr_ty.as_str()) {
375 eprintln!(
376 "`repr({})` is not supported by the `AsValue` macro.",
377 repr_ty
378 );
379 }
380
381 quote! {
382 #ident
383 }
384 } else {
385 // Default to u32
386 quote! {
387 u32
388 }
389 };
390
391 if enum_data.variants.is_empty() {
392 eprintln!("Enums with no variants are not supported by the `AsValue` macro.")
393 }
394
395 let enum_name = &derive_input.ident;
396
397 // Returns a variant's fields' paddings and the variant's size.
398 //
399 // Expects:
400 // - chunk_size: usize
401 // - fn padded_size(align: usize, data_size: usize) -> usize
402 let variant_type_field_paddings_and_sizes = enum_data.variants.iter().map(|v| {
403 let field_sizes = v.fields.iter().map(|f| {
404 let ty = &f.ty;
405 quote! {{
406 let ir_type = <#ty>::get_ir_type(type_context);
407 type_context.target_data.get_store_size(&ir_type) as usize
408 }}
409 });
410
411 let field_alignments = v.fields.iter().map(|f| {
412 let ty = &f.ty;
413 quote! {{
414 let ir_type = <#ty>::get_ir_type(type_context);
415 type_context.target_data.get_preferred_alignment(&ir_type) as usize
416 }}
417 });
418
419 quote! {{
420 // Start with the tag's size (same as chunk_size)
421 let mut total_size = chunk_size;
422
423 let field_sizes = [ #(#field_sizes),* ];
424 let field_alignments = [ #(#field_alignments),* ];
425
426 // Calculate the padding required to align each field
427 let field_paddings: Vec<usize> = field_sizes
428 .iter()
429 .zip(field_alignments.iter())
430 .map(|(size, align)| {
431 let padded_size = padded_size(*align, total_size);
432 let padding = padded_size - total_size;
433 total_size = padded_size + size;
434 padding
435 })
436 .collect();
437
438 (
439 field_paddings,
440 total_size,
441 )
442 }}
443 });
444
445 let variant_value_field_paddings_and_sizes =
446 variant_type_field_paddings_and_sizes.clone();
447
448 let variant_type_alignments = enum_data.variants.iter().map(|v| {
449 let field_alignments = v.fields.iter().map(|f| {
450 let ty = &f.ty;
451 quote! {{
452 let ir_type = <#ty>::get_ir_type(type_context);
453 type_context.target_data.get_preferred_alignment(&ir_type) as usize
454 }}
455 });
456
457 let variant_align = quote! {{
458 let field_alignments = [#(#field_alignments),*];
459 field_alignments.iter().max().cloned().unwrap_or(1)
460 }};
461
462 variant_align
463 });
464
465 let variant_value_alignments = variant_type_alignments.clone();
466
467 // Generate a list of bytes and `inkwell::values::PointerValue`s for each field.
468 //
469 // Expects:
470 // - type_context: &IrTypeContext
471 // - enum_size: usize
472 // - variant_sizes: Vec<usize>
473 let variant_bytes_and_ptrs = {
474 let variant_bytes_and_ptrs_mapping = enum_data
475 .variants
476 .iter()
477 .enumerate()
478 .map(|(tag, v)| {
479 let tag = Index::from(tag);
480 let field_mappings = v.fields.iter().enumerate().map(|(idx, f)| {
481 let name = f.ident.as_ref().map(|i| quote! { #i }).unwrap_or_else(|| {
482 // If this is a tuple struct, map the index to an alias (e.g. 0: t0)
483 let concatenated = format!("t{}", idx);
484 let local = Ident::new(&concatenated, Span::call_site());
485 let idx = Index::from(idx);
486 quote! { #idx: #local }
487 });
488
489 name
490 });
491
492 let field_bytes_and_ptrs = v.fields.iter().enumerate().map(|(idx, f)| {
493 let name = f.ident.as_ref().map(|i| quote! { #i }).unwrap_or_else(|| {
494 // If this is a tuple struct, map the use an alias (e.g. t0 for 0)
495 let concatenated = format!("t{}", idx);
496 let local = Ident::new(&concatenated, Span::call_site());
497 quote! { #local }
498 });
499
500 quote! {
501 #name .as_bytes_and_ptrs(type_context)
502 }
503 });
504
505 let ident = &v.ident;
506 quote! {
507 #enum_name :: #ident { #(#field_mappings),* } => {
508 let (variant_field_paddings, variant_size) =
509 variant_field_paddings_and_sizes.get(#tag).expect(
510 "Number of `variant_field_paddings_and_sizes` does not match the number of variants."
511 );
512
513 let variant_field_paddings = variant_field_paddings
514 .iter()
515 .map(|p| vec![0u8; *p].into());
516
517 let field_bytes_and_ptrs = vec![
518 // Convert the tag to bytes
519 vec![BytesOrPtr::Bytes(
520 bytemuck::cast_ref::<#repr_ty, [u8; std::mem::size_of::<#repr_ty>()]>(&#tag)
521 .to_vec()
522 )],
523 // Converts all other fields to bytes and pointers
524 #(#field_bytes_and_ptrs),*
525 ];
526 let mut field_bytes_and_ptrs: Vec<_> = field_bytes_and_ptrs
527 .iter()
528 .flatten()
529 .cloned()
530 // Interleave field bytes and padding bytes, resulting in:
531 // [tag, align_padding1, type1, align_padding2, type2]
532 .interleave(variant_field_paddings)
533 .collect();
534
535 // Calculate the rear padding required to fill all of the struct's
536 // memory.
537 let rear_padding = enum_size - variant_size;
538 field_bytes_and_ptrs.push(vec![0u8; rear_padding].into());
539
540 field_bytes_and_ptrs
541 }
542 }
543 });
544
545 quote! {
546 match self {
547 #(#variant_bytes_and_ptrs_mapping)*
548 }
549 }
550 };
551
552 // Generate Phase
553 (quote! {
554 impl<'ink> crate::value::ConcreteValueType<'ink> for #ident {
555 type Value = inkwell::values::StructValue<'ink>;
556 }
557
558 impl<'ink> crate::value::SizedValueType<'ink> for #ident {
559 fn get_ir_type(
560 context: &crate::value::IrTypeContext<'ink, '_>
561 ) -> inkwell::types::StructType<'ink> {
562 use std::convert::TryFrom;
563 use inkwell::types::AnyType;
564
565 let key = std::any::type_name::<#ident>();
566 if let Some(value) = context.struct_types.borrow().get(&key) {
567 return *value;
568 };
569
570 // Aliasing to make sure that all procedurally generated macros can use the
571 // same variable name.
572 let type_context = context;
573
574 // Insert an opaque struct type to fix self referential types.
575 let struct_ty = type_context.context.opaque_struct_type(&key);
576 type_context.struct_types.borrow_mut().insert(key, struct_ty);
577
578 // The chunk size is the same as the tag's size
579 let chunk_ty = <#repr_ty>::get_ir_type(type_context);
580 let chunk_size = std::mem::size_of::<#repr_ty>();
581
582 let variant_alignments = [#(#variant_type_alignments),*];
583 let max_align = core::cmp::max(
584 chunk_size,
585 variant_alignments.iter().max().cloned().unwrap_or(1),
586 );
587
588 fn padded_size(align: usize, data_size: usize) -> usize {
589 ((data_size + align - 1) / align) * align
590 }
591
592 let variant_field_paddings_and_sizes = [ #(#variant_type_field_paddings_and_sizes),* ];
593 let max_size = variant_field_paddings_and_sizes
594 .iter()
595 .map(|(_, s)| *s)
596 .max()
597 .unwrap_or(0);
598
599 // Add padding for the end of the variant
600 let enum_size = padded_size(chunk_size, max_size);
601
602 // The tag is excluded from the number of chunks
603 let num_chunks = enum_size / chunk_size - 1;
604 let num_chunks = u32::try_from(num_chunks).expect(
605 "Number of chunks is too large (max: `u32::max()`)"
606 );
607
608 struct_ty.set_body(&[
609 <[#repr_ty; 0]>::get_ir_type(type_context).into(),
610 chunk_ty.into(),
611 chunk_ty.array_type(num_chunks).into(),
612 ], true);
613
614 struct_ty
615 }
616 }
617
618 impl<'ink> crate::value::PointerValueType<'ink> for #ident {
619 fn get_ptr_type(context: &crate::value::IrTypeContext<'ink, '_>, address_space: Option<inkwell::AddressSpace>) -> inkwell::types::PointerType<'ink> {
620 Self::get_ir_type(context).ptr_type(address_space.unwrap_or(inkwell::AddressSpace::Generic))
621 }
622 }
623
624 impl<'ink> crate::value::HasConstValue for #ident {
625 fn has_const_value() -> bool {
626 false
627 }
628 }
629
630 impl<'ink> crate::value::AsBytesAndPtrs<'ink> for #ident {
631 fn as_bytes_and_ptrs(
632 &self,
633 context: &crate::value::IrTypeContext<'ink, '_>
634 ) -> Vec<crate::value::BytesOrPtr<'ink>> {
635 use crate::value::{AsBytesAndPtrs, BytesOrPtr};
636 use inkwell::types::AnyType;
637
638 // Aliasing to make sure that all procedurally generated macros can use the
639 // same variable name.
640 let type_context = context;
641
642 // The chunk size is the same as the tag's size
643 let chunk_ty = <#repr_ty>::get_ir_type(type_context);
644 let chunk_size = std::mem::size_of::<#repr_ty>();
645
646 let variant_alignments = [#(#variant_value_alignments),*];
647 let max_align = core::cmp::max(
648 chunk_size,
649 variant_alignments.iter().max().cloned().unwrap_or(1),
650 );
651
652 fn padded_size(align: usize, data_size: usize) -> usize {
653 ((data_size + align - 1) / align) * align
654 }
655
656 let variant_field_paddings_and_sizes = [ #(#variant_value_field_paddings_and_sizes),* ];
657
658 let max_size = variant_field_paddings_and_sizes
659 .iter()
660 .map(|(_, s)| *s)
661 .max()
662 .unwrap_or(0);
663
664 // Add padding for the end of the variant
665 let enum_size = padded_size(chunk_size, max_size);
666
667 #variant_bytes_and_ptrs
668 }
669 }
670
671 impl<'ink> crate::value::AsValue<'ink, #ident> for #ident {
672 fn as_value(&self, context: &crate::value::IrValueContext<'ink, '_, '_>) -> crate::value::Value<'ink, Self> {
673 use crate::value::{AsBytesAndPtrs, BytesOrPtr};
674 use inkwell::values::BasicValueEnum;
675 use inkwell::types::AnyType;
676
677 let field_bytes_and_ptrs = self
678 .as_bytes_and_ptrs(context.type_context)
679 .into_iter()
680 .fold(Vec::new(), |mut v, rhs| {
681 match rhs {
682 BytesOrPtr::Bytes(mut rhs) => {
683 if let Some(BytesOrPtr::Bytes(lhs)) = v.last_mut() {
684 lhs.append(&mut rhs);
685 } else {
686 v.push(BytesOrPtr::Bytes(rhs));
687 }
688 }
689 BytesOrPtr::UntypedPtr(p) => {
690 v.push(BytesOrPtr::UntypedPtr(p));
691 }
692 }
693 v
694 });
695
696 let byte_ty = <u8>::get_ir_type(context.type_context);
697
698 let field_values: Vec<BasicValueEnum> = field_bytes_and_ptrs
699 .into_iter()
700 .map(|f| match f {
701 BytesOrPtr::Bytes(b) => {
702 let bytes: Vec<_> = b
703 .into_iter()
704 .map(|b| byte_ty.const_int(u64::from(b), false))
705 .collect();
706
707 byte_ty.const_array(&bytes).into()
708 }
709 BytesOrPtr::UntypedPtr(ptr) => ptr.into(),
710 })
711 .collect();
712
713 let value = context.context.const_struct(&field_values, true);
714 Value::from_raw(value)
715 }
716 }
717
718 impl<'ink> crate::value::AddressableType<'ink, #ident> for #ident {}
719 }).into()
720 }
721 }
722}