cairo_lang_sierra/extensions/modules/
structure.rs

1//! Sierra example:
2//! ```ignore
3//! type felt252 = felt252;
4//! type Tuple<felt252, felt252> = Struct<ut@Tuple, felt252, felt252>;
5//! libfunc tuple_construct = struct_construct<Tuple<felt252, felt252>>;
6//! libfunc tuple_deconstruct = struct_deconstruct<Tuple<felt252, felt252>>;
7//! ...
8//! felt252_const<0>() -> (felt0);
9//! felt252_const<1>() -> (felt1);
10//! tuple_construct(felt0, felt1) -> (tup);
11//! tuple_deconstruct(tup) -> (felt0, felt1);
12//! ```
13
14use cairo_lang_utils::try_extract_matches;
15
16use super::snapshot::snapshot_ty;
17use crate::define_libfunc_hierarchy;
18use crate::extensions::lib_func::{
19    DeferredOutputKind, LibfuncSignature, OutputVarInfo, ParamSignature, SierraApChange,
20    SignatureOnlyGenericLibfunc, SignatureSpecializationContext,
21};
22use crate::extensions::type_specialization_context::TypeSpecializationContext;
23use crate::extensions::types::TypeInfo;
24use crate::extensions::{
25    ConcreteType, NamedType, OutputVarReferenceInfo, SpecializationError, args_as_single_type,
26};
27use crate::ids::{ConcreteTypeId, GenericTypeId};
28use crate::program::{ConcreteTypeLongId, GenericArg};
29
30/// Type representing a struct.
31#[derive(Default)]
32pub struct StructType {}
33impl NamedType for StructType {
34    type Concrete = StructConcreteType;
35    const ID: GenericTypeId = GenericTypeId::new_inline("Struct");
36
37    fn specialize(
38        &self,
39        context: &dyn TypeSpecializationContext,
40        args: &[GenericArg],
41    ) -> Result<Self::Concrete, SpecializationError> {
42        Self::Concrete::new(context, args)
43    }
44}
45
46pub struct StructConcreteType {
47    pub info: TypeInfo,
48    pub members: Vec<ConcreteTypeId>,
49}
50impl StructConcreteType {
51    fn new(
52        context: &dyn TypeSpecializationContext,
53        args: &[GenericArg],
54    ) -> Result<Self, SpecializationError> {
55        let mut args_iter = args.iter();
56        args_iter
57            .next()
58            .and_then(|arg| try_extract_matches!(arg, GenericArg::UserType))
59            .ok_or(SpecializationError::UnsupportedGenericArg)?;
60        let mut duplicatable = true;
61        let mut droppable = true;
62        let mut storable = true;
63        let mut members: Vec<ConcreteTypeId> = Vec::new();
64        let mut zero_sized = true;
65        for arg in args_iter {
66            let ty = try_extract_matches!(arg, GenericArg::Type)
67                .ok_or(SpecializationError::UnsupportedGenericArg)?
68                .clone();
69            let info = context.get_type_info(ty.clone())?;
70            if !info.storable {
71                storable = false;
72            }
73            if !info.duplicatable {
74                duplicatable = false;
75            }
76            if !info.droppable {
77                droppable = false;
78            }
79            zero_sized = zero_sized && info.zero_sized;
80            members.push(ty);
81        }
82        Ok(StructConcreteType {
83            info: TypeInfo {
84                long_id: ConcreteTypeLongId {
85                    generic_id: "Struct".into(),
86                    generic_args: args.to_vec(),
87                },
88                duplicatable,
89                droppable,
90                storable,
91                zero_sized,
92            },
93            members,
94        })
95    }
96
97    /// Returns the StructConcreteType of the given long id, or a specialization error if not
98    /// possible.
99    fn try_from_long_id(
100        context: &dyn SignatureSpecializationContext,
101        long_id: &ConcreteTypeLongId,
102    ) -> Result<Self, SpecializationError> {
103        if long_id.generic_id != StructType::ID {
104            return Err(SpecializationError::UnsupportedGenericArg);
105        }
106        Self::new(context.as_type_specialization_context(), &long_id.generic_args)
107    }
108
109    /// Returns the StructConcreteType of the given type, or a specialization error if not possible.
110    pub fn try_from_concrete_type(
111        context: &dyn SignatureSpecializationContext,
112        ty: &ConcreteTypeId,
113    ) -> Result<Self, SpecializationError> {
114        let long_id = context.get_type_info(ty.clone())?.long_id;
115        Self::try_from_long_id(context, &long_id)
116    }
117}
118impl ConcreteType for StructConcreteType {
119    fn info(&self) -> &TypeInfo {
120        &self.info
121    }
122}
123
124define_libfunc_hierarchy! {
125    pub enum StructLibfunc {
126        Construct(StructConstructLibfunc),
127        Deconstruct(StructDeconstructLibfunc),
128        SnapshotDeconstruct(StructSnapshotDeconstructLibfunc),
129    }, StructConcreteLibfunc
130}
131
132/// Libfunc for constructing a struct.
133#[derive(Default)]
134pub struct StructConstructLibfunc {}
135impl SignatureOnlyGenericLibfunc for StructConstructLibfunc {
136    const STR_ID: &'static str = "struct_construct";
137
138    fn specialize_signature(
139        &self,
140        context: &dyn SignatureSpecializationContext,
141        args: &[GenericArg],
142    ) -> Result<LibfuncSignature, SpecializationError> {
143        let struct_type = args_as_single_type(args)?;
144        let type_info = context.get_type_info(struct_type.clone())?;
145        let member_types =
146            StructConcreteType::try_from_long_id(context, &type_info.long_id)?.members;
147
148        let mut opt_same_as_param_idx = None;
149        for (idx, ty) in member_types.iter().cloned().enumerate() {
150            if !context.get_type_info(ty)?.zero_sized {
151                if opt_same_as_param_idx.is_some() {
152                    // There are multiple non-zero sized items, can't use the same param.
153                    opt_same_as_param_idx = None;
154                    break;
155                }
156                opt_same_as_param_idx = Some(idx);
157            }
158        }
159
160        Ok(LibfuncSignature::new_non_branch_ex(
161            member_types
162                .into_iter()
163                .map(|ty| ParamSignature {
164                    ty,
165                    allow_deferred: true,
166                    allow_add_const: true,
167                    allow_const: true,
168                })
169                .collect(),
170            vec![OutputVarInfo {
171                ty: struct_type,
172                ref_info: if type_info.zero_sized {
173                    OutputVarReferenceInfo::ZeroSized
174                } else if let Some(param_idx) = opt_same_as_param_idx {
175                    OutputVarReferenceInfo::SameAsParam { param_idx }
176                } else {
177                    OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic)
178                },
179            }],
180            SierraApChange::Known { new_vars_only: true },
181        ))
182    }
183}
184
185/// Libfunc for deconstructing a struct.
186#[derive(Default)]
187pub struct StructDeconstructLibfunc {}
188impl SignatureOnlyGenericLibfunc for StructDeconstructLibfunc {
189    const STR_ID: &'static str = "struct_deconstruct";
190
191    fn specialize_signature(
192        &self,
193        context: &dyn SignatureSpecializationContext,
194        args: &[GenericArg],
195    ) -> Result<LibfuncSignature, SpecializationError> {
196        let struct_type = args_as_single_type(args)?;
197        let member_types =
198            StructConcreteType::try_from_concrete_type(context, &struct_type)?.members;
199        Ok(LibfuncSignature::new_non_branch_ex(
200            vec![ParamSignature {
201                ty: struct_type,
202                allow_deferred: true,
203                allow_add_const: false,
204                allow_const: true,
205            }],
206            member_types
207                .into_iter()
208                .map(|ty| {
209                    Ok(OutputVarInfo {
210                        ty: ty.clone(),
211                        ref_info: if context.get_type_info(ty)?.zero_sized {
212                            OutputVarReferenceInfo::ZeroSized
213                        } else {
214                            // All memory of the deconstruction would have the same lifetime as the
215                            // first param - as it is its deconstruction.
216                            OutputVarReferenceInfo::PartialParam { param_idx: 0 }
217                        },
218                    })
219                })
220                .collect::<Result<Vec<_>, _>>()?,
221            SierraApChange::Known { new_vars_only: true },
222        ))
223    }
224}
225
226/// Libfunc for deconstructing a struct snapshot.
227#[derive(Default)]
228pub struct StructSnapshotDeconstructLibfunc {}
229impl SignatureOnlyGenericLibfunc for StructSnapshotDeconstructLibfunc {
230    const STR_ID: &'static str = "struct_snapshot_deconstruct";
231
232    fn specialize_signature(
233        &self,
234        context: &dyn SignatureSpecializationContext,
235        args: &[GenericArg],
236    ) -> Result<LibfuncSignature, SpecializationError> {
237        let struct_type = args_as_single_type(args)?;
238        let member_types =
239            StructConcreteType::try_from_concrete_type(context, &struct_type)?.members;
240        Ok(LibfuncSignature::new_non_branch(
241            vec![snapshot_ty(context, struct_type)?],
242            member_types
243                .into_iter()
244                .map(|ty| {
245                    Ok(OutputVarInfo {
246                        ty: snapshot_ty(context, ty.clone())?,
247                        ref_info: if context.get_type_info(ty)?.zero_sized {
248                            OutputVarReferenceInfo::ZeroSized
249                        } else {
250                            // All memory of the deconstruction would have the same lifetime as the
251                            // first param - as it is its deconstruction.
252                            OutputVarReferenceInfo::PartialParam { param_idx: 0 }
253                        },
254                    })
255                })
256                .collect::<Result<Vec<_>, _>>()?,
257            SierraApChange::Known { new_vars_only: true },
258        ))
259    }
260}