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