cairo_lang_sierra/extensions/
lib_func.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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
use itertools::Itertools;

use super::args_as_single_type;
use super::error::{ExtensionError, SpecializationError};
use super::type_specialization_context::TypeSpecializationContext;
use crate::ids::{ConcreteTypeId, FunctionId, GenericLibfuncId, GenericTypeId};
use crate::program::{Function, FunctionSignature, GenericArg};

/// Trait for the specialization of libfunc signatures.
pub trait SignatureSpecializationContext: TypeSpecializationContext {
    /// Returns concrete type id given a generic type and the generic arguments.
    fn try_get_concrete_type(
        &self,
        id: GenericTypeId,
        generic_args: &[GenericArg],
    ) -> Option<ConcreteTypeId>;

    /// Wraps [Self::try_get_concrete_type] with a result object.
    fn get_concrete_type(
        &self,
        id: GenericTypeId,
        generic_args: &[GenericArg],
    ) -> Result<ConcreteTypeId, SpecializationError> {
        self.try_get_concrete_type(id.clone(), generic_args)
            .ok_or_else(|| SpecializationError::TypeWasNotDeclared(id, generic_args.to_vec()))
    }

    /// Returns the function's signature object associated with the given [FunctionId].
    fn try_get_function_signature(&self, function_id: &FunctionId) -> Option<FunctionSignature>;

    /// Wraps [Self::try_get_function_signature] with a result object.
    fn get_function_signature(
        &self,
        function_id: &FunctionId,
    ) -> Result<FunctionSignature, SpecializationError> {
        self.try_get_function_signature(function_id)
            .ok_or_else(|| SpecializationError::MissingFunction(function_id.clone()))
    }

    /// Returns the ap-change of the given function.
    fn try_get_function_ap_change(&self, function_id: &FunctionId) -> Option<SierraApChange>;

    /// Wraps [Self::try_get_function_ap_change] with a result object.
    fn get_function_ap_change(
        &self,
        function_id: &FunctionId,
    ) -> Result<SierraApChange, SpecializationError> {
        self.try_get_function_ap_change(function_id)
            .ok_or_else(|| SpecializationError::MissingFunction(function_id.clone()))
    }

    /// Returns the concrete id of `T<S>` given generic type T and concrete type S.
    fn get_wrapped_concrete_type(
        &self,
        id: GenericTypeId,
        wrapped: ConcreteTypeId,
    ) -> Result<ConcreteTypeId, SpecializationError> {
        self.get_concrete_type(id, &[GenericArg::Type(wrapped)])
    }

    /// Upcasting to the [TypeSpecializationContext], since trait upcasting is still experimental.
    fn as_type_specialization_context(&self) -> &dyn TypeSpecializationContext;
}

/// Trait for the specialization of full libfuncs.
pub trait SpecializationContext: SignatureSpecializationContext {
    /// Upcasting to the [SignatureSpecializationContext], since trait upcasting is still
    /// experimental.
    fn upcast(&self) -> &dyn SignatureSpecializationContext;

    /// Returns the function object associated with the given [FunctionId].
    fn try_get_function(&self, function_id: &FunctionId) -> Option<Function>;

    /// Wraps [Self::try_get_function] with a result object.
    fn get_function(&self, function_id: &FunctionId) -> Result<Function, SpecializationError> {
        self.try_get_function(function_id)
            .ok_or_else(|| SpecializationError::MissingFunction(function_id.clone()))
    }
}

/// Trait for implementing a libfunc specialization generator.
pub trait GenericLibfunc: Sized {
    type Concrete: ConcreteLibfunc;

    /// Returns the list of generic libfuncs ids that can be instantiated through this type.
    /// This is useful on hierarchical libfunc aggregates such as `CoreLibfunc`.
    fn supported_ids() -> Vec<GenericLibfuncId>;

    /// Instantiates the libfunc by id.
    fn by_id(id: &GenericLibfuncId) -> Option<Self>;

    /// Creates the specialization of the libfunc's signature with the template arguments.
    fn specialize_signature(
        &self,
        context: &dyn SignatureSpecializationContext,
        args: &[GenericArg],
    ) -> Result<LibfuncSignature, SpecializationError>;

    /// Creates the specialization with the template arguments.
    fn specialize(
        &self,
        context: &dyn SpecializationContext,
        args: &[GenericArg],
    ) -> Result<Self::Concrete, SpecializationError>;
}

/// Trait for introducing helper methods on [GenericLibfunc].
pub trait GenericLibfuncEx: GenericLibfunc {
    fn specialize_signature_by_id(
        context: &dyn SignatureSpecializationContext,
        libfunc_id: &GenericLibfuncId,
        args: &[GenericArg],
    ) -> Result<LibfuncSignature, ExtensionError>;

    fn specialize_by_id(
        context: &dyn SpecializationContext,
        libfunc_id: &GenericLibfuncId,
        args: &[GenericArg],
    ) -> Result<Self::Concrete, ExtensionError>;
}
impl<TGenericLibfunc: GenericLibfunc> GenericLibfuncEx for TGenericLibfunc {
    fn specialize_signature_by_id(
        context: &dyn SignatureSpecializationContext,
        libfunc_id: &GenericLibfuncId,
        generic_args: &[GenericArg],
    ) -> Result<LibfuncSignature, ExtensionError> {
        if let Some(generic_libfunc) = Self::by_id(libfunc_id) {
            generic_libfunc.specialize_signature(context, generic_args)
        } else {
            Err(SpecializationError::UnsupportedId(libfunc_id.0.clone()))
        }
        .map_err(move |error| ExtensionError::LibfuncSpecialization {
            libfunc_id: libfunc_id.clone(),
            generic_args: generic_args.iter().cloned().collect_vec(),
            error,
        })
    }

    fn specialize_by_id(
        context: &dyn SpecializationContext,
        libfunc_id: &GenericLibfuncId,
        generic_args: &[GenericArg],
    ) -> Result<TGenericLibfunc::Concrete, ExtensionError> {
        if let Some(generic_libfunc) = Self::by_id(libfunc_id) {
            generic_libfunc.specialize(context, generic_args)
        } else {
            Err(SpecializationError::UnsupportedId(libfunc_id.0.clone()))
        }
        .map_err(move |error| ExtensionError::LibfuncSpecialization {
            libfunc_id: libfunc_id.clone(),
            generic_args: generic_args.iter().cloned().collect_vec(),
            error,
        })
    }
}

/// Trait for implementing a specialization generator with a simple id.
pub trait NamedLibfunc: Default {
    type Concrete: ConcreteLibfunc;
    const STR_ID: &'static str;

    /// Creates the specialization of the libfunc's signature with the template arguments.
    fn specialize_signature(
        &self,
        context: &dyn SignatureSpecializationContext,
        args: &[GenericArg],
    ) -> Result<LibfuncSignature, SpecializationError>;

    /// Creates the specialization with the template arguments.
    fn specialize(
        &self,
        context: &dyn SpecializationContext,
        args: &[GenericArg],
    ) -> Result<Self::Concrete, SpecializationError>;
}
impl<TNamedLibfunc: NamedLibfunc> GenericLibfunc for TNamedLibfunc {
    type Concrete = <Self as NamedLibfunc>::Concrete;

    fn supported_ids() -> Vec<GenericLibfuncId> {
        vec![GenericLibfuncId::from(Self::STR_ID)]
    }

    fn by_id(id: &GenericLibfuncId) -> Option<Self> {
        if Self::STR_ID == id.0 { Some(Self::default()) } else { None }
    }

    fn specialize_signature(
        &self,
        context: &dyn SignatureSpecializationContext,
        args: &[GenericArg],
    ) -> Result<LibfuncSignature, SpecializationError> {
        self.specialize_signature(context, args)
    }

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

/// Trait for implementing a specialization generator not holding anything more than a signature.
pub trait SignatureOnlyGenericLibfunc: Default {
    const STR_ID: &'static str;

    fn specialize_signature(
        &self,
        context: &dyn SignatureSpecializationContext,
        args: &[GenericArg],
    ) -> Result<LibfuncSignature, SpecializationError>;
}

impl<T: SignatureOnlyGenericLibfunc> NamedLibfunc for T {
    type Concrete = SignatureOnlyConcreteLibfunc;
    const STR_ID: &'static str = <Self as SignatureOnlyGenericLibfunc>::STR_ID;

    fn specialize_signature(
        &self,
        context: &dyn SignatureSpecializationContext,
        args: &[GenericArg],
    ) -> Result<LibfuncSignature, SpecializationError> {
        self.specialize_signature(context, args)
    }

    fn specialize(
        &self,
        context: &dyn SpecializationContext,
        args: &[GenericArg],
    ) -> Result<Self::Concrete, SpecializationError> {
        Ok(SignatureOnlyConcreteLibfunc {
            signature: self.specialize_signature(context.upcast(), args)?,
        })
    }
}

/// Trait for implementing a specialization generator expecting a single generic param type, and
/// creating a concrete libfunc containing that type as well.
pub trait SignatureAndTypeGenericLibfunc: Default {
    const STR_ID: &'static str;

    fn specialize_signature(
        &self,
        context: &dyn SignatureSpecializationContext,
        ty: ConcreteTypeId,
    ) -> Result<LibfuncSignature, SpecializationError>;
}

/// Wrapper to prevent implementation collisions for [NamedLibfunc].
#[derive(Default)]
pub struct WrapSignatureAndTypeGenericLibfunc<T: SignatureAndTypeGenericLibfunc>(T);

impl<T: SignatureAndTypeGenericLibfunc> NamedLibfunc for WrapSignatureAndTypeGenericLibfunc<T> {
    type Concrete = SignatureAndTypeConcreteLibfunc;
    const STR_ID: &'static str = <T as SignatureAndTypeGenericLibfunc>::STR_ID;

    fn specialize_signature(
        &self,
        context: &dyn SignatureSpecializationContext,
        args: &[GenericArg],
    ) -> Result<LibfuncSignature, SpecializationError> {
        self.0.specialize_signature(context, args_as_single_type(args)?)
    }

    fn specialize(
        &self,
        context: &dyn SpecializationContext,
        args: &[GenericArg],
    ) -> Result<Self::Concrete, SpecializationError> {
        let ty = args_as_single_type(args)?;
        Ok(SignatureAndTypeConcreteLibfunc {
            ty: ty.clone(),
            signature: self.0.specialize_signature(context.upcast(), ty)?,
        })
    }
}

/// Trait for implementing a specialization generator with no generic arguments.
pub trait NoGenericArgsGenericLibfunc: Default {
    const STR_ID: &'static str;

    fn specialize_signature(
        &self,
        context: &dyn SignatureSpecializationContext,
    ) -> Result<LibfuncSignature, SpecializationError>;
}
impl<T: NoGenericArgsGenericLibfunc> SignatureOnlyGenericLibfunc for T {
    const STR_ID: &'static str = <Self as NoGenericArgsGenericLibfunc>::STR_ID;

    fn specialize_signature(
        &self,
        context: &dyn SignatureSpecializationContext,
        args: &[GenericArg],
    ) -> Result<LibfuncSignature, SpecializationError> {
        if args.is_empty() {
            self.specialize_signature(context)
        } else {
            Err(SpecializationError::WrongNumberOfGenericArgs)
        }
    }
}

/// Information regarding a parameter of the libfunc.
#[derive(Clone)]
pub struct ParamSignature {
    /// The type of the parameter.
    pub ty: ConcreteTypeId,
    /// Whether the libfunc argument can be an expression of the form `[ap/fp + i] + [ap/fp + j]`.
    /// For example, `store_temp()` and `store_local()`.
    pub allow_deferred: bool,
    /// Whether the libfunc argument can be an expression of the form `[ap + i] + const`.
    pub allow_add_const: bool,
    /// Whether the libfunc argument can be a constant.
    pub allow_const: bool,
}
impl ParamSignature {
    /// Returns a [ParamSignature] with default attributes.
    pub fn new(ty: ConcreteTypeId) -> Self {
        Self { ty, allow_add_const: false, allow_deferred: false, allow_const: false }
    }

    /// Returns a modified version of [ParamSignature], with the `allow_deferred` flag set.
    pub fn with_allow_deferred(mut self) -> Self {
        self.allow_deferred = true;
        self
    }

    /// Returns a modified version of [ParamSignature], with the `allow_add_const` flag set.
    pub fn with_allow_add_const(mut self) -> Self {
        self.allow_add_const = true;
        self
    }

    /// Returns a modified version of [ParamSignature], with the `allow_const` flag set.
    pub fn with_allow_const(mut self) -> Self {
        self.allow_const = true;
        self
    }

    /// Returns a modified version of [ParamSignature], with all attributes set.
    pub fn with_allow_all(mut self) -> Self {
        self.allow_add_const = true;
        self.allow_deferred = true;
        self.allow_const = true;
        self
    }
}
impl From<ConcreteTypeId> for ParamSignature {
    fn from(ty: ConcreteTypeId) -> Self {
        Self::new(ty)
    }
}

/// Information regarding the reference created as an output of a library function.
///
/// For example, whether the reference is equal to one of the parameters (as in the dup() function),
/// or whether it's newly allocated local variable.
#[derive(Debug, Clone)]
pub enum OutputVarReferenceInfo {
    /// The output value is exactly the same as one of the parameters.
    SameAsParam { param_idx: usize },
    /// The output value is a part of one of the parameters.
    /// For example, it may be the first element of a struct.
    ///
    /// Information, such as whether the parameter was a temporary or local variable, will be
    /// copied to the output variable.
    PartialParam { param_idx: usize },
    /// The output was allocated as a temporary variable and it is at the top of the stack
    /// (contiguously).
    NewTempVar {
        /// The index of the temporary variable in the stack (0 is the variable with the lowest
        /// memory address).
        idx: usize,
    },
    /// The output was allocated as a local variable.
    NewLocalVar,
    /// The output is the result of a computation. For example `[ap] + [fp]`,
    /// `[ap + 1] * [fp - 3]`, `[ap] + 3`, `7`.
    Deferred(DeferredOutputKind),
    /// All the output cells are of the form `[ap/fp + const]`. For example, `([ap + 1], [fp])`.
    SimpleDerefs,
    /// The output is a of size 0.
    ZeroSized,
}

/// The type of a deferred output.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum DeferredOutputKind {
    /// The output is a constant. For example, `7`.
    Const,
    /// The output is the addition of a constant to one of the parameters. For example, `x + 3`.
    AddConst { param_idx: usize },
    /// The output is not one of the above (e.g., `[ap] + [fp]`, `[ap + 1] * [fp - 3]`,
    /// `[ap] * 3`).
    Generic,
}

/// Contains information regarding an output variable in a single branch.
#[derive(Debug, Clone)]
pub struct OutputVarInfo {
    pub ty: ConcreteTypeId,
    pub ref_info: OutputVarReferenceInfo,
}
impl OutputVarInfo {
    /// Convenience function to get the common OutputVarInfo for builtins.
    pub fn new_builtin(builtin: ConcreteTypeId, param_idx: usize) -> Self {
        Self {
            ty: builtin,
            ref_info: OutputVarReferenceInfo::Deferred(DeferredOutputKind::AddConst { param_idx }),
        }
    }
}

/// Contains information on the variables returned in a single libfunc branch
/// for all the output variables in an output branch.
///
/// See [OutputVarInfo].
#[derive(Debug)]
pub struct BranchSignature {
    /// Information about the new variables created in the branch.
    pub vars: Vec<OutputVarInfo>,
    /// Information about the change in the `ap` register in the branch.
    pub ap_change: SierraApChange,
}

/// Describes the effect on the `ap` register in a given libfunc branch.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SierraApChange {
    /// The libfunc changes `ap` in an unknown way.
    Unknown,
    /// The libfunc changes `ap` in a known (during compilation) way.
    Known {
        /// `true` if all the new stack cells created by the libfunc are its output
        /// variables (as described in [OutputVarReferenceInfo::NewTempVar] in
        /// [`BranchSignature::vars`]).
        new_vars_only: bool,
    },
    /// The lib func is `branch_align`.
    /// The `ap` change is known during compilation.
    BranchAlign,
}
/// Trait for a specialized library function.
pub trait ConcreteLibfunc {
    /// The parameter types and other information for the parameters for calling a library
    /// function.
    fn param_signatures(&self) -> &[ParamSignature];
    /// The output types and other information returning from a library function per branch.
    fn branch_signatures(&self) -> &[BranchSignature];
    /// The index of the fallthrough branch of the library function if any.
    fn fallthrough(&self) -> Option<usize>;

    /// Returns the output types returning from a library function per branch.
    fn output_types(&self) -> Vec<Vec<ConcreteTypeId>> {
        self.branch_signatures()
            .iter()
            .map(|branch_info| {
                branch_info.vars.iter().map(|var_info| var_info.ty.clone()).collect()
            })
            .collect()
    }
}

/// Represents the signature of a library function.
pub struct LibfuncSignature {
    /// The parameter types and other information for the parameters for calling a library
    /// function.
    pub param_signatures: Vec<ParamSignature>,
    /// The output types and other information for the return values of a library function per
    /// branch.
    pub branch_signatures: Vec<BranchSignature>,
    /// The index of the fallthrough branch of the library function if any.
    pub fallthrough: Option<usize>,
}
impl LibfuncSignature {
    /// Creates a non branch signature.
    pub fn new_non_branch(
        input_types: Vec<ConcreteTypeId>,
        output_info: Vec<OutputVarInfo>,
        ap_change: SierraApChange,
    ) -> Self {
        Self::new_non_branch_ex(
            input_types.into_iter().map(ParamSignature::new).collect(),
            output_info,
            ap_change,
        )
    }

    /// Same as [LibfuncSignature::new_non_branch], except that more complicated [ParamSignature]
    /// are supported.
    pub fn new_non_branch_ex(
        param_signatures: Vec<ParamSignature>,
        output_info: Vec<OutputVarInfo>,
        ap_change: SierraApChange,
    ) -> LibfuncSignature {
        Self {
            param_signatures,
            branch_signatures: vec![BranchSignature { vars: output_info, ap_change }],
            fallthrough: Some(0),
        }
    }
}

/// Trait for implementing a [ConcreteLibfunc] that returns a reference to the full signature of the
/// library function.
pub trait SignatureBasedConcreteLibfunc {
    fn signature(&self) -> &LibfuncSignature;
}

impl<TSignatureBasedConcreteLibfunc: SignatureBasedConcreteLibfunc> ConcreteLibfunc
    for TSignatureBasedConcreteLibfunc
{
    fn param_signatures(&self) -> &[ParamSignature] {
        &self.signature().param_signatures
    }
    fn branch_signatures(&self) -> &[BranchSignature] {
        &self.signature().branch_signatures
    }
    fn fallthrough(&self) -> Option<usize> {
        self.signature().fallthrough
    }
}

/// Struct providing a [ConcreteLibfunc] only with a signature and a type.
pub struct SignatureAndTypeConcreteLibfunc {
    pub ty: ConcreteTypeId,
    pub signature: LibfuncSignature,
}
impl SignatureBasedConcreteLibfunc for SignatureAndTypeConcreteLibfunc {
    fn signature(&self) -> &LibfuncSignature {
        &self.signature
    }
}

/// Struct providing a [ConcreteLibfunc] only with a signature - should not be implemented for
/// concrete libfuncs that require any extra data.
pub struct SignatureOnlyConcreteLibfunc {
    pub signature: LibfuncSignature,
}
impl SignatureBasedConcreteLibfunc for SignatureOnlyConcreteLibfunc {
    fn signature(&self) -> &LibfuncSignature {
        &self.signature
    }
}

/// Forms a concrete library function type from an enum of library calls.
/// The new enum implements [ConcreteLibfunc].
/// All the variant types must also implement [ConcreteLibfunc].
/// Usage example:
/// ```ignore
/// define_concrete_libfunc_hierarchy! {
///     pub enum MyLibfunc {
///       LF0(Libfunc0),
///       LF1(Libfunc1),
///     }
/// }
/// ```
#[macro_export]
macro_rules! define_concrete_libfunc_hierarchy {
    (pub enum $name:ident $(<
        $generic_arg:ident : $generic_arg_first_req:ident $(+ $generic_arg_other_reqs:ident)*
    >)? {
        $($variant_name:ident ($variant:ty),)*
    }) => {
        #[allow(clippy::enum_variant_names)]
        pub enum $name $(< $generic_arg : $generic_arg_first_req $(+ $generic_arg_other_reqs)* >)? {
            $($variant_name ($variant),)*
        }
        impl $(< $generic_arg : $generic_arg_first_req $(+ $generic_arg_other_reqs)* >)?
            $crate::extensions::ConcreteLibfunc for $name $(< $generic_arg >)? {
            $crate::extensions::lib_func::concrete_method_impl! {
                fn param_signatures(&self) -> &[$crate::extensions::lib_func::ParamSignature] {
                    $($variant_name => $variant,)*
                }
            }
            $crate::extensions::lib_func::concrete_method_impl!{
                fn branch_signatures(&self) -> &[$crate::extensions::lib_func::BranchSignature] {
                    $($variant_name => $variant,)*
                }
            }
            $crate::extensions::lib_func::concrete_method_impl!{
                fn fallthrough(&self) -> Option<usize> {
                    $($variant_name => $variant,)*
                }
            }
        }
    }
}

/// Implements a method for an enum of library calls by recursively calling the enum option existing
/// implementation.
macro_rules! concrete_method_impl {
    (fn $method_name:ident(&self $(,$var_name:ident : $var:ty)*) -> $ret_type:ty {
        $($variant_name:ident => $variant:ty,)*
    }) => {
        fn $method_name(&self $(,$var_name:ident : $var:ty)*) -> $ret_type {
            match self {
                $(Self::$variant_name(value) => value.$method_name()),*
            }
        }
    }
}
pub(crate) use concrete_method_impl;

/// Forms a libfunc type from an enum of libfuncs.
/// The new enum implements [GenericLibfunc].
/// All the variant types must also implement [GenericLibfunc].
/// Usage example:
/// ```ignore
/// define_libfunc_hierarchy! {
///     pub enum MyLibfunc {
///       LF0(Libfunc0),
///       LF1(Libfunc1),
///     }, MyLibfuncConcrete
/// }
/// ```
#[macro_export]
macro_rules! define_libfunc_hierarchy {
    (pub enum $name:ident $(<
        $generic_arg:ident : $generic_arg_first_req:ident $(+ $generic_arg_other_reqs:ident)*
    >)? {
        $($variant_name:ident ($variant:ty),)*
    },
    $concrete_name:ident) => {
        #[allow(clippy::enum_variant_names)]
        pub enum $name $(< $generic_arg : $generic_arg_first_req $(+ $generic_arg_other_reqs)* >)? {
            $($variant_name ($variant)),*
        }

        impl $(< $generic_arg : $generic_arg_first_req $(+ $generic_arg_other_reqs)* >)?
            $crate::extensions::GenericLibfunc for $name $(< $generic_arg >)? {
            type Concrete = $concrete_name $(< $generic_arg >)?;
            fn supported_ids() -> Vec<$crate::ids::GenericLibfuncId> {
                itertools::chain!(
                    $(
                        <$variant as $crate::extensions::GenericLibfunc>::supported_ids()
                    ),*
                ).collect()
            }
            fn by_id(id: &$crate::ids::GenericLibfuncId) -> Option<Self> {
                $(
                    if let Some(res) = <$variant>::by_id(id){
                        return Some(Self::$variant_name(res));
                    }
                )*
                None
            }
            fn specialize_signature(
                    &self,
                    context: &dyn $crate::extensions::lib_func::SignatureSpecializationContext,
                    args: &[$crate::program::GenericArg],
            ) -> Result<
                    $crate::extensions::lib_func::LibfuncSignature,
                    $crate::extensions::SpecializationError
                >{
                match self {
                    $(
                        Self::$variant_name(value) => {
                            <$variant as $crate::extensions::GenericLibfunc>::specialize_signature(
                                value, context, args,
                            )
                        }
                    ),*
                }
            }
            fn specialize(
                    &self,
                    context: &dyn $crate::extensions::lib_func::SpecializationContext,
                    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::GenericLibfunc>::specialize(
                                    value, context, args,
                                )?
                                .into(),
                            ))
                        }
                    ),*
                }
            }
        }

        $crate::define_concrete_libfunc_hierarchy! {
            pub enum $concrete_name $(<
                $generic_arg : $generic_arg_first_req $(+ $generic_arg_other_reqs)*
            >)? {
                $($variant_name (<$variant as $crate::extensions::GenericLibfunc> ::Concrete),)*
            }
        }
    }
}