cairo_lang_semantic/items/
constant.rs

1use std::iter::zip;
2use std::sync::Arc;
3
4use cairo_lang_debug::DebugWithDb;
5use cairo_lang_defs::ids::{
6    ConstantId, ExternFunctionId, GenericParamId, LanguageElementId, LookupItemId, ModuleItemId,
7    NamedLanguageElementId, TopLevelLanguageElementId, TraitConstantId, TraitId, VarId,
8};
9use cairo_lang_diagnostics::{
10    DiagnosticAdded, DiagnosticEntry, DiagnosticNote, Diagnostics, Maybe, ToMaybe, skip_diagnostic,
11};
12use cairo_lang_proc_macros::{DebugWithDb, SemanticObject};
13use cairo_lang_syntax::node::ast::ItemConstant;
14use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
15use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode};
16use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
17use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
18use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
19use cairo_lang_utils::{
20    Intern, LookupIntern, define_short_id, extract_matches, require, try_extract_matches,
21};
22use itertools::Itertools;
23use num_bigint::BigInt;
24use num_traits::{Num, ToPrimitive, Zero};
25use smol_str::SmolStr;
26
27use super::functions::{GenericFunctionId, GenericFunctionWithBodyId};
28use super::imp::{ImplId, ImplLongId};
29use crate::corelib::{
30    CoreInfo, LiteralError, core_box_ty, core_nonzero_ty, false_variant, get_core_ty_by_name,
31    true_variant, try_extract_nz_wrapped_type, unit_ty, validate_literal,
32};
33use crate::db::SemanticGroup;
34use crate::diagnostic::{SemanticDiagnosticKind, SemanticDiagnostics, SemanticDiagnosticsBuilder};
35use crate::expr::compute::{
36    ComputationContext, ContextFunction, Environment, ExprAndId, compute_expr_semantic,
37};
38use crate::expr::inference::conform::InferenceConform;
39use crate::expr::inference::{ConstVar, InferenceId};
40use crate::helper::ModuleHelper;
41use crate::items::enm::SemanticEnumEx;
42use crate::resolve::{Resolver, ResolverData};
43use crate::substitution::{GenericSubstitution, SemanticRewriter};
44use crate::types::resolve_type;
45use crate::{
46    Arenas, ConcreteFunction, ConcreteTypeId, ConcreteVariant, Condition, Expr, ExprBlock,
47    ExprConstant, ExprFunctionCall, ExprFunctionCallArg, ExprId, ExprMemberAccess, ExprStructCtor,
48    FunctionId, GenericParam, LogicalOperator, Pattern, PatternId, SemanticDiagnostic, Statement,
49    TypeId, TypeLongId, semantic_object_for_id,
50};
51
52#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
53#[debug_db(dyn SemanticGroup + 'static)]
54pub struct Constant {
55    /// The actual id of the const expression value.
56    pub value: ExprId,
57    /// The arena of all the expressions for the const calculation.
58    pub arenas: Arc<Arenas>,
59}
60
61impl Constant {
62    pub fn ty(&self) -> TypeId {
63        self.arenas.exprs[self.value].ty()
64    }
65}
66
67/// Information about a constant definition.
68///
69/// Helper struct for the data returned by [SemanticGroup::priv_constant_semantic_data].
70#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
71#[debug_db(dyn SemanticGroup + 'static)]
72pub struct ConstantData {
73    pub diagnostics: Diagnostics<SemanticDiagnostic>,
74    pub constant: Maybe<Constant>,
75    pub const_value: ConstValueId,
76    pub resolver_data: Arc<ResolverData>,
77}
78
79define_short_id!(
80    ConstValueId,
81    ConstValue,
82    SemanticGroup,
83    lookup_intern_const_value,
84    intern_const_value
85);
86semantic_object_for_id!(ConstValueId, lookup_intern_const_value, intern_const_value, ConstValue);
87impl ConstValueId {
88    pub fn format(&self, db: &dyn SemanticGroup) -> String {
89        format!("{:?}", self.lookup_intern(db).debug(db.elongate()))
90    }
91
92    /// Returns true if the const does not depend on any generics.
93    pub fn is_fully_concrete(&self, db: &dyn SemanticGroup) -> bool {
94        self.lookup_intern(db).is_fully_concrete(db)
95    }
96
97    /// Returns true if the const does not contain any inference variables.
98    pub fn is_var_free(&self, db: &dyn SemanticGroup) -> bool {
99        self.lookup_intern(db).is_var_free(db)
100    }
101
102    /// Returns the type of the const.
103    pub fn ty(&self, db: &dyn SemanticGroup) -> Maybe<TypeId> {
104        self.lookup_intern(db).ty(db)
105    }
106}
107
108/// A constant value.
109#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
110pub enum ConstValue {
111    Int(#[dont_rewrite] BigInt, TypeId),
112    Struct(Vec<ConstValue>, TypeId),
113    Enum(ConcreteVariant, Box<ConstValue>),
114    NonZero(Box<ConstValue>),
115    Boxed(Box<ConstValue>),
116    Generic(#[dont_rewrite] GenericParamId),
117    ImplConstant(ImplConstantId),
118    Var(ConstVar, TypeId),
119    /// A missing value, used in cases where the value is not known due to diagnostics.
120    Missing(#[dont_rewrite] DiagnosticAdded),
121}
122impl ConstValue {
123    /// Returns true if the const does not depend on any generics.
124    pub fn is_fully_concrete(&self, db: &dyn SemanticGroup) -> bool {
125        self.ty(db).unwrap().is_fully_concrete(db)
126            && match self {
127                ConstValue::Int(_, _) => true,
128                ConstValue::Struct(members, _) => {
129                    members.iter().all(|member: &ConstValue| member.is_fully_concrete(db))
130                }
131                ConstValue::Enum(_, value)
132                | ConstValue::NonZero(value)
133                | ConstValue::Boxed(value) => value.is_fully_concrete(db),
134                ConstValue::Generic(_)
135                | ConstValue::Var(_, _)
136                | ConstValue::Missing(_)
137                | ConstValue::ImplConstant(_) => false,
138            }
139    }
140
141    /// Returns true if the const does not contain any inference variables.
142    pub fn is_var_free(&self, db: &dyn SemanticGroup) -> bool {
143        self.ty(db).unwrap().is_var_free(db)
144            && match self {
145                ConstValue::Int(_, _) | ConstValue::Generic(_) | ConstValue::Missing(_) => true,
146                ConstValue::Struct(members, _) => {
147                    members.iter().all(|member| member.is_var_free(db))
148                }
149                ConstValue::Enum(_, value)
150                | ConstValue::NonZero(value)
151                | ConstValue::Boxed(value) => value.is_var_free(db),
152                ConstValue::Var(_, _) => false,
153                ConstValue::ImplConstant(impl_constant) => impl_constant.impl_id().is_var_free(db),
154            }
155    }
156
157    /// Returns the type of the const.
158    pub fn ty(&self, db: &dyn SemanticGroup) -> Maybe<TypeId> {
159        Ok(match self {
160            ConstValue::Int(_, ty) => *ty,
161            ConstValue::Struct(_, ty) => *ty,
162            ConstValue::Enum(variant, _) => {
163                TypeLongId::Concrete(ConcreteTypeId::Enum(variant.concrete_enum_id)).intern(db)
164            }
165            ConstValue::NonZero(value) => core_nonzero_ty(db, value.ty(db)?),
166            ConstValue::Boxed(value) => core_box_ty(db, value.ty(db)?),
167            ConstValue::Generic(param) => {
168                extract_matches!(db.generic_param_semantic(*param)?, GenericParam::Const).ty
169            }
170            ConstValue::Var(_, ty) => *ty,
171            ConstValue::Missing(_) => TypeId::missing(db, skip_diagnostic()),
172            ConstValue::ImplConstant(impl_constant_id) => {
173                db.impl_constant_concrete_implized_type(*impl_constant_id)?
174            }
175        })
176    }
177
178    /// Returns the value of an int const as a BigInt.
179    pub fn into_int(self) -> Option<BigInt> {
180        match self {
181            ConstValue::Int(value, _) => Some(value),
182            _ => None,
183        }
184    }
185}
186
187/// An impl item of kind const.
188#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
189pub struct ImplConstantId {
190    /// The impl the item const is in.
191    impl_id: ImplId,
192    /// The trait const this impl const "implements".
193    trait_constant_id: TraitConstantId,
194}
195
196impl ImplConstantId {
197    /// Creates a new impl constant id. For an impl constant of a concrete impl, asserts that the
198    /// trait constant belongs to the same trait that the impl implements (panics if not).
199    pub fn new(
200        impl_id: ImplId,
201        trait_constant_id: TraitConstantId,
202        db: &dyn SemanticGroup,
203    ) -> Self {
204        if let ImplLongId::Concrete(concrete_impl) = impl_id.lookup_intern(db) {
205            let impl_def_id = concrete_impl.impl_def_id(db);
206            assert_eq!(Ok(trait_constant_id.trait_id(db)), db.impl_def_trait(impl_def_id));
207        }
208
209        ImplConstantId { impl_id, trait_constant_id }
210    }
211    pub fn impl_id(&self) -> ImplId {
212        self.impl_id
213    }
214    pub fn trait_constant_id(&self) -> TraitConstantId {
215        self.trait_constant_id
216    }
217
218    pub fn format(&self, db: &dyn SemanticGroup) -> SmolStr {
219        format!("{}::{}", self.impl_id.name(db), self.trait_constant_id.name(db)).into()
220    }
221}
222impl DebugWithDb<dyn SemanticGroup> for ImplConstantId {
223    fn fmt(
224        &self,
225        f: &mut std::fmt::Formatter<'_>,
226        db: &(dyn SemanticGroup + 'static),
227    ) -> std::fmt::Result {
228        write!(f, "{}", self.format(db))
229    }
230}
231
232/// Query implementation of [SemanticGroup::priv_constant_semantic_data].
233pub fn priv_constant_semantic_data(
234    db: &dyn SemanticGroup,
235    const_id: ConstantId,
236    in_cycle: bool,
237) -> Maybe<ConstantData> {
238    let lookup_item_id = LookupItemId::ModuleItem(ModuleItemId::Constant(const_id));
239    if in_cycle {
240        constant_semantic_data_cycle_helper(
241            db,
242            &db.module_constant_by_id(const_id)?.to_maybe()?,
243            lookup_item_id,
244            None,
245            &const_id,
246        )
247    } else {
248        constant_semantic_data_helper(
249            db,
250            &db.module_constant_by_id(const_id)?.to_maybe()?,
251            lookup_item_id,
252            None,
253            &const_id,
254        )
255    }
256}
257
258/// Cycle handling for [SemanticGroup::priv_constant_semantic_data].
259pub fn priv_constant_semantic_data_cycle(
260    db: &dyn SemanticGroup,
261    _cycle: &salsa::Cycle,
262    const_id: &ConstantId,
263    _in_cycle: &bool,
264) -> Maybe<ConstantData> {
265    priv_constant_semantic_data(db, *const_id, true)
266}
267
268/// Returns constant semantic data for the given ItemConstant.
269pub fn constant_semantic_data_helper(
270    db: &dyn SemanticGroup,
271    constant_ast: &ItemConstant,
272    lookup_item_id: LookupItemId,
273    parent_resolver_data: Option<Arc<ResolverData>>,
274    element_id: &impl LanguageElementId,
275) -> Maybe<ConstantData> {
276    let mut diagnostics: SemanticDiagnostics = SemanticDiagnostics::default();
277    // TODO(spapini): when code changes in a file, all the AST items change (as they contain a path
278    // to the green root that changes. Once ASTs are rooted on items, use a selector that picks only
279    // the item instead of all the module data.
280    let syntax_db = db;
281
282    let inference_id = InferenceId::LookupItemDeclaration(lookup_item_id);
283
284    let mut resolver = match parent_resolver_data {
285        Some(parent_resolver_data) => {
286            Resolver::with_data(db, parent_resolver_data.clone_with_inference_id(db, inference_id))
287        }
288        None => Resolver::new(db, element_id.module_file_id(db), inference_id),
289    };
290    resolver.set_feature_config(element_id, constant_ast, &mut diagnostics);
291
292    let constant_type = resolve_type(
293        db,
294        &mut diagnostics,
295        &mut resolver,
296        &constant_ast.type_clause(syntax_db).ty(syntax_db),
297    );
298
299    let environment = Environment::empty();
300    let mut ctx = ComputationContext::new(
301        db,
302        &mut diagnostics,
303        resolver,
304        None,
305        environment,
306        ContextFunction::Global,
307    );
308
309    let value = compute_expr_semantic(&mut ctx, &constant_ast.value(syntax_db));
310    let const_value = resolve_const_expr_and_evaluate(
311        db,
312        &mut ctx,
313        &value,
314        constant_ast.stable_ptr(syntax_db).untyped(),
315        constant_type,
316        true,
317    )
318    .intern(db);
319
320    let const_value = ctx
321        .resolver
322        .inference()
323        .rewrite(const_value)
324        .unwrap_or_else(|_| ConstValue::Missing(skip_diagnostic()).intern(db));
325    let resolver_data = Arc::new(ctx.resolver.data);
326    let constant = Constant { value: value.id, arenas: Arc::new(ctx.arenas) };
327    Ok(ConstantData {
328        diagnostics: diagnostics.build(),
329        const_value,
330        constant: Ok(constant),
331        resolver_data,
332    })
333}
334
335/// Helper for cycle handling of constants.
336pub fn constant_semantic_data_cycle_helper(
337    db: &dyn SemanticGroup,
338    constant_ast: &ItemConstant,
339    lookup_item_id: LookupItemId,
340    parent_resolver_data: Option<Arc<ResolverData>>,
341    element_id: &impl LanguageElementId,
342) -> Maybe<ConstantData> {
343    let mut diagnostics: SemanticDiagnostics = SemanticDiagnostics::default();
344
345    let inference_id = InferenceId::LookupItemDeclaration(lookup_item_id);
346
347    let resolver = match parent_resolver_data {
348        Some(parent_resolver_data) => {
349            Resolver::with_data(db, parent_resolver_data.clone_with_inference_id(db, inference_id))
350        }
351        None => Resolver::new(db, element_id.module_file_id(db), inference_id),
352    };
353
354    let resolver_data = Arc::new(resolver.data);
355
356    let diagnostic_added =
357        diagnostics.report(constant_ast.stable_ptr(db), SemanticDiagnosticKind::ConstCycle);
358    Ok(ConstantData {
359        constant: Err(diagnostic_added),
360        const_value: ConstValue::Missing(diagnostic_added).intern(db),
361        diagnostics: diagnostics.build(),
362        resolver_data,
363    })
364}
365
366/// Checks if the given expression only involved constant calculations.
367pub fn validate_const_expr(ctx: &mut ComputationContext<'_>, expr_id: ExprId) {
368    let info = ctx.db.const_calc_info();
369    let mut eval_ctx = ConstantEvaluateContext {
370        db: ctx.db,
371        info: info.as_ref(),
372        arenas: &ctx.arenas,
373        vars: Default::default(),
374        generic_substitution: Default::default(),
375        depth: 0,
376        diagnostics: ctx.diagnostics,
377    };
378    eval_ctx.validate(expr_id);
379}
380
381/// Resolves the given const expression and evaluates its value.
382pub fn resolve_const_expr_and_evaluate(
383    db: &dyn SemanticGroup,
384    ctx: &mut ComputationContext<'_>,
385    value: &ExprAndId,
386    const_stable_ptr: SyntaxStablePtrId,
387    target_type: TypeId,
388    finalize: bool,
389) -> ConstValue {
390    let prev_err_count = ctx.diagnostics.error_count;
391    let inference = &mut ctx.resolver.inference();
392    if let Err(err_set) = inference.conform_ty(value.ty(), target_type) {
393        inference.report_on_pending_error(err_set, ctx.diagnostics, const_stable_ptr);
394    }
395
396    if finalize {
397        // Check fully resolved.
398        inference.finalize(ctx.diagnostics, const_stable_ptr);
399    } else if let Err(err_set) = inference.solve() {
400        inference.report_on_pending_error(err_set, ctx.diagnostics, const_stable_ptr);
401    }
402
403    // TODO(orizi): Consider moving this to be called only upon creating const values, other callees
404    // don't necessarily need it.
405    ctx.apply_inference_rewriter_to_exprs();
406
407    match &value.expr {
408        Expr::Constant(ExprConstant { const_value_id, .. }) => const_value_id.lookup_intern(db),
409        // Check that the expression is a valid constant.
410        _ if ctx.diagnostics.error_count > prev_err_count => ConstValue::Missing(skip_diagnostic()),
411        _ => {
412            let info = db.const_calc_info();
413            let mut eval_ctx = ConstantEvaluateContext {
414                db,
415                info: info.as_ref(),
416                arenas: &ctx.arenas,
417                vars: Default::default(),
418                generic_substitution: Default::default(),
419                depth: 0,
420                diagnostics: ctx.diagnostics,
421            };
422            eval_ctx.validate(value.id);
423            if eval_ctx.diagnostics.error_count > prev_err_count {
424                ConstValue::Missing(skip_diagnostic())
425            } else {
426                eval_ctx.evaluate(value.id)
427            }
428        }
429    }
430}
431
432/// creates a [ConstValue] from a [BigInt] value.
433pub fn value_as_const_value(
434    db: &dyn SemanticGroup,
435    ty: TypeId,
436    value: &BigInt,
437) -> Result<ConstValue, LiteralError> {
438    validate_literal(db, ty, value)?;
439    let get_basic_const_value = |ty| {
440        let u256_ty = get_core_ty_by_name(db, "u256".into(), vec![]);
441
442        if ty != u256_ty {
443            ConstValue::Int(value.clone(), ty)
444        } else {
445            let u128_ty = get_core_ty_by_name(db, "u128".into(), vec![]);
446            let mask128 = BigInt::from(u128::MAX);
447            let low = value & mask128;
448            let high = value >> 128;
449            ConstValue::Struct(
450                vec![(ConstValue::Int(low, u128_ty)), (ConstValue::Int(high, u128_ty))],
451                ty,
452            )
453        }
454    };
455
456    if let Some(inner) = try_extract_nz_wrapped_type(db, ty) {
457        Ok(ConstValue::NonZero(Box::new(get_basic_const_value(inner))))
458    } else {
459        Ok(get_basic_const_value(ty))
460    }
461}
462
463/// A context for evaluating constant expressions.
464struct ConstantEvaluateContext<'a> {
465    db: &'a dyn SemanticGroup,
466    info: &'a ConstCalcInfo,
467    arenas: &'a Arenas,
468    vars: OrderedHashMap<VarId, ConstValue>,
469    generic_substitution: GenericSubstitution,
470    depth: usize,
471    diagnostics: &'a mut SemanticDiagnostics,
472}
473impl ConstantEvaluateContext<'_> {
474    /// Validate the given expression can be used as constant.
475    fn validate(&mut self, expr_id: ExprId) {
476        match &self.arenas.exprs[expr_id] {
477            Expr::Var(_) | Expr::Constant(_) | Expr::Missing(_) => {}
478            Expr::Block(ExprBlock { statements, tail: Some(inner), .. }) => {
479                for statement_id in statements {
480                    match &self.arenas.statements[*statement_id] {
481                        Statement::Let(statement) => {
482                            self.validate(statement.expr);
483                        }
484                        Statement::Expr(expr) => {
485                            self.validate(expr.expr);
486                        }
487                        other => {
488                            self.diagnostics.report(
489                                other.stable_ptr(),
490                                SemanticDiagnosticKind::UnsupportedConstant,
491                            );
492                        }
493                    }
494                }
495                self.validate(*inner);
496            }
497            Expr::FunctionCall(expr) => {
498                for arg in &expr.args {
499                    match arg {
500                        ExprFunctionCallArg::Value(arg) => self.validate(*arg),
501                        ExprFunctionCallArg::Reference(var) => {
502                            self.diagnostics.report(
503                                var.stable_ptr(),
504                                SemanticDiagnosticKind::UnsupportedConstant,
505                            );
506                        }
507                    }
508                    if let ExprFunctionCallArg::Value(arg) = arg {
509                        self.validate(*arg);
510                    }
511                }
512                if !self.is_function_const(expr.function) {
513                    self.diagnostics.report(
514                        expr.stable_ptr.untyped(),
515                        SemanticDiagnosticKind::UnsupportedConstant,
516                    );
517                }
518            }
519            Expr::Literal(expr) => {
520                if let Err(err) = validate_literal(self.db, expr.ty, &expr.value) {
521                    self.diagnostics.report(
522                        expr.stable_ptr.untyped(),
523                        SemanticDiagnosticKind::LiteralError(err),
524                    );
525                }
526            }
527            Expr::Tuple(expr) => {
528                for item in &expr.items {
529                    self.validate(*item);
530                }
531            }
532            Expr::StructCtor(ExprStructCtor { members, base_struct: None, .. }) => {
533                for (_, expr_id) in members {
534                    self.validate(*expr_id);
535                }
536            }
537            Expr::EnumVariantCtor(expr) => self.validate(expr.value_expr),
538            Expr::MemberAccess(expr) => self.validate(expr.expr),
539            Expr::FixedSizeArray(expr) => match &expr.items {
540                crate::FixedSizeArrayItems::Items(items) => {
541                    for item in items {
542                        self.validate(*item);
543                    }
544                }
545                crate::FixedSizeArrayItems::ValueAndSize(value, _) => {
546                    self.validate(*value);
547                }
548            },
549            Expr::Snapshot(expr) => self.validate(expr.inner),
550            Expr::Desnap(expr) => self.validate(expr.inner),
551            Expr::LogicalOperator(expr) => {
552                self.validate(expr.lhs);
553                self.validate(expr.rhs);
554            }
555            Expr::Match(expr) => {
556                self.validate(expr.matched_expr);
557                for arm in &expr.arms {
558                    self.validate(arm.expression);
559                }
560            }
561            Expr::If(expr) => {
562                for condition in &expr.conditions {
563                    self.validate(match condition {
564                        Condition::BoolExpr(id) | Condition::Let(id, _) => *id,
565                    });
566                }
567                self.validate(expr.if_block);
568                if let Some(else_block) = expr.else_block {
569                    self.validate(else_block);
570                }
571            }
572            other => {
573                self.diagnostics.report(
574                    other.stable_ptr().untyped(),
575                    SemanticDiagnosticKind::UnsupportedConstant,
576                );
577            }
578        }
579    }
580
581    /// Returns true if the given function is allowed to be called in constant context.
582    fn is_function_const(&self, function_id: FunctionId) -> bool {
583        if function_id == self.panic_with_felt252 {
584            return true;
585        }
586        let db = self.db;
587        let concrete_function = function_id.get_concrete(db);
588        let signature = (|| match concrete_function.generic_function {
589            GenericFunctionId::Free(id) => db.free_function_signature(id),
590            GenericFunctionId::Extern(id) => db.extern_function_signature(id),
591            GenericFunctionId::Impl(id) => {
592                if let ImplLongId::Concrete(impl_id) = id.impl_id.lookup_intern(db) {
593                    if let Ok(Some(impl_function_id)) = impl_id.get_impl_function(db, id.function) {
594                        return self.db.impl_function_signature(impl_function_id);
595                    }
596                }
597                self.db.trait_function_signature(id.function)
598            }
599        })();
600        if signature.map(|s| s.is_const) == Ok(true) {
601            return true;
602        }
603        let Ok(Some(body)) = concrete_function.body(db) else { return false };
604        let GenericFunctionWithBodyId::Impl(imp) = body.generic_function(db) else {
605            return false;
606        };
607        let impl_def = imp.concrete_impl_id.impl_def_id(db);
608        if impl_def.parent_module(db).owning_crate(db) != db.core_crate() {
609            return false;
610        }
611        let Ok(trait_id) = db.impl_def_trait(impl_def) else {
612            return false;
613        };
614        self.const_traits.contains(&trait_id)
615    }
616
617    /// Evaluate the given const expression value.
618    fn evaluate(&mut self, expr_id: ExprId) -> ConstValue {
619        let expr = &self.arenas.exprs[expr_id];
620        let db = self.db;
621        match expr {
622            Expr::Var(expr) => self.vars.get(&expr.var).cloned().unwrap_or_else(|| {
623                ConstValue::Missing(
624                    self.diagnostics
625                        .report(expr.stable_ptr, SemanticDiagnosticKind::UnsupportedConstant),
626                )
627            }),
628            Expr::Constant(expr) => self
629                .generic_substitution
630                .substitute(self.db, expr.const_value_id.lookup_intern(db))
631                .unwrap_or_else(ConstValue::Missing),
632            Expr::Block(ExprBlock { statements, tail: Some(inner), .. }) => {
633                for statement_id in statements {
634                    match &self.arenas.statements[*statement_id] {
635                        Statement::Let(statement) => {
636                            let value = self.evaluate(statement.expr);
637                            self.destructure_pattern(statement.pattern, value);
638                        }
639                        Statement::Expr(expr) => {
640                            self.evaluate(expr.expr);
641                        }
642                        other => {
643                            self.diagnostics.report(
644                                other.stable_ptr(),
645                                SemanticDiagnosticKind::UnsupportedConstant,
646                            );
647                        }
648                    }
649                }
650                self.evaluate(*inner)
651            }
652            Expr::FunctionCall(expr) => self.evaluate_function_call(expr),
653            Expr::Literal(expr) => value_as_const_value(db, expr.ty, &expr.value)
654                .expect("LiteralError should have been caught on `validate`"),
655            Expr::Tuple(expr) => ConstValue::Struct(
656                expr.items.iter().map(|expr_id| self.evaluate(*expr_id)).collect(),
657                expr.ty,
658            ),
659            Expr::StructCtor(ExprStructCtor {
660                members,
661                base_struct: None,
662                ty,
663                concrete_struct_id,
664                ..
665            }) => {
666                let member_order = match db.concrete_struct_members(*concrete_struct_id) {
667                    Ok(member_order) => member_order,
668                    Err(diag_add) => return ConstValue::Missing(diag_add),
669                };
670                ConstValue::Struct(
671                    member_order
672                        .values()
673                        .map(|m| {
674                            members
675                                .iter()
676                                .find(|(member_id, _)| m.id == *member_id)
677                                .map(|(_, expr_id)| self.evaluate(*expr_id))
678                                .expect("Should have been caught by semantic validation")
679                        })
680                        .collect(),
681                    *ty,
682                )
683            }
684            Expr::EnumVariantCtor(expr) => {
685                ConstValue::Enum(expr.variant, Box::new(self.evaluate(expr.value_expr)))
686            }
687            Expr::MemberAccess(expr) => {
688                self.evaluate_member_access(expr).unwrap_or_else(ConstValue::Missing)
689            }
690            Expr::FixedSizeArray(expr) => ConstValue::Struct(
691                match &expr.items {
692                    crate::FixedSizeArrayItems::Items(items) => {
693                        items.iter().map(|expr_id| self.evaluate(*expr_id)).collect()
694                    }
695                    crate::FixedSizeArrayItems::ValueAndSize(value, count) => {
696                        let value = self.evaluate(*value);
697                        let count = count.lookup_intern(db);
698                        if let Some(count) = count.into_int() {
699                            (0..count.to_usize().unwrap()).map(|_| value.clone()).collect()
700                        } else {
701                            self.diagnostics.report(
702                                expr.stable_ptr.untyped(),
703                                SemanticDiagnosticKind::UnsupportedConstant,
704                            );
705                            vec![]
706                        }
707                    }
708                },
709                expr.ty,
710            ),
711            Expr::Snapshot(expr) => self.evaluate(expr.inner),
712            Expr::Desnap(expr) => self.evaluate(expr.inner),
713            Expr::LogicalOperator(expr) => {
714                let lhs = self.evaluate(expr.lhs);
715                if let ConstValue::Enum(v, _) = &lhs {
716                    let early_return_variant = match expr.op {
717                        LogicalOperator::AndAnd => false_variant(self.db),
718                        LogicalOperator::OrOr => true_variant(self.db),
719                    };
720                    if *v == early_return_variant { lhs } else { self.evaluate(expr.lhs) }
721                } else {
722                    ConstValue::Missing(skip_diagnostic())
723                }
724            }
725            Expr::Match(expr) => {
726                let value = self.evaluate(expr.matched_expr);
727                let ConstValue::Enum(variant, value) = value else {
728                    return ConstValue::Missing(skip_diagnostic());
729                };
730                for arm in &expr.arms {
731                    for pattern_id in &arm.patterns {
732                        let pattern = &self.arenas.patterns[*pattern_id];
733                        if matches!(pattern, Pattern::Otherwise(_)) {
734                            return self.evaluate(arm.expression);
735                        }
736                        let Pattern::EnumVariant(pattern) = pattern else {
737                            continue;
738                        };
739                        if pattern.variant.idx != variant.idx {
740                            continue;
741                        }
742                        if let Some(inner_pattern) = pattern.inner_pattern {
743                            self.destructure_pattern(inner_pattern, *value);
744                        }
745                        return self.evaluate(arm.expression);
746                    }
747                }
748                ConstValue::Missing(
749                    self.diagnostics.report(
750                        expr.stable_ptr.untyped(),
751                        SemanticDiagnosticKind::UnsupportedConstant,
752                    ),
753                )
754            }
755            Expr::If(expr) => {
756                let mut if_condition: bool = true;
757                for condition in &expr.conditions {
758                    match condition {
759                        crate::Condition::BoolExpr(id) => {
760                            let condition = self.evaluate(*id);
761                            let ConstValue::Enum(variant, _) = condition else {
762                                return ConstValue::Missing(skip_diagnostic());
763                            };
764                            if variant != true_variant(self.db) {
765                                if_condition = false;
766                                break;
767                            }
768                        }
769                        crate::Condition::Let(id, patterns) => {
770                            let value = self.evaluate(*id);
771                            let ConstValue::Enum(variant, value) = value else {
772                                return ConstValue::Missing(skip_diagnostic());
773                            };
774                            let mut found_pattern = false;
775                            for pattern_id in patterns {
776                                let Pattern::EnumVariant(pattern) =
777                                    &self.arenas.patterns[*pattern_id]
778                                else {
779                                    continue;
780                                };
781                                if pattern.variant != variant {
782                                    // Continue to the next option in the `|` list.
783                                    continue;
784                                }
785                                if let Some(inner_pattern) = pattern.inner_pattern {
786                                    self.destructure_pattern(inner_pattern, *value);
787                                }
788                                found_pattern = true;
789                                break;
790                            }
791                            if !found_pattern {
792                                if_condition = false;
793                                break;
794                            }
795                        }
796                    }
797                }
798
799                if if_condition {
800                    self.evaluate(expr.if_block)
801                } else if let Some(else_block) = expr.else_block {
802                    self.evaluate(else_block)
803                } else {
804                    self.unit_const.clone()
805                }
806            }
807            _ => ConstValue::Missing(skip_diagnostic()),
808        }
809    }
810
811    /// Attempts to evaluate constants from a const function call.
812    fn evaluate_function_call(&mut self, expr: &ExprFunctionCall) -> ConstValue {
813        let db = self.db;
814        let args = expr
815            .args
816            .iter()
817            .filter_map(|arg| try_extract_matches!(arg, ExprFunctionCallArg::Value))
818            .map(|arg| self.evaluate(*arg))
819            .collect_vec();
820        if expr.function == self.panic_with_felt252 {
821            return ConstValue::Missing(self.diagnostics.report(
822                expr.stable_ptr.untyped(),
823                SemanticDiagnosticKind::FailedConstantCalculation,
824            ));
825        }
826        let concrete_function =
827            match self.generic_substitution.substitute(db, expr.function.get_concrete(db)) {
828                Ok(v) => v,
829                Err(err) => return ConstValue::Missing(err),
830            };
831        if let Some(calc_result) =
832            self.evaluate_const_function_call(&concrete_function, &args, expr)
833        {
834            return calc_result;
835        }
836
837        let imp = extract_matches!(concrete_function.generic_function, GenericFunctionId::Impl);
838        let bool_value = |condition: bool| {
839            if condition { self.true_const.clone() } else { self.false_const.clone() }
840        };
841
842        if imp.function == self.eq_fn {
843            return bool_value(args[0] == args[1]);
844        } else if imp.function == self.ne_fn {
845            return bool_value(args[0] != args[1]);
846        } else if imp.function == self.not_fn {
847            return bool_value(args[0] == self.false_const);
848        }
849
850        let args = match args
851            .into_iter()
852            .map(|arg| NumericArg::try_new(db, arg))
853            .collect::<Option<Vec<_>>>()
854        {
855            Some(args) => args,
856            // Diagnostic can be skipped as we would either have a semantic error for a bad arg for
857            // the function, or the arg itself couldn't have been calculated.
858            None => return ConstValue::Missing(skip_diagnostic()),
859        };
860        let mut value = match imp.function {
861            id if id == self.neg_fn => -&args[0].v,
862            id if id == self.add_fn => &args[0].v + &args[1].v,
863            id if id == self.sub_fn => &args[0].v - &args[1].v,
864            id if id == self.mul_fn => &args[0].v * &args[1].v,
865            id if (id == self.div_fn || id == self.rem_fn) && args[1].v.is_zero() => {
866                return ConstValue::Missing(
867                    self.diagnostics
868                        .report(expr.stable_ptr.untyped(), SemanticDiagnosticKind::DivisionByZero),
869                );
870            }
871            id if id == self.div_fn => &args[0].v / &args[1].v,
872            id if id == self.rem_fn => &args[0].v % &args[1].v,
873            id if id == self.bitand_fn => &args[0].v & &args[1].v,
874            id if id == self.bitor_fn => &args[0].v | &args[1].v,
875            id if id == self.bitxor_fn => &args[0].v ^ &args[1].v,
876            id if id == self.lt_fn => return bool_value(args[0].v < args[1].v),
877            id if id == self.le_fn => return bool_value(args[0].v <= args[1].v),
878            id if id == self.gt_fn => return bool_value(args[0].v > args[1].v),
879            id if id == self.ge_fn => return bool_value(args[0].v >= args[1].v),
880            id if id == self.div_rem_fn => {
881                // No need for non-zero check as this is type checked to begin with.
882                // Also results are always in the range of the input type, so `unwrap`s are ok.
883                return ConstValue::Struct(
884                    vec![
885                        value_as_const_value(db, args[0].ty, &(&args[0].v / &args[1].v)).unwrap(),
886                        value_as_const_value(db, args[0].ty, &(&args[0].v % &args[1].v)).unwrap(),
887                    ],
888                    expr.ty,
889                );
890            }
891            _ => {
892                unreachable!("Unexpected function call in constant lowering: {:?}", expr)
893            }
894        };
895        if expr.ty == db.core_info().felt252 {
896            // Specifically handling felt252s since their evaluation is more complex.
897            value %= BigInt::from_str_radix(
898                "800000000000011000000000000000000000000000000000000000000000001",
899                16,
900            )
901            .unwrap();
902        }
903        value_as_const_value(db, expr.ty, &value)
904            .map_err(|err| {
905                self.diagnostics
906                    .report(expr.stable_ptr.untyped(), SemanticDiagnosticKind::LiteralError(err))
907            })
908            .unwrap_or_else(ConstValue::Missing)
909    }
910
911    /// Attempts to evaluate a constant function call.
912    fn evaluate_const_function_call(
913        &mut self,
914        concrete_function: &ConcreteFunction,
915        args: &[ConstValue],
916        expr: &ExprFunctionCall,
917    ) -> Option<ConstValue> {
918        let db = self.db;
919        if let GenericFunctionId::Extern(extern_fn) = concrete_function.generic_function {
920            let expr_ty = self.generic_substitution.substitute(db, expr.ty).ok()?;
921            if self.upcast_fns.contains(&extern_fn) {
922                let [ConstValue::Int(value, _)] = args else { return None };
923                return Some(ConstValue::Int(value.clone(), expr_ty));
924            } else if self.unwrap_non_zero == extern_fn {
925                let [ConstValue::NonZero(value)] = args else { return None };
926                return Some(value.as_ref().clone());
927            } else if let Some(reversed) = self.downcast_fns.get(&extern_fn) {
928                let [ConstValue::Int(value, _)] = args else { return None };
929                let TypeLongId::Concrete(ConcreteTypeId::Enum(enm)) = expr_ty.lookup_intern(db)
930                else {
931                    return None;
932                };
933                let (variant0, variant1) =
934                    db.concrete_enum_variants(enm).ok()?.into_iter().collect_tuple()?;
935                let (some, none) =
936                    if *reversed { (variant1, variant0) } else { (variant0, variant1) };
937                let success_ty = some.ty;
938                return Some(match validate_literal(db, success_ty, value) {
939                    Ok(()) => {
940                        ConstValue::Enum(some, ConstValue::Int(value.clone(), success_ty).into())
941                    }
942                    Err(LiteralError::OutOfRange(_)) => {
943                        ConstValue::Enum(none, self.unit_const.clone().into())
944                    }
945                    Err(LiteralError::InvalidTypeForLiteral(_)) => unreachable!(
946                        "`downcast` is only allowed into types that can be literals. Got `{}`.",
947                        success_ty.format(db)
948                    ),
949                });
950            } else {
951                unreachable!(
952                    "Unexpected extern function in constant lowering: `{}`",
953                    extern_fn.full_path(db)
954                );
955            }
956        }
957        let body_id = concrete_function.body(db).ok()??;
958        let concrete_body_id = body_id.function_with_body_id(db);
959        let signature = db.function_with_body_signature(concrete_body_id).ok()?;
960        require(signature.is_const)?;
961        let generic_substitution = body_id.substitution(db).ok()?;
962        let body = db.function_body(concrete_body_id).ok()?;
963        const MAX_CONST_EVAL_DEPTH: usize = 100;
964        if self.depth > MAX_CONST_EVAL_DEPTH {
965            return Some(ConstValue::Missing(self.diagnostics.report(
966                expr.stable_ptr,
967                SemanticDiagnosticKind::ConstantCalculationDepthExceeded,
968            )));
969        }
970        let mut diagnostics = SemanticDiagnostics::default();
971        let mut inner = ConstantEvaluateContext {
972            db,
973            info: self.info,
974            arenas: &body.arenas,
975            vars: signature
976                .params
977                .into_iter()
978                .map(|p| VarId::Param(p.id))
979                .zip(args.iter().cloned())
980                .collect(),
981            generic_substitution,
982            depth: self.depth + 1,
983            diagnostics: &mut diagnostics,
984        };
985        let value = inner.evaluate(body.body_expr);
986        for diagnostic in diagnostics.build().get_all() {
987            let location = diagnostic.location(db.elongate());
988            let (inner_diag, mut notes) = match diagnostic.kind {
989                SemanticDiagnosticKind::ConstantCalculationDepthExceeded => {
990                    self.diagnostics.report(
991                        expr.stable_ptr,
992                        SemanticDiagnosticKind::ConstantCalculationDepthExceeded,
993                    );
994                    continue;
995                }
996                SemanticDiagnosticKind::InnerFailedConstantCalculation(inner_diag, notes) => {
997                    (inner_diag, notes)
998                }
999                _ => (diagnostic.into(), vec![]),
1000            };
1001            notes.push(DiagnosticNote::with_location(
1002                format!("In `{}`", concrete_function.full_path(db)),
1003                location,
1004            ));
1005            self.diagnostics.report(
1006                expr.stable_ptr,
1007                SemanticDiagnosticKind::InnerFailedConstantCalculation(inner_diag, notes),
1008            );
1009        }
1010        Some(value)
1011    }
1012
1013    /// Extract const member access from a const value.
1014    fn evaluate_member_access(&mut self, expr: &ExprMemberAccess) -> Maybe<ConstValue> {
1015        let full_struct = self.evaluate(expr.expr);
1016        let ConstValue::Struct(mut values, _) = full_struct else {
1017            // A semantic diagnostic should have been reported.
1018            return Err(skip_diagnostic());
1019        };
1020        let members = self.db.concrete_struct_members(expr.concrete_struct_id)?;
1021        let Some(member_idx) = members.iter().position(|(_, member)| member.id == expr.member)
1022        else {
1023            // A semantic diagnostic should have been reported.
1024            return Err(skip_diagnostic());
1025        };
1026        Ok(values.swap_remove(member_idx))
1027    }
1028
1029    /// Destructures the pattern into the const value of the variables in scope.
1030    fn destructure_pattern(&mut self, pattern_id: PatternId, value: ConstValue) {
1031        let pattern = &self.arenas.patterns[pattern_id];
1032        match pattern {
1033            Pattern::Literal(_)
1034            | Pattern::StringLiteral(_)
1035            | Pattern::Otherwise(_)
1036            | Pattern::Missing(_) => {}
1037            Pattern::Variable(pattern) => {
1038                self.vars.insert(VarId::Local(pattern.var.id), value);
1039            }
1040            Pattern::Struct(pattern) => {
1041                if let ConstValue::Struct(inner_values, _) = value {
1042                    let member_order =
1043                        match self.db.concrete_struct_members(pattern.concrete_struct_id) {
1044                            Ok(member_order) => member_order,
1045                            Err(_) => return,
1046                        };
1047                    for (member, inner_value) in zip(member_order.values(), inner_values) {
1048                        if let Some((_, inner_pattern)) =
1049                            pattern.field_patterns.iter().find(|(field, _)| member.id == field.id)
1050                        {
1051                            self.destructure_pattern(*inner_pattern, inner_value);
1052                        }
1053                    }
1054                }
1055            }
1056            Pattern::Tuple(pattern) => {
1057                if let ConstValue::Struct(inner_values, _) = value {
1058                    for (inner_pattern, inner_value) in zip(&pattern.field_patterns, inner_values) {
1059                        self.destructure_pattern(*inner_pattern, inner_value);
1060                    }
1061                }
1062            }
1063            Pattern::FixedSizeArray(pattern) => {
1064                if let ConstValue::Struct(inner_values, _) = value {
1065                    for (inner_pattern, inner_value) in
1066                        zip(&pattern.elements_patterns, inner_values)
1067                    {
1068                        self.destructure_pattern(*inner_pattern, inner_value);
1069                    }
1070                }
1071            }
1072            Pattern::EnumVariant(pattern) => {
1073                if let ConstValue::Enum(variant, inner_value) = value {
1074                    if pattern.variant == variant {
1075                        if let Some(inner_pattern) = pattern.inner_pattern {
1076                            self.destructure_pattern(inner_pattern, *inner_value);
1077                        }
1078                    }
1079                }
1080            }
1081        }
1082    }
1083}
1084
1085impl std::ops::Deref for ConstantEvaluateContext<'_> {
1086    type Target = ConstCalcInfo;
1087    fn deref(&self) -> &Self::Target {
1088        self.info
1089    }
1090}
1091
1092/// Helper for the arguments info.
1093struct NumericArg {
1094    /// The arg's integer value.
1095    v: BigInt,
1096    /// The arg's type.
1097    ty: TypeId,
1098}
1099impl NumericArg {
1100    fn try_new(db: &dyn SemanticGroup, arg: ConstValue) -> Option<Self> {
1101        Some(Self { ty: arg.ty(db).ok()?, v: numeric_arg_value(arg)? })
1102    }
1103}
1104
1105/// Helper for creating a `NumericArg` value.
1106/// This includes unwrapping of `NonZero` values and struct of 2 values as a `u256`.
1107fn numeric_arg_value(value: ConstValue) -> Option<BigInt> {
1108    match value {
1109        ConstValue::Int(value, _) => Some(value),
1110        ConstValue::Struct(v, _) => {
1111            if let [ConstValue::Int(low, _), ConstValue::Int(high, _)] = &v[..] {
1112                Some(low + (high << 128))
1113            } else {
1114                None
1115            }
1116        }
1117        ConstValue::NonZero(const_value) => numeric_arg_value(*const_value),
1118        _ => None,
1119    }
1120}
1121
1122/// Query implementation of [SemanticGroup::constant_semantic_diagnostics].
1123pub fn constant_semantic_diagnostics(
1124    db: &dyn SemanticGroup,
1125    const_id: ConstantId,
1126) -> Diagnostics<SemanticDiagnostic> {
1127    db.priv_constant_semantic_data(const_id, false).map(|data| data.diagnostics).unwrap_or_default()
1128}
1129
1130/// Query implementation of [SemanticGroup::constant_semantic_data].
1131pub fn constant_semantic_data(db: &dyn SemanticGroup, const_id: ConstantId) -> Maybe<Constant> {
1132    db.priv_constant_semantic_data(const_id, false)?.constant
1133}
1134
1135/// Cycle handling for [SemanticGroup::constant_semantic_data].
1136pub fn constant_semantic_data_cycle(
1137    db: &dyn SemanticGroup,
1138    _cycle: &salsa::Cycle,
1139    const_id: &ConstantId,
1140) -> Maybe<Constant> {
1141    // Forwarding cycle handling to `priv_constant_semantic_data` handler.
1142    db.priv_constant_semantic_data(*const_id, true)?.constant
1143}
1144
1145/// Query implementation of [crate::db::SemanticGroup::constant_resolver_data].
1146pub fn constant_resolver_data(
1147    db: &dyn SemanticGroup,
1148    const_id: ConstantId,
1149) -> Maybe<Arc<ResolverData>> {
1150    Ok(db.priv_constant_semantic_data(const_id, false)?.resolver_data)
1151}
1152
1153/// Cycle handling for [crate::db::SemanticGroup::constant_resolver_data].
1154pub fn constant_resolver_data_cycle(
1155    db: &dyn SemanticGroup,
1156    _cycle: &salsa::Cycle,
1157    const_id: &ConstantId,
1158) -> Maybe<Arc<ResolverData>> {
1159    Ok(db.priv_constant_semantic_data(*const_id, true)?.resolver_data)
1160}
1161
1162/// Query implementation of [crate::db::SemanticGroup::constant_const_value].
1163pub fn constant_const_value(db: &dyn SemanticGroup, const_id: ConstantId) -> Maybe<ConstValueId> {
1164    Ok(db.priv_constant_semantic_data(const_id, false)?.const_value)
1165}
1166
1167/// Cycle handling for [crate::db::SemanticGroup::constant_const_value].
1168pub fn constant_const_value_cycle(
1169    db: &dyn SemanticGroup,
1170    _cycle: &salsa::Cycle,
1171    const_id: &ConstantId,
1172) -> Maybe<ConstValueId> {
1173    // Forwarding cycle handling to `priv_constant_semantic_data` handler.
1174    Ok(db.priv_constant_semantic_data(*const_id, true)?.const_value)
1175}
1176
1177/// Query implementation of [crate::db::SemanticGroup::constant_const_type].
1178pub fn constant_const_type(db: &dyn SemanticGroup, const_id: ConstantId) -> Maybe<TypeId> {
1179    db.priv_constant_semantic_data(const_id, false)?.const_value.ty(db)
1180}
1181
1182/// Cycle handling for [crate::db::SemanticGroup::constant_const_type].
1183pub fn constant_const_type_cycle(
1184    db: &dyn SemanticGroup,
1185    _cycle: &salsa::Cycle,
1186    const_id: &ConstantId,
1187) -> Maybe<TypeId> {
1188    // Forwarding cycle handling to `priv_constant_semantic_data` handler.
1189    db.priv_constant_semantic_data(*const_id, true)?.const_value.ty(db)
1190}
1191
1192/// Query implementation of [crate::db::SemanticGroup::const_calc_info].
1193pub fn const_calc_info(db: &dyn SemanticGroup) -> Arc<ConstCalcInfo> {
1194    Arc::new(ConstCalcInfo::new(db))
1195}
1196
1197/// Holds static information about extern functions required for const calculations.
1198#[derive(Debug, PartialEq, Eq)]
1199pub struct ConstCalcInfo {
1200    /// Traits that are allowed for consts if their impls is in the corelib.
1201    const_traits: UnorderedHashSet<TraitId>,
1202    /// The const value for the unit type `()`.
1203    unit_const: ConstValue,
1204    /// The const value for `true`.
1205    true_const: ConstValue,
1206    /// The const value for `false`.
1207    false_const: ConstValue,
1208    /// The function for panicking with a felt252.
1209    panic_with_felt252: FunctionId,
1210    /// The integer `upcast` style functions.
1211    pub upcast_fns: UnorderedHashSet<ExternFunctionId>,
1212    /// The integer `downcast` style functions, mapping to whether it returns a reversed Option
1213    /// enum.
1214    pub downcast_fns: UnorderedHashMap<ExternFunctionId, bool>,
1215    /// The `unwrap_non_zero` function.
1216    unwrap_non_zero: ExternFunctionId,
1217
1218    core_info: Arc<CoreInfo>,
1219}
1220
1221impl std::ops::Deref for ConstCalcInfo {
1222    type Target = CoreInfo;
1223    fn deref(&self) -> &CoreInfo {
1224        &self.core_info
1225    }
1226}
1227
1228impl ConstCalcInfo {
1229    /// Creates a new ConstCalcInfo.
1230    fn new(db: &dyn SemanticGroup) -> Self {
1231        let core_info = db.core_info();
1232        let unit_const = ConstValue::Struct(vec![], unit_ty(db));
1233        let core = ModuleHelper::core(db);
1234        let bounded_int = core.submodule("internal").submodule("bounded_int");
1235        let integer = core.submodule("integer");
1236        let zeroable = core.submodule("zeroable");
1237        let starknet = core.submodule("starknet");
1238        let class_hash_module = starknet.submodule("class_hash");
1239        let contract_address_module = starknet.submodule("contract_address");
1240        Self {
1241            const_traits: FromIterator::from_iter([
1242                core_info.neg_trt,
1243                core_info.add_trt,
1244                core_info.sub_trt,
1245                core_info.mul_trt,
1246                core_info.div_trt,
1247                core_info.rem_trt,
1248                core_info.div_rem_trt,
1249                core_info.bitand_trt,
1250                core_info.bitor_trt,
1251                core_info.bitxor_trt,
1252                core_info.partialeq_trt,
1253                core_info.partialord_trt,
1254                core_info.not_trt,
1255            ]),
1256            true_const: ConstValue::Enum(true_variant(db), unit_const.clone().into()),
1257            false_const: ConstValue::Enum(false_variant(db), unit_const.clone().into()),
1258            unit_const,
1259            panic_with_felt252: core.function_id("panic_with_felt252", vec![]),
1260            upcast_fns: FromIterator::from_iter([
1261                bounded_int.extern_function_id("upcast"),
1262                integer.extern_function_id("u8_to_felt252"),
1263                integer.extern_function_id("u16_to_felt252"),
1264                integer.extern_function_id("u32_to_felt252"),
1265                integer.extern_function_id("u64_to_felt252"),
1266                integer.extern_function_id("u128_to_felt252"),
1267                integer.extern_function_id("i8_to_felt252"),
1268                integer.extern_function_id("i16_to_felt252"),
1269                integer.extern_function_id("i32_to_felt252"),
1270                integer.extern_function_id("i64_to_felt252"),
1271                integer.extern_function_id("i128_to_felt252"),
1272                class_hash_module.extern_function_id("class_hash_to_felt252"),
1273                contract_address_module.extern_function_id("contract_address_to_felt252"),
1274            ]),
1275            downcast_fns: FromIterator::from_iter([
1276                (bounded_int.extern_function_id("downcast"), false),
1277                (bounded_int.extern_function_id("bounded_int_trim_min"), true),
1278                (bounded_int.extern_function_id("bounded_int_trim_max"), true),
1279                (integer.extern_function_id("u8_try_from_felt252"), false),
1280                (integer.extern_function_id("u16_try_from_felt252"), false),
1281                (integer.extern_function_id("u32_try_from_felt252"), false),
1282                (integer.extern_function_id("u64_try_from_felt252"), false),
1283                (integer.extern_function_id("i8_try_from_felt252"), false),
1284                (integer.extern_function_id("i16_try_from_felt252"), false),
1285                (integer.extern_function_id("i32_try_from_felt252"), false),
1286                (integer.extern_function_id("i64_try_from_felt252"), false),
1287                (integer.extern_function_id("i128_try_from_felt252"), false),
1288                (class_hash_module.extern_function_id("class_hash_try_from_felt252"), false),
1289                (
1290                    contract_address_module.extern_function_id("contract_address_try_from_felt252"),
1291                    false,
1292                ),
1293            ]),
1294            unwrap_non_zero: zeroable.extern_function_id("unwrap_non_zero"),
1295            core_info,
1296        }
1297    }
1298}