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