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.clone())?;
144        Ok(Self::Concrete { info: self.0.calc_info(context, long_id, wrapped_info)?, ty })
145    }
146}
147
148/// Information on Sierra types required for generic libfunc calls.
149#[derive(Clone, Debug, Eq, PartialEq)]
150pub struct TypeInfo {
151    /// The long ID of the concrete type.
152    pub long_id: ConcreteTypeLongId,
153    /// Can the type be stored by any of the store commands.
154    pub storable: bool,
155    /// Can the type be (trivially) dropped.
156    pub droppable: bool,
157    /// Can the type be (trivially) duplicated.
158    pub duplicatable: bool,
159    /// Is the type zero sized.
160    pub zero_sized: bool,
161}
162
163/// Trait for a specialized type.
164pub trait ConcreteType {
165    fn info(&self) -> &TypeInfo;
166}
167
168/// Struct providing a ConcreteType only with the type info - should not be implemented for
169/// concrete types that require any extra data.
170pub struct InfoOnlyConcreteType {
171    pub info: TypeInfo,
172}
173
174impl ConcreteType for InfoOnlyConcreteType {
175    fn info(&self) -> &TypeInfo {
176        &self.info
177    }
178}
179
180/// Struct providing a ConcreteType with the type info and a wrapped type.
181pub struct InfoAndTypeConcreteType {
182    pub info: TypeInfo,
183    pub ty: ConcreteTypeId,
184}
185
186impl ConcreteType for InfoAndTypeConcreteType {
187    fn info(&self) -> &TypeInfo {
188        &self.info
189    }
190}
191
192/// Forms a Sierra type used by extensions type from an enum of such types.
193/// The new enum implements GenericType.
194/// All the variant types must also implement GenericType.
195/// Usage example:
196/// ```ignore
197/// define_type_hierarchy! {
198///     pub enum MyType {
199///       Ty0(Type0),
200///       Ty1(Type1),
201///     }, MyTypeConcrete
202/// }
203/// ```
204#[macro_export]
205macro_rules! define_type_hierarchy {
206    (pub enum $name:ident { $($variant_name:ident ($variant:ty),)* },
207    $concrete_name:ident) => {
208        #[allow(clippy::enum_variant_names)]
209        pub enum $name {
210            $($variant_name ($variant)),*
211        }
212
213        impl $crate::extensions::types::GenericType for $name {
214            type Concrete = $concrete_name;
215            fn by_id(id: &$crate::ids::GenericTypeId) -> Option<Self> {
216                $(
217                    if let Some(res) = <$variant>::by_id(id){
218                        return Some(Self::$variant_name(res));
219                    }
220                )*
221                None
222            }
223            fn specialize(
224                    &self,
225                    context: &dyn $crate::extensions::type_specialization_context::TypeSpecializationContext,
226                    args: &[$crate::program::GenericArg]
227            ) -> Result<Self::Concrete, $crate::extensions::SpecializationError>{
228                match self {
229                    $(
230                        Self::$variant_name(value) => {
231                            Ok(Self::Concrete::$variant_name(
232                                <$variant as $crate::extensions::GenericType>::specialize(
233                                    value, context, args,
234                                )?
235                                .into(),
236                            ))
237                        }
238                    ),*
239                }
240            }
241        }
242
243        pub enum $concrete_name {
244            $($variant_name (<$variant as $crate::extensions::GenericType> ::Concrete),)*
245        }
246        impl $crate::extensions::ConcreteType for $concrete_name {
247            fn info(&self) -> &$crate::extensions::types::TypeInfo {
248                match self {
249                    $(Self::$variant_name(value) => value.info()),*
250                }
251            }
252        }
253    }
254}