cairo_lang_sierra/extensions/
types.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
use super::args_as_single_type;
use super::error::{ExtensionError, SpecializationError};
use super::type_specialization_context::TypeSpecializationContext;
use crate::ids::{ConcreteTypeId, GenericTypeId};
use crate::program::{ConcreteTypeLongId, GenericArg};

/// Trait for implementing a specialization generator for types.
pub trait GenericType: Sized {
    type Concrete: ConcreteType;

    /// Instantiates the type by id.
    fn by_id(id: &GenericTypeId) -> Option<Self>;
    /// Creates the specialization with the template arguments.
    fn specialize(
        &self,
        context: &dyn TypeSpecializationContext,
        args: &[GenericArg],
    ) -> Result<Self::Concrete, SpecializationError>;
}

/// Trait for introducing helper methods on GenericType.
pub trait GenericTypeEx: GenericType {
    fn specialize_by_id(
        context: &dyn TypeSpecializationContext,
        type_id: &GenericTypeId,
        args: &[GenericArg],
    ) -> Result<Self::Concrete, ExtensionError>;
}
impl<TGenericType: GenericType> GenericTypeEx for TGenericType {
    fn specialize_by_id(
        context: &dyn TypeSpecializationContext,
        type_id: &GenericTypeId,
        args: &[GenericArg],
    ) -> Result<TGenericType::Concrete, ExtensionError> {
        Self::by_id(type_id)
            .ok_or_else(move || ExtensionError::TypeSpecialization {
                type_id: type_id.clone(),
                error: SpecializationError::UnsupportedId(type_id.0.clone()),
            })?
            .specialize(context, args)
            .map_err(move |error| ExtensionError::TypeSpecialization {
                type_id: type_id.clone(),
                error,
            })
    }
}

/// Trait for implementing a specialization generator with with a simple id.
pub trait NamedType: Default {
    type Concrete: ConcreteType;
    const ID: GenericTypeId;
    /// Returns the generic id of named types.
    fn id() -> GenericTypeId {
        Self::ID
    }
    /// Returns the long ID of the concrete type with `ID` as the generic ID and the given args.
    fn concrete_type_long_id(generic_args: &[GenericArg]) -> ConcreteTypeLongId {
        ConcreteTypeLongId { generic_id: Self::id(), generic_args: generic_args.to_vec() }
    }
    /// Creates the specialization with the template arguments.
    fn specialize(
        &self,
        context: &dyn TypeSpecializationContext,
        args: &[GenericArg],
    ) -> Result<Self::Concrete, SpecializationError>;
}
impl<TNamedType: NamedType> GenericType for TNamedType {
    type Concrete = <Self as NamedType>::Concrete;

    fn by_id(id: &GenericTypeId) -> Option<Self> {
        if &Self::ID == id { Some(Self::default()) } else { None }
    }

    fn specialize(
        &self,
        context: &dyn TypeSpecializationContext,
        args: &[GenericArg],
    ) -> Result<Self::Concrete, SpecializationError> {
        <Self as NamedType>::specialize(self, context, args)
    }
}

/// Trait for describing a generic type with no generic arguments.
pub trait NoGenericArgsGenericType: Default {
    const ID: GenericTypeId;
    const STORABLE: bool;
    const DUPLICATABLE: bool;
    const DROPPABLE: bool;
    const ZERO_SIZED: bool;
}
impl<T: NoGenericArgsGenericType> NamedType for T {
    type Concrete = InfoOnlyConcreteType;
    const ID: GenericTypeId = <Self as NoGenericArgsGenericType>::ID;

    fn specialize(
        &self,
        _context: &dyn TypeSpecializationContext,
        args: &[GenericArg],
    ) -> Result<Self::Concrete, SpecializationError> {
        if args.is_empty() {
            Ok(Self::Concrete {
                info: TypeInfo {
                    long_id: Self::concrete_type_long_id(args),
                    storable: T::STORABLE,
                    droppable: T::DROPPABLE,
                    duplicatable: T::DUPLICATABLE,
                    zero_sized: T::ZERO_SIZED,
                },
            })
        } else {
            Err(SpecializationError::WrongNumberOfGenericArgs)
        }
    }
}

/// Trait for describing a generic type with a single type arg.
pub trait GenericTypeArgGenericType: Default {
    const ID: GenericTypeId;

    /// Returns the type info of the wrapping type.
    fn calc_info(
        &self,
        context: &dyn TypeSpecializationContext,
        long_id: ConcreteTypeLongId,
        wrapped_info: TypeInfo,
    ) -> Result<TypeInfo, SpecializationError>;
}

/// Wrapper for a specialization generator with a single type arg.
#[derive(Default)]
pub struct GenericTypeArgGenericTypeWrapper<T: GenericTypeArgGenericType>(T);
impl<T: GenericTypeArgGenericType> NamedType for GenericTypeArgGenericTypeWrapper<T> {
    type Concrete = InfoAndTypeConcreteType;
    const ID: GenericTypeId = T::ID;

    fn specialize(
        &self,
        context: &dyn TypeSpecializationContext,
        args: &[GenericArg],
    ) -> Result<Self::Concrete, SpecializationError> {
        let ty = args_as_single_type(args)?;
        let long_id = Self::concrete_type_long_id(args);
        let wrapped_info = context.get_type_info(ty.clone())?;
        Ok(Self::Concrete { info: self.0.calc_info(context, long_id, wrapped_info)?, ty })
    }
}

/// Information on Sierra types required for generic libfunc calls.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TypeInfo {
    /// The long ID of the concrete type.
    pub long_id: ConcreteTypeLongId,
    /// Can the type be stored by any of the store commands.
    pub storable: bool,
    /// Can the type be (trivially) dropped.
    pub droppable: bool,
    /// Can the type be (trivially) duplicated.
    pub duplicatable: bool,
    /// Is the type zero sized.
    pub zero_sized: bool,
}

/// Trait for a specialized type.
pub trait ConcreteType {
    fn info(&self) -> &TypeInfo;
}

/// Struct providing a ConcreteType only with the type info - should not be implemented for
/// concrete types that require any extra data.
pub struct InfoOnlyConcreteType {
    pub info: TypeInfo,
}

impl ConcreteType for InfoOnlyConcreteType {
    fn info(&self) -> &TypeInfo {
        &self.info
    }
}

/// Struct providing a ConcreteType with the type info and a wrapped type.
pub struct InfoAndTypeConcreteType {
    pub info: TypeInfo,
    pub ty: ConcreteTypeId,
}

impl ConcreteType for InfoAndTypeConcreteType {
    fn info(&self) -> &TypeInfo {
        &self.info
    }
}

/// Forms a Sierra type used by extensions type from an enum of such types.
/// The new enum implements GenericType.
/// All the variant types must also implement GenericType.
/// Usage example:
/// ```ignore
/// define_type_hierarchy! {
///     pub enum MyType {
///       Ty0(Type0),
///       Ty1(Type1),
///     }, MyTypeConcrete
/// }
/// ```
#[macro_export]
macro_rules! define_type_hierarchy {
    (pub enum $name:ident { $($variant_name:ident ($variant:ty),)* },
    $concrete_name:ident) => {
        #[allow(clippy::enum_variant_names)]
        pub enum $name {
            $($variant_name ($variant)),*
        }

        impl $crate::extensions::types::GenericType for $name {
            type Concrete = $concrete_name;
            fn by_id(id: &$crate::ids::GenericTypeId) -> Option<Self> {
                $(
                    if let Some(res) = <$variant>::by_id(id){
                        return Some(Self::$variant_name(res));
                    }
                )*
                None
            }
            fn specialize(
                    &self,
                    context: &dyn $crate::extensions::type_specialization_context::TypeSpecializationContext,
                    args: &[$crate::program::GenericArg]
            ) -> Result<Self::Concrete, $crate::extensions::SpecializationError>{
                match self {
                    $(
                        Self::$variant_name(value) => {
                            Ok(Self::Concrete::$variant_name(
                                <$variant as $crate::extensions::GenericType>::specialize(
                                    value, context, args,
                                )?
                                .into(),
                            ))
                        }
                    ),*
                }
            }
        }

        pub enum $concrete_name {
            $($variant_name (<$variant as $crate::extensions::GenericType> ::Concrete),)*
        }
        impl $crate::extensions::ConcreteType for $concrete_name {
            fn info(&self) -> &$crate::extensions::types::TypeInfo {
                match self {
                    $(Self::$variant_name(value) => value.info()),*
                }
            }
        }
    }
}