cairo_lang_sierra/extensions/modules/
nullable.rs

1use super::boxing::box_ty;
2use super::snapshot::snapshot_ty;
3use super::utils::reinterpret_cast_signature;
4use crate::define_libfunc_hierarchy;
5use crate::extensions::lib_func::{
6    BranchSignature, DeferredOutputKind, LibfuncSignature, OutputVarInfo, ParamSignature,
7    SierraApChange, SignatureAndTypeGenericLibfunc, SignatureOnlyGenericLibfunc,
8    SignatureSpecializationContext, WrapSignatureAndTypeGenericLibfunc,
9};
10use crate::extensions::type_specialization_context::TypeSpecializationContext;
11use crate::extensions::types::{
12    GenericTypeArgGenericType, GenericTypeArgGenericTypeWrapper, TypeInfo,
13};
14use crate::extensions::{
15    ConcreteType, NamedType, OutputVarReferenceInfo, SpecializationError, args_as_single_type,
16};
17use crate::ids::{ConcreteTypeId, GenericTypeId};
18use crate::program::GenericArg;
19
20/// A type that holds a possibly-null pointer to an object.
21///
22/// It behaves exactly like `Option<Box<T>>`, except that it only uses 1 memory cell (rather than 2
23/// in `Option<Box<T>>`) - the value is 0 if and only if there is no object.
24///
25/// This type uses the fact that Casm pointers can never be zero.
26#[derive(Default)]
27pub struct NullableTypeWrapped {}
28impl GenericTypeArgGenericType for NullableTypeWrapped {
29    const ID: GenericTypeId = GenericTypeId::new_inline("Nullable");
30    fn calc_info(
31        &self,
32        _context: &dyn TypeSpecializationContext,
33        long_id: crate::program::ConcreteTypeLongId,
34        TypeInfo { storable, droppable, duplicatable, .. }: TypeInfo,
35    ) -> Result<TypeInfo, SpecializationError> {
36        if storable {
37            Ok(TypeInfo { long_id, zero_sized: false, storable: true, droppable, duplicatable })
38        } else {
39            Err(SpecializationError::UnsupportedGenericArg)
40        }
41    }
42}
43pub type NullableType = GenericTypeArgGenericTypeWrapper<NullableTypeWrapped>;
44
45pub struct NullableConcreteType {
46    pub info: TypeInfo,
47    pub ty: ConcreteTypeId,
48}
49impl ConcreteType for NullableConcreteType {
50    fn info(&self) -> &TypeInfo {
51        &self.info
52    }
53}
54
55/// Helper for getting the type `Nullable<T>`.
56pub fn nullable_ty(
57    context: &dyn SignatureSpecializationContext,
58    ty: ConcreteTypeId,
59) -> Result<ConcreteTypeId, SpecializationError> {
60    context.get_wrapped_concrete_type(NullableType::id(), ty)
61}
62
63define_libfunc_hierarchy! {
64    pub enum NullableLibfunc {
65        Null(NullLibfunc),
66        NullableFromBox(NullableFromBoxLibfunc),
67        MatchNullable(MatchNullableLibfunc),
68        ForwardSnapshot(NullableForwardSnapshotLibfunc),
69    }, NullableConcreteLibfunc
70}
71
72/// Libfunc for creating a null object of type `Nullable<T>`.
73#[derive(Default)]
74pub struct NullLibfunc {}
75impl SignatureOnlyGenericLibfunc for NullLibfunc {
76    const STR_ID: &'static str = "null";
77
78    fn specialize_signature(
79        &self,
80        context: &dyn SignatureSpecializationContext,
81        args: &[GenericArg],
82    ) -> Result<LibfuncSignature, SpecializationError> {
83        let ty = args_as_single_type(args)?;
84        Ok(LibfuncSignature::new_non_branch(
85            vec![],
86            vec![OutputVarInfo {
87                ty: nullable_ty(context, ty)?,
88                ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic),
89            }],
90            SierraApChange::Known { new_vars_only: true },
91        ))
92    }
93}
94
95/// Libfunc for converting `Box<T>` to `Nullable<T>`.
96#[derive(Default)]
97pub struct NullableFromBoxLibfuncWrapped {}
98impl SignatureAndTypeGenericLibfunc for NullableFromBoxLibfuncWrapped {
99    const STR_ID: &'static str = "nullable_from_box";
100
101    fn specialize_signature(
102        &self,
103        context: &dyn SignatureSpecializationContext,
104        ty: ConcreteTypeId,
105    ) -> Result<LibfuncSignature, SpecializationError> {
106        Ok(reinterpret_cast_signature(box_ty(context, ty.clone())?, nullable_ty(context, ty)?))
107    }
108}
109pub type NullableFromBoxLibfunc = WrapSignatureAndTypeGenericLibfunc<NullableFromBoxLibfuncWrapped>;
110
111/// Libfunc for converting `Nullable<T>` to either `Box<T>` or nothing (in the case of `null`).
112#[derive(Default)]
113pub struct MatchNullableLibfuncWrapped {}
114impl SignatureAndTypeGenericLibfunc for MatchNullableLibfuncWrapped {
115    const STR_ID: &'static str = "match_nullable";
116
117    fn specialize_signature(
118        &self,
119        context: &dyn SignatureSpecializationContext,
120        ty: ConcreteTypeId,
121    ) -> Result<LibfuncSignature, SpecializationError> {
122        Ok(LibfuncSignature {
123            param_signatures: vec![ParamSignature::new(nullable_ty(context, ty.clone())?)],
124            branch_signatures: vec![
125                // `null`.
126                BranchSignature {
127                    vars: vec![],
128                    ap_change: SierraApChange::Known { new_vars_only: true },
129                },
130                // `Box<T>`.
131                BranchSignature {
132                    vars: vec![OutputVarInfo {
133                        ty: box_ty(context, ty)?,
134                        ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 0 },
135                    }],
136                    ap_change: SierraApChange::Known { new_vars_only: true },
137                },
138            ],
139            fallthrough: Some(0),
140        })
141    }
142}
143pub type MatchNullableLibfunc = WrapSignatureAndTypeGenericLibfunc<MatchNullableLibfuncWrapped>;
144
145/// Libfunc for converting `@Nullable<T>` into `Nullable<@T>`.
146#[derive(Default)]
147pub struct NullableForwardSnapshotLibfuncWrapped {}
148impl SignatureAndTypeGenericLibfunc for NullableForwardSnapshotLibfuncWrapped {
149    const STR_ID: &'static str = "nullable_forward_snapshot";
150    fn specialize_signature(
151        &self,
152        context: &dyn SignatureSpecializationContext,
153        ty: ConcreteTypeId,
154    ) -> Result<LibfuncSignature, SpecializationError> {
155        Ok(reinterpret_cast_signature(
156            snapshot_ty(context, nullable_ty(context, ty.clone())?)?,
157            nullable_ty(context, snapshot_ty(context, ty)?)?,
158        ))
159    }
160}
161
162pub type NullableForwardSnapshotLibfunc =
163    WrapSignatureAndTypeGenericLibfunc<NullableForwardSnapshotLibfuncWrapped>;