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}