cairo_lang_sierra/extensions/modules/
structure.rs1use cairo_lang_utils::try_extract_matches;
15
16use super::snapshot::snapshot_ty;
17use super::utils::peel_snapshot;
18use crate::define_libfunc_hierarchy;
19use crate::extensions::boxing::box_ty;
20use crate::extensions::lib_func::{
21 DeferredOutputKind, LibfuncSignature, OutputVarInfo, ParamSignature, SierraApChange,
22 SignatureOnlyGenericLibfunc, SignatureSpecializationContext, SpecializationContext,
23};
24use crate::extensions::type_specialization_context::TypeSpecializationContext;
25use crate::extensions::types::TypeInfo;
26use crate::extensions::utils::ty_with_optional_snapshot;
27use crate::extensions::{
28 ConcreteType, NamedLibfunc, NamedType, OutputVarReferenceInfo, SignatureBasedConcreteLibfunc,
29 SpecializationError, args_as_single_type,
30};
31use crate::ids::{ConcreteTypeId, GenericTypeId};
32use crate::program::{ConcreteTypeLongId, GenericArg};
33
34#[derive(Default)]
36pub struct StructType {}
37impl NamedType for StructType {
38 type Concrete = StructConcreteType;
39 const ID: GenericTypeId = GenericTypeId::new_inline("Struct");
40
41 fn specialize(
42 &self,
43 context: &dyn TypeSpecializationContext,
44 args: &[GenericArg],
45 ) -> Result<Self::Concrete, SpecializationError> {
46 Self::Concrete::new(context, args)
47 }
48}
49
50pub struct StructConcreteType {
51 pub info: TypeInfo,
52 pub members: Vec<ConcreteTypeId>,
53}
54impl StructConcreteType {
55 fn new(
56 context: &dyn TypeSpecializationContext,
57 args: &[GenericArg],
58 ) -> Result<Self, SpecializationError> {
59 let mut args_iter = args.iter();
60 args_iter
61 .next()
62 .and_then(|arg| try_extract_matches!(arg, GenericArg::UserType))
63 .ok_or(SpecializationError::UnsupportedGenericArg)?;
64 let mut duplicatable = true;
65 let mut droppable = true;
66 let mut storable = true;
67 let mut members: Vec<ConcreteTypeId> = Vec::with_capacity(args_iter.len());
68 let mut zero_sized = true;
69 for arg in args_iter {
70 let ty = try_extract_matches!(arg, GenericArg::Type)
71 .ok_or(SpecializationError::UnsupportedGenericArg)?
72 .clone();
73 let info = context.get_type_info(&ty)?;
74 if !info.storable {
75 storable = false;
76 }
77 if !info.duplicatable {
78 duplicatable = false;
79 }
80 if !info.droppable {
81 droppable = false;
82 }
83 zero_sized = zero_sized && info.zero_sized;
84 members.push(ty);
85 }
86 Ok(StructConcreteType {
87 info: TypeInfo {
88 long_id: ConcreteTypeLongId {
89 generic_id: "Struct".into(),
90 generic_args: args.to_vec(),
91 },
92 duplicatable,
93 droppable,
94 storable,
95 zero_sized,
96 },
97 members,
98 })
99 }
100
101 fn try_from_long_id(
104 context: &dyn SignatureSpecializationContext,
105 long_id: &ConcreteTypeLongId,
106 ) -> Result<Self, SpecializationError> {
107 if long_id.generic_id != StructType::ID {
108 return Err(SpecializationError::UnsupportedGenericArg);
109 }
110 Self::new(context, &long_id.generic_args)
111 }
112
113 pub fn try_from_concrete_type(
115 context: &dyn SignatureSpecializationContext,
116 ty: &ConcreteTypeId,
117 ) -> Result<Self, SpecializationError> {
118 Self::try_from_long_id(context, &context.get_type_info(ty)?.long_id)
119 }
120}
121impl ConcreteType for StructConcreteType {
122 fn info(&self) -> &TypeInfo {
123 &self.info
124 }
125}
126
127define_libfunc_hierarchy! {
128 pub enum StructLibfunc {
129 Construct(StructConstructLibfunc),
130 Deconstruct(StructDeconstructLibfunc),
131 SnapshotDeconstruct(StructSnapshotDeconstructLibfunc),
132 BoxedDeconstruct(StructBoxedDeconstructLibfunc),
133 }, StructConcreteLibfunc
134}
135
136#[derive(Default)]
138pub struct StructConstructLibfunc {}
139impl SignatureOnlyGenericLibfunc for StructConstructLibfunc {
140 const STR_ID: &'static str = "struct_construct";
141
142 fn specialize_signature(
143 &self,
144 context: &dyn SignatureSpecializationContext,
145 args: &[GenericArg],
146 ) -> Result<LibfuncSignature, SpecializationError> {
147 let struct_type = args_as_single_type(args)?;
148 let type_info = context.get_type_info(struct_type)?;
149 let member_types =
150 StructConcreteType::try_from_long_id(context, &type_info.long_id)?.members;
151
152 let mut opt_same_as_param_idx = None;
153 for (idx, ty) in member_types.iter().enumerate() {
154 if !context.get_type_info(ty)?.zero_sized {
155 if opt_same_as_param_idx.is_some() {
156 opt_same_as_param_idx = None;
158 break;
159 }
160 opt_same_as_param_idx = Some(idx);
161 }
162 }
163
164 Ok(LibfuncSignature::new_non_branch_ex(
165 member_types
166 .into_iter()
167 .map(|ty| ParamSignature {
168 ty,
169 allow_deferred: true,
170 allow_add_const: true,
171 allow_const: true,
172 })
173 .collect(),
174 vec![OutputVarInfo {
175 ty: struct_type.clone(),
176 ref_info: if type_info.zero_sized {
177 OutputVarReferenceInfo::ZeroSized
178 } else if let Some(param_idx) = opt_same_as_param_idx {
179 OutputVarReferenceInfo::SameAsParam { param_idx }
180 } else {
181 OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic)
182 },
183 }],
184 SierraApChange::Known { new_vars_only: true },
185 ))
186 }
187}
188
189#[derive(Default)]
191pub struct StructDeconstructLibfunc {}
192impl SignatureOnlyGenericLibfunc for StructDeconstructLibfunc {
193 const STR_ID: &'static str = "struct_deconstruct";
194
195 fn specialize_signature(
196 &self,
197 context: &dyn SignatureSpecializationContext,
198 args: &[GenericArg],
199 ) -> Result<LibfuncSignature, SpecializationError> {
200 let struct_type = args_as_single_type(args)?;
201 let member_types =
202 StructConcreteType::try_from_concrete_type(context, struct_type)?.members;
203 Ok(LibfuncSignature::new_non_branch_ex(
204 vec![ParamSignature {
205 ty: struct_type.clone(),
206 allow_deferred: true,
207 allow_add_const: false,
208 allow_const: true,
209 }],
210 member_types
211 .into_iter()
212 .map(|ty| {
213 Ok(OutputVarInfo {
214 ref_info: if context.get_type_info(&ty)?.zero_sized {
215 OutputVarReferenceInfo::ZeroSized
216 } else {
217 OutputVarReferenceInfo::PartialParam { param_idx: 0 }
220 },
221 ty,
222 })
223 })
224 .collect::<Result<Vec<_>, _>>()?,
225 SierraApChange::Known { new_vars_only: true },
226 ))
227 }
228}
229
230#[derive(Default)]
232pub struct StructSnapshotDeconstructLibfunc {}
233impl SignatureOnlyGenericLibfunc for StructSnapshotDeconstructLibfunc {
234 const STR_ID: &'static str = "struct_snapshot_deconstruct";
235
236 fn specialize_signature(
237 &self,
238 context: &dyn SignatureSpecializationContext,
239 args: &[GenericArg],
240 ) -> Result<LibfuncSignature, SpecializationError> {
241 let struct_type = args_as_single_type(args)?;
242 let member_types =
243 StructConcreteType::try_from_concrete_type(context, struct_type)?.members;
244 Ok(LibfuncSignature::new_non_branch(
245 vec![snapshot_ty(context, struct_type.clone())?],
246 member_types
247 .into_iter()
248 .map(|ty| {
249 Ok(OutputVarInfo {
250 ref_info: if context.get_type_info(&ty)?.zero_sized {
251 OutputVarReferenceInfo::ZeroSized
252 } else {
253 OutputVarReferenceInfo::PartialParam { param_idx: 0 }
256 },
257 ty: snapshot_ty(context, ty)?,
258 })
259 })
260 .collect::<Result<Vec<_>, _>>()?,
261 SierraApChange::Known { new_vars_only: true },
262 ))
263 }
264}
265
266pub struct ConcreteStructBoxedDeconstructLibfunc {
268 pub members: Vec<ConcreteTypeId>,
271 signature: LibfuncSignature,
272}
273
274impl SignatureBasedConcreteLibfunc for ConcreteStructBoxedDeconstructLibfunc {
275 fn signature(&self) -> &LibfuncSignature {
276 &self.signature
277 }
278}
279
280#[derive(Default)]
282pub struct StructBoxedDeconstructLibfunc {}
283
284impl StructBoxedDeconstructLibfunc {
285 fn analyze_struct_type(
295 context: &dyn SignatureSpecializationContext,
296 ty: &ConcreteTypeId,
297 ) -> Result<(Vec<ConcreteTypeId>, bool), SpecializationError> {
298 let type_info = context.get_type_info(ty)?;
299 let (inner_ty, is_snapshot) = peel_snapshot(ty, type_info)?;
300 let struct_type = StructConcreteType::try_from_concrete_type(context, inner_ty)?;
301 Ok((struct_type.members, is_snapshot))
302 }
303
304 fn create_signature(
315 context: &dyn SignatureSpecializationContext,
316 ty: ConcreteTypeId,
317 mut member_types: impl ExactSizeIterator<Item = ConcreteTypeId>,
318 is_snapshot: bool,
319 ) -> Result<LibfuncSignature, SpecializationError> {
320 let mut outputs = Vec::with_capacity(member_types.len());
321
322 for member_ty in member_types.by_ref() {
323 let ref_info = OutputVarReferenceInfo::SameAsParam { param_idx: 0 };
324 outputs.push(OutputVarInfo {
325 ty: box_ty(
326 context,
327 ty_with_optional_snapshot(context, member_ty.clone(), is_snapshot)?,
328 )?,
329 ref_info,
330 });
331 if !context.get_type_info(&member_ty)?.zero_sized {
332 break;
333 }
334 }
335
336 for member_ty in member_types {
337 let ref_info = OutputVarReferenceInfo::Deferred(DeferredOutputKind::AddConst);
338 outputs.push(OutputVarInfo {
339 ty: box_ty(context, ty_with_optional_snapshot(context, member_ty, is_snapshot)?)?,
340 ref_info,
341 });
342 }
343
344 Ok(LibfuncSignature::new_non_branch_ex(
345 vec![ParamSignature::new(box_ty(context, ty)?).with_allow_add_const()],
346 outputs,
347 SierraApChange::Known { new_vars_only: true },
348 ))
349 }
350}
351
352impl NamedLibfunc for StructBoxedDeconstructLibfunc {
353 type Concrete = ConcreteStructBoxedDeconstructLibfunc;
354 const STR_ID: &'static str = "struct_boxed_deconstruct";
355
356 fn specialize_signature(
357 &self,
358 context: &dyn SignatureSpecializationContext,
359 args: &[GenericArg],
360 ) -> Result<LibfuncSignature, SpecializationError> {
361 let ty = args_as_single_type(args)?;
362 let (member_types, is_snapshot) = Self::analyze_struct_type(context, ty)?;
363 Self::create_signature(context, ty.clone(), member_types.into_iter(), is_snapshot)
364 }
365
366 fn specialize(
367 &self,
368 context: &dyn SpecializationContext,
369 args: &[GenericArg],
370 ) -> Result<Self::Concrete, SpecializationError> {
371 let ty = args_as_single_type(args)?;
372 let (members, is_snapshot) = Self::analyze_struct_type(context, ty)?;
373 let signature =
374 Self::create_signature(context, ty.clone(), members.iter().cloned(), is_snapshot)?;
375 Ok(ConcreteStructBoxedDeconstructLibfunc { members, signature })
376 }
377}