cairo_lang_sierra/extensions/modules/
felt252_dict.rs

1use super::enm::EnumType;
2use super::felt252::Felt252Type;
3use super::gas::GasBuiltinType;
4use super::int::unsigned::{Uint8Type, Uint16Type, Uint32Type, Uint64Type};
5use super::int::unsigned128::Uint128Type;
6use super::nullable::NullableType;
7use super::range_check::RangeCheckType;
8use super::segment_arena::SegmentArenaType;
9use super::squashed_felt252_dict::SquashedFelt252DictType;
10use crate::define_libfunc_hierarchy;
11use crate::extensions::lib_func::{
12    DeferredOutputKind, LibfuncSignature, OutputVarInfo, ParamSignature, SierraApChange,
13    SignatureAndTypeGenericLibfunc, SignatureOnlyGenericLibfunc, SignatureSpecializationContext,
14    WrapSignatureAndTypeGenericLibfunc,
15};
16use crate::extensions::type_specialization_context::TypeSpecializationContext;
17use crate::extensions::types::{
18    GenericTypeArgGenericType, GenericTypeArgGenericTypeWrapper, TypeInfo,
19};
20use crate::extensions::{
21    NamedType, OutputVarReferenceInfo, SpecializationError, args_as_single_type,
22};
23use crate::ids::{ConcreteTypeId, GenericTypeId};
24use crate::program::{ConcreteTypeLongId, GenericArg};
25
26/// Type representing a dictionary from a felt252 to types of size one.
27///
28/// This is currently only bounded for all numeric types, Nullable, and Enum types with 2 or less
29/// variants, as this are the types that has proper default as 0, and therefore can be properly used
30/// as a value in the dictionary.
31#[derive(Default)]
32pub struct Felt252DictTypeWrapped {}
33impl GenericTypeArgGenericType for Felt252DictTypeWrapped {
34    const ID: GenericTypeId = GenericTypeId::new_inline("Felt252Dict");
35
36    fn calc_info(
37        &self,
38        context: &dyn TypeSpecializationContext,
39        long_id: crate::program::ConcreteTypeLongId,
40        wrapped_type_info: TypeInfo,
41    ) -> Result<TypeInfo, SpecializationError> {
42        specialize_with_dict_value_param(context, long_id, wrapped_type_info)
43    }
44}
45
46/// Specializes a generic type matching a dictionary value parameter.
47/// Called for both `Felt252Dict` and `Felt252DictEntry`.
48fn specialize_with_dict_value_param(
49    context: &dyn TypeSpecializationContext,
50    long_id: ConcreteTypeLongId,
51    TypeInfo {
52        long_id: ConcreteTypeLongId { generic_id, generic_args },
53        storable,
54        droppable,
55        duplicatable: _,
56        zero_sized: _,
57    }: TypeInfo,
58) -> Result<TypeInfo, SpecializationError> {
59    // Checking for specific types allowed as dictionary values.
60    // TODO(Gil): Check in the higher level compiler and raise proper diagnostic (when we'll
61    // have a 'where' equivalent).
62    // TODO(Gil): Allow any type of size 1 which implement the 'Felt252DictValue' trait.
63    let allowed = match generic_id {
64        id if id == Felt252Type::id() => generic_args.is_empty(),
65        id if id == Uint8Type::id() => generic_args.is_empty(),
66        id if id == Uint16Type::id() => generic_args.is_empty(),
67        id if id == Uint32Type::id() => generic_args.is_empty(),
68        id if id == Uint64Type::id() => generic_args.is_empty(),
69        id if id == Uint128Type::id() => generic_args.is_empty(),
70        id if id == NullableType::id() => generic_args.len() == 1,
71        id if id == EnumType::id() => {
72            // Checking the enum type is valid.
73            !generic_args.is_empty()
74            // Zero is not a valid value for enums with 3 or more variants, so they cannot be
75            // used in a dict (whose default value is zero).
76            // (the additional argument is the user type).
77            && generic_args.len() <= 3
78                // All contained types must be 0-sized, so the total size will be exactly 1.
79                && generic_args.into_iter().skip(1).all(|arg| {
80                    let GenericArg::Type(ty) = arg else {
81                        return false;
82                    };
83                    let Ok(info) = context.get_type_info(ty) else {
84                        return false;
85                    };
86                    info.zero_sized
87                })
88        }
89        _ => false,
90    };
91    if allowed && storable && droppable {
92        Ok(TypeInfo {
93            long_id,
94            duplicatable: false,
95            droppable: false,
96            storable: true,
97            zero_sized: false,
98        })
99    } else {
100        Err(SpecializationError::UnsupportedGenericArg)
101    }
102}
103pub type Felt252DictType = GenericTypeArgGenericTypeWrapper<Felt252DictTypeWrapped>;
104
105define_libfunc_hierarchy! {
106    pub enum Felt252DictLibfunc {
107        New(Felt252DictNewLibfunc),
108        Squash(Felt252DictSquashLibfunc),
109    }, Felt252DictConcreteLibfunc
110}
111
112/// Libfunc for creating a new felt252_dict.
113#[derive(Default)]
114pub struct Felt252DictNewLibfunc {}
115impl SignatureOnlyGenericLibfunc for Felt252DictNewLibfunc {
116    const STR_ID: &'static str = "felt252_dict_new";
117
118    fn specialize_signature(
119        &self,
120        context: &dyn SignatureSpecializationContext,
121        args: &[GenericArg],
122    ) -> Result<LibfuncSignature, SpecializationError> {
123        let ty = args_as_single_type(args)?;
124        let segment_arena_ty = context.get_concrete_type(SegmentArenaType::id(), &[])?;
125        Ok(LibfuncSignature::new_non_branch_ex(
126            vec![ParamSignature::new(segment_arena_ty.clone()).with_allow_add_const()],
127            vec![
128                OutputVarInfo::new_builtin(segment_arena_ty, 0),
129                OutputVarInfo {
130                    ty: context.get_wrapped_concrete_type(Felt252DictType::id(), ty)?,
131                    ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic),
132                },
133            ],
134            SierraApChange::Known { new_vars_only: false },
135        ))
136    }
137}
138
139/// Libfunc for performing a `squash` operation on a dict. Returns a pointer to the squashed dict.
140#[derive(Default)]
141pub struct Felt252DictSquashLibfunc {}
142impl SignatureOnlyGenericLibfunc for Felt252DictSquashLibfunc {
143    const STR_ID: &'static str = "felt252_dict_squash";
144
145    fn specialize_signature(
146        &self,
147        context: &dyn SignatureSpecializationContext,
148        args: &[GenericArg],
149    ) -> Result<LibfuncSignature, SpecializationError> {
150        let generic_ty = args_as_single_type(args)?;
151        let dict_ty =
152            context.get_wrapped_concrete_type(Felt252DictType::id(), generic_ty.clone())?;
153        let squashed_dict_ty =
154            context.get_wrapped_concrete_type(SquashedFelt252DictType::id(), generic_ty)?;
155        let range_check_type = context.get_concrete_type(RangeCheckType::id(), &[])?;
156        let gas_builtin_type = context.get_concrete_type(GasBuiltinType::id(), &[])?;
157        let segment_arena_ty = context.get_concrete_type(SegmentArenaType::id(), &[])?;
158        Ok(LibfuncSignature::new_non_branch(
159            vec![
160                range_check_type.clone(),
161                gas_builtin_type.clone(),
162                segment_arena_ty.clone(),
163                dict_ty,
164            ],
165            vec![
166                OutputVarInfo {
167                    ty: range_check_type,
168                    ref_info: OutputVarReferenceInfo::NewTempVar { idx: 0 },
169                },
170                OutputVarInfo {
171                    ty: gas_builtin_type,
172                    ref_info: OutputVarReferenceInfo::NewTempVar { idx: 1 },
173                },
174                OutputVarInfo {
175                    ty: segment_arena_ty,
176                    ref_info: OutputVarReferenceInfo::NewTempVar { idx: 2 },
177                },
178                OutputVarInfo {
179                    ty: squashed_dict_ty,
180                    ref_info: OutputVarReferenceInfo::NewTempVar { idx: 3 },
181                },
182            ],
183            SierraApChange::Unknown,
184        ))
185    }
186}
187
188/// Type representing an entry access to a felt252_dict.
189#[derive(Default)]
190pub struct Felt252DictEntryTypeWrapped {}
191impl GenericTypeArgGenericType for Felt252DictEntryTypeWrapped {
192    const ID: GenericTypeId = GenericTypeId::new_inline("Felt252DictEntry");
193
194    fn calc_info(
195        &self,
196        context: &dyn TypeSpecializationContext,
197        long_id: crate::program::ConcreteTypeLongId,
198        wrapped_type_info: TypeInfo,
199    ) -> Result<TypeInfo, SpecializationError> {
200        specialize_with_dict_value_param(context, long_id, wrapped_type_info)
201    }
202}
203pub type Felt252DictEntryType = GenericTypeArgGenericTypeWrapper<Felt252DictEntryTypeWrapped>;
204
205define_libfunc_hierarchy! {
206    pub enum Felt252DictEntryLibfunc {
207        Get(Felt252DictEntryGetLibfunc),
208        Finalize(Felt252DictEntryFinalizeLibfunc),
209    }, Felt252DictEntryConcreteLibfunc
210}
211
212/// Libfunc for creating a new felt252_dict_entry, it owns the dictionary until finalized.
213#[derive(Default)]
214pub struct Felt252DictEntryGetLibfuncWrapped {}
215impl SignatureAndTypeGenericLibfunc for Felt252DictEntryGetLibfuncWrapped {
216    const STR_ID: &'static str = "felt252_dict_entry_get";
217
218    fn specialize_signature(
219        &self,
220        context: &dyn SignatureSpecializationContext,
221        ty: ConcreteTypeId,
222    ) -> Result<LibfuncSignature, SpecializationError> {
223        let dict_ty = context.get_wrapped_concrete_type(Felt252DictType::id(), ty.clone())?;
224        let dict_entry_ty =
225            context.get_wrapped_concrete_type(Felt252DictEntryType::id(), ty.clone())?;
226        let felt252_ty = context.get_concrete_type(Felt252Type::id(), &[])?;
227        Ok(LibfuncSignature::new_non_branch_ex(
228            vec![
229                ParamSignature::new(dict_ty).with_allow_add_const(),
230                // Key.
231                ParamSignature::new(felt252_ty),
232            ],
233            vec![
234                OutputVarInfo {
235                    ty: dict_entry_ty,
236                    ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::AddConst {
237                        param_idx: 0,
238                    }),
239                },
240                // Current value.
241                OutputVarInfo {
242                    ty,
243                    ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic),
244                },
245            ],
246            SierraApChange::Known { new_vars_only: true },
247        ))
248    }
249}
250pub type Felt252DictEntryGetLibfunc =
251    WrapSignatureAndTypeGenericLibfunc<Felt252DictEntryGetLibfuncWrapped>;
252
253/// Libfunc for finalizing a felt252_dict_entry, returns the owned dict.
254#[derive(Default)]
255pub struct Felt252DictEntryFinalizeLibfuncWrapped {}
256impl SignatureAndTypeGenericLibfunc for Felt252DictEntryFinalizeLibfuncWrapped {
257    const STR_ID: &'static str = "felt252_dict_entry_finalize";
258
259    fn specialize_signature(
260        &self,
261        context: &dyn SignatureSpecializationContext,
262        ty: ConcreteTypeId,
263    ) -> Result<LibfuncSignature, SpecializationError> {
264        let dict_ty = context.get_wrapped_concrete_type(Felt252DictType::id(), ty.clone())?;
265        let dict_entry_ty =
266            context.get_wrapped_concrete_type(Felt252DictEntryType::id(), ty.clone())?;
267        Ok(LibfuncSignature::new_non_branch_ex(
268            vec![
269                ParamSignature::new(dict_entry_ty).with_allow_add_const(),
270                // New value.
271                ParamSignature::new(ty),
272            ],
273            vec![OutputVarInfo {
274                ty: dict_ty,
275                ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 0 },
276            }],
277            SierraApChange::Known { new_vars_only: true },
278        ))
279    }
280}
281
282pub type Felt252DictEntryFinalizeLibfunc =
283    WrapSignatureAndTypeGenericLibfunc<Felt252DictEntryFinalizeLibfuncWrapped>;