Skip to main content

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        wrapped_info: &TypeInfo,
35    ) -> Result<TypeInfo, SpecializationError> {
36        if wrapped_info.storable {
37            Ok(TypeInfo {
38                long_id,
39                zero_sized: false,
40                storable: true,
41                droppable: wrapped_info.droppable,
42                duplicatable: wrapped_info.duplicatable,
43            })
44        } else {
45            Err(SpecializationError::UnsupportedGenericArg)
46        }
47    }
48}
49pub type NullableType = GenericTypeArgGenericTypeWrapper<NullableTypeWrapped>;
50
51pub struct NullableConcreteType {
52    pub info: TypeInfo,
53    pub ty: ConcreteTypeId,
54}
55impl ConcreteType for NullableConcreteType {
56    fn info(&self) -> &TypeInfo {
57        &self.info
58    }
59}
60
61/// Helper for getting the type `Nullable<T>`.
62pub fn nullable_ty(
63    context: &dyn SignatureSpecializationContext,
64    ty: ConcreteTypeId,
65) -> Result<ConcreteTypeId, SpecializationError> {
66    context.get_wrapped_concrete_type(NullableType::id(), ty)
67}
68
69define_libfunc_hierarchy! {
70    pub enum NullableLibfunc {
71        Null(NullLibfunc),
72        NullableFromBox(NullableFromBoxLibfunc),
73        MatchNullable(MatchNullableLibfunc),
74        ForwardSnapshot(NullableForwardSnapshotLibfunc),
75    }, NullableConcreteLibfunc
76}
77
78/// Libfunc for creating a null object of type `Nullable<T>`.
79#[derive(Default)]
80pub struct NullLibfunc {}
81impl SignatureOnlyGenericLibfunc for NullLibfunc {
82    const STR_ID: &'static str = "null";
83
84    fn specialize_signature(
85        &self,
86        context: &dyn SignatureSpecializationContext,
87        args: &[GenericArg],
88    ) -> Result<LibfuncSignature, SpecializationError> {
89        let ty = args_as_single_type(args)?;
90        Ok(LibfuncSignature::new_non_branch(
91            vec![],
92            vec![OutputVarInfo {
93                ty: nullable_ty(context, ty.clone())?,
94                ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::Generic),
95            }],
96            SierraApChange::Known { new_vars_only: true },
97        ))
98    }
99}
100
101/// Libfunc for converting `Box<T>` to `Nullable<T>`.
102#[derive(Default)]
103pub struct NullableFromBoxLibfuncWrapped {}
104impl SignatureAndTypeGenericLibfunc for NullableFromBoxLibfuncWrapped {
105    const STR_ID: &'static str = "nullable_from_box";
106
107    fn specialize_signature(
108        &self,
109        context: &dyn SignatureSpecializationContext,
110        ty: ConcreteTypeId,
111    ) -> Result<LibfuncSignature, SpecializationError> {
112        Ok(reinterpret_cast_signature(box_ty(context, ty.clone())?, nullable_ty(context, ty)?))
113    }
114}
115pub type NullableFromBoxLibfunc = WrapSignatureAndTypeGenericLibfunc<NullableFromBoxLibfuncWrapped>;
116
117/// Libfunc for converting `Nullable<T>` to either `Box<T>` or nothing (in the case of `null`).
118#[derive(Default)]
119pub struct MatchNullableLibfuncWrapped {}
120impl SignatureAndTypeGenericLibfunc for MatchNullableLibfuncWrapped {
121    const STR_ID: &'static str = "match_nullable";
122
123    fn specialize_signature(
124        &self,
125        context: &dyn SignatureSpecializationContext,
126        ty: ConcreteTypeId,
127    ) -> Result<LibfuncSignature, SpecializationError> {
128        Ok(LibfuncSignature {
129            param_signatures: vec![ParamSignature::new(nullable_ty(context, ty.clone())?)],
130            branch_signatures: vec![
131                // `null`.
132                BranchSignature {
133                    vars: vec![],
134                    ap_change: SierraApChange::Known { new_vars_only: true },
135                },
136                // `Box<T>`.
137                BranchSignature {
138                    vars: vec![OutputVarInfo {
139                        ty: box_ty(context, ty)?,
140                        ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 0 },
141                    }],
142                    ap_change: SierraApChange::Known { new_vars_only: true },
143                },
144            ],
145            fallthrough: Some(0),
146        })
147    }
148}
149pub type MatchNullableLibfunc = WrapSignatureAndTypeGenericLibfunc<MatchNullableLibfuncWrapped>;
150
151/// Libfunc for converting `@Nullable<T>` into `Nullable<@T>`.
152#[derive(Default)]
153pub struct NullableForwardSnapshotLibfuncWrapped {}
154impl SignatureAndTypeGenericLibfunc for NullableForwardSnapshotLibfuncWrapped {
155    const STR_ID: &'static str = "nullable_forward_snapshot";
156    fn specialize_signature(
157        &self,
158        context: &dyn SignatureSpecializationContext,
159        ty: ConcreteTypeId,
160    ) -> Result<LibfuncSignature, SpecializationError> {
161        Ok(reinterpret_cast_signature(
162            snapshot_ty(context, nullable_ty(context, ty.clone())?)?,
163            nullable_ty(context, snapshot_ty(context, ty)?)?,
164        ))
165    }
166}
167
168pub type NullableForwardSnapshotLibfunc =
169    WrapSignatureAndTypeGenericLibfunc<NullableForwardSnapshotLibfuncWrapped>;