Skip to main content

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