Skip to main content

cairo_lang_sierra/extensions/
types.rs

1use super::args_as_single_type;
2use super::error::{ExtensionError, SpecializationError};
3use super::type_specialization_context::TypeSpecializationContext;
4use crate::ids::{ConcreteTypeId, GenericTypeId};
5use crate::program::{ConcreteTypeLongId, GenericArg};
6
7/// Trait for implementing a specialization generator for types.
8pub trait GenericType: Sized {
9    type Concrete: ConcreteType;
10
11    /// Instantiates the type by id.
12    fn by_id(id: &GenericTypeId) -> Option<Self>;
13    /// Creates the specialization with the template arguments.
14    fn specialize(
15        &self,
16        context: &dyn TypeSpecializationContext,
17        args: &[GenericArg],
18    ) -> Result<Self::Concrete, SpecializationError>;
19}
20
21/// Trait for introducing helper methods on GenericType.
22pub trait GenericTypeEx: GenericType {
23    fn specialize_by_id(
24        context: &dyn TypeSpecializationContext,
25        type_id: &GenericTypeId,
26        args: &[GenericArg],
27    ) -> Result<Self::Concrete, ExtensionError>;
28}
29impl<TGenericType: GenericType> GenericTypeEx for TGenericType {
30    fn specialize_by_id(
31        context: &dyn TypeSpecializationContext,
32        type_id: &GenericTypeId,
33        args: &[GenericArg],
34    ) -> Result<TGenericType::Concrete, ExtensionError> {
35        Self::by_id(type_id)
36            .ok_or_else(move || ExtensionError::TypeSpecialization {
37                type_id: type_id.clone(),
38                error: SpecializationError::UnsupportedId(type_id.0.clone()),
39            })?
40            .specialize(context, args)
41            .map_err(move |error| ExtensionError::TypeSpecialization {
42                type_id: type_id.clone(),
43                error,
44            })
45    }
46}
47
48/// Trait for implementing a specialization generator with a simple id.
49pub trait NamedType: Default {
50    type Concrete: ConcreteType;
51    const ID: GenericTypeId;
52    /// Returns the generic id of named types.
53    fn id() -> GenericTypeId {
54        Self::ID
55    }
56    /// Returns the long ID of the concrete type with `ID` as the generic ID and the given args.
57    fn concrete_type_long_id(generic_args: &[GenericArg]) -> ConcreteTypeLongId {
58        ConcreteTypeLongId { generic_id: Self::id(), generic_args: generic_args.to_vec() }
59    }
60    /// Creates the specialization with the template arguments.
61    fn specialize(
62        &self,
63        context: &dyn TypeSpecializationContext,
64        args: &[GenericArg],
65    ) -> Result<Self::Concrete, SpecializationError>;
66}
67impl<TNamedType: NamedType> GenericType for TNamedType {
68    type Concrete = <Self as NamedType>::Concrete;
69
70    fn by_id(id: &GenericTypeId) -> Option<Self> {
71        if &Self::ID == id { Some(Self::default()) } else { None }
72    }
73
74    fn specialize(
75        &self,
76        context: &dyn TypeSpecializationContext,
77        args: &[GenericArg],
78    ) -> Result<Self::Concrete, SpecializationError> {
79        <Self as NamedType>::specialize(self, context, args)
80    }
81}
82
83/// Trait for describing a generic type with no generic arguments.
84pub trait NoGenericArgsGenericType: Default {
85    const ID: GenericTypeId;
86    const STORABLE: bool;
87    const DUPLICATABLE: bool;
88    const DROPPABLE: bool;
89    const ZERO_SIZED: bool;
90}
91impl<T: NoGenericArgsGenericType> NamedType for T {
92    type Concrete = InfoOnlyConcreteType;
93    const ID: GenericTypeId = <Self as NoGenericArgsGenericType>::ID;
94
95    fn specialize(
96        &self,
97        _context: &dyn TypeSpecializationContext,
98        args: &[GenericArg],
99    ) -> Result<Self::Concrete, SpecializationError> {
100        if args.is_empty() {
101            Ok(Self::Concrete {
102                info: TypeInfo {
103                    long_id: Self::concrete_type_long_id(args),
104                    storable: T::STORABLE,
105                    droppable: T::DROPPABLE,
106                    duplicatable: T::DUPLICATABLE,
107                    zero_sized: T::ZERO_SIZED,
108                },
109            })
110        } else {
111            Err(SpecializationError::WrongNumberOfGenericArgs)
112        }
113    }
114}
115
116/// Trait for describing a generic type with a single type arg.
117pub trait GenericTypeArgGenericType: Default {
118    const ID: GenericTypeId;
119
120    /// Returns the type info of the wrapping type.
121    fn calc_info(
122        &self,
123        context: &dyn TypeSpecializationContext,
124        long_id: ConcreteTypeLongId,
125        wrapped_info: &TypeInfo,
126    ) -> Result<TypeInfo, SpecializationError>;
127}
128
129/// Wrapper for a specialization generator with a single type arg.
130#[derive(Default)]
131pub struct GenericTypeArgGenericTypeWrapper<T: GenericTypeArgGenericType>(T);
132impl<T: GenericTypeArgGenericType> NamedType for GenericTypeArgGenericTypeWrapper<T> {
133    type Concrete = InfoAndTypeConcreteType;
134    const ID: GenericTypeId = T::ID;
135
136    fn specialize(
137        &self,
138        context: &dyn TypeSpecializationContext,
139        args: &[GenericArg],
140    ) -> Result<Self::Concrete, SpecializationError> {
141        let ty = args_as_single_type(args)?;
142        let long_id = Self::concrete_type_long_id(args);
143        let wrapped_info = context.get_type_info(ty)?;
144        Ok(Self::Concrete {
145            info: self.0.calc_info(context, long_id, wrapped_info)?,
146            ty: ty.clone(),
147        })
148    }
149}
150
151/// Information on Sierra types required for generic libfunc calls.
152#[derive(Clone, Debug, Eq, PartialEq)]
153pub struct TypeInfo {
154    /// The long ID of the concrete type.
155    pub long_id: ConcreteTypeLongId,
156    /// Can the type be stored by any of the store commands.
157    pub storable: bool,
158    /// Can the type be (trivially) dropped.
159    pub droppable: bool,
160    /// Can the type be (trivially) duplicated.
161    pub duplicatable: bool,
162    /// Is the type zero sized.
163    pub zero_sized: bool,
164}
165
166/// Trait for a specialized type.
167pub trait ConcreteType {
168    fn info(&self) -> &TypeInfo;
169}
170
171/// Struct providing a ConcreteType only with the type info - should not be implemented for
172/// concrete types that require any extra data.
173pub struct InfoOnlyConcreteType {
174    pub info: TypeInfo,
175}
176
177impl ConcreteType for InfoOnlyConcreteType {
178    fn info(&self) -> &TypeInfo {
179        &self.info
180    }
181}
182
183/// Struct providing a ConcreteType with the type info and a wrapped type.
184pub struct InfoAndTypeConcreteType {
185    pub info: TypeInfo,
186    pub ty: ConcreteTypeId,
187}
188
189impl ConcreteType for InfoAndTypeConcreteType {
190    fn info(&self) -> &TypeInfo {
191        &self.info
192    }
193}
194
195/// Forms a Sierra type used by extensions type from an enum of such types.
196/// The new enum implements GenericType.
197/// All the variant types must also implement GenericType.
198/// Usage example:
199/// ```ignore
200/// define_type_hierarchy! {
201///     pub enum MyType {
202///       Ty0(Type0),
203///       Ty1(Type1),
204///     }, MyTypeConcrete
205/// }
206/// ```
207#[macro_export]
208macro_rules! define_type_hierarchy {
209    (pub enum $name:ident { $($variant_name:ident ($variant:ty),)* },
210    $concrete_name:ident) => {
211        #[allow(clippy::enum_variant_names)]
212        pub enum $name {
213            $($variant_name ($variant)),*
214        }
215
216        impl $crate::extensions::types::GenericType for $name {
217            type Concrete = $concrete_name;
218            fn by_id(id: &$crate::ids::GenericTypeId) -> Option<Self> {
219                $(
220                    if let Some(res) = <$variant>::by_id(id){
221                        return Some(Self::$variant_name(res));
222                    }
223                )*
224                None
225            }
226            fn specialize(
227                    &self,
228                    context: &dyn $crate::extensions::type_specialization_context::TypeSpecializationContext,
229                    args: &[$crate::program::GenericArg]
230            ) -> Result<Self::Concrete, $crate::extensions::SpecializationError>{
231                match self {
232                    $(
233                        Self::$variant_name(value) => {
234                            Ok(Self::Concrete::$variant_name(
235                                <$variant as $crate::extensions::GenericType>::specialize(
236                                    value, context, args,
237                                )?
238                                .into(),
239                            ))
240                        }
241                    ),*
242                }
243            }
244        }
245
246        pub enum $concrete_name {
247            $($variant_name (<$variant as $crate::extensions::GenericType> ::Concrete),)*
248        }
249        impl $crate::extensions::ConcreteType for $concrete_name {
250            fn info(&self) -> &$crate::extensions::types::TypeInfo {
251                match self {
252                    $(Self::$variant_name(value) => value.info()),*
253                }
254            }
255        }
256    }
257}