cairo_lang_semantic/items/
constant.rs

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