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 pub value: ExprId,
64 pub arenas: Arc<Arenas<'db>>,
66}
67
68unsafe 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#[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 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 pub fn format(&self, db: &dyn Database) -> String {
131 format!("{:?}", self.long(db).debug(db))
132 }
133
134 pub fn is_fully_concrete(&self, db: &dyn Database) -> bool {
136 self.long(db).is_fully_concrete(db)
137 }
138
139 pub fn is_var_free(&self, db: &dyn Database) -> bool {
141 self.long(db).is_var_free(db)
142 }
143
144 pub fn ty(&self, db: &'db dyn Database) -> Maybe<TypeId<'db>> {
146 self.long(db).ty(db)
147 }
148
149 pub fn to_int(&self, db: &'db dyn Database) -> Option<&'db BigInt> {
151 self.long(db).to_int()
152 }
153
154 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
189pub fn felt252_for_downcast(value: &BigInt, range_min: &BigInt) -> BigInt {
191 Felt252::from(value - range_min).to_bigint() + range_min
192}
193
194pub fn canonical_felt252(value: &BigInt) -> BigInt {
196 value % &*CAIRO_PRIME_BIGINT
197}
198
199#[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 Missing(#[dont_rewrite] DiagnosticAdded),
211}
212impl<'db> ConstValue<'db> {
213 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 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 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 pub fn to_int(&self) -> Option<&BigInt> {
265 match self {
266 ConstValue::Int(value, _) => Some(value),
267 _ => None,
268 }
269 }
270}
271
272#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject, HeapSize, salsa::Update)]
274pub struct ImplConstantId<'db> {
275 impl_id: ImplId<'db>,
277 trait_constant_id: TraitConstantId<'db>,
279}
280
281impl<'db> ImplConstantId<'db> {
282 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#[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
342fn 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
359pub 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 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 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
413pub 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
445pub 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
460pub 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 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 _ 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
511struct 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
522macro_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 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 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 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 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 return self.evaluate(*else_clause);
706 } else {
707 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 .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 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 let (q, r) = args[0].v.div_rem(&args[1].v);
969 let ty = args[0].ty;
970 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 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 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 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 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 return Err(skip_diagnostic());
1191 };
1192 member_idx
1193 }
1194 MemberAccessKind::Index { index, .. } => *index,
1195 };
1196 Ok(values[member_idx])
1197 }
1198
1199 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 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 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 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
1312struct NumericArg<'db> {
1314 v: BigInt,
1316 ty: TypeId<'db>,
1318}
1319impl<'db> NumericArg<'db> {
1320 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
1338fn const_calc_info<'db>(db: &'db dyn Database) -> Arc<ConstCalcInfo<'db>> {
1340 Arc::new(ConstCalcInfo::new(db))
1341}
1342
1343#[salsa::tracked]
1345fn const_calc_info_tracked<'db>(db: &'db dyn Database) -> Arc<ConstCalcInfo<'db>> {
1346 const_calc_info(db)
1347}
1348
1349#[derive(Debug, PartialEq, Eq, salsa::Update)]
1351pub struct ConstCalcInfo<'db> {
1352 const_traits: UnorderedHashSet<TraitId<'db>>,
1354 unit_const: ConstValueId<'db>,
1356 true_const: ConstValueId<'db>,
1358 false_const: ConstValueId<'db>,
1360 panic_with_felt252: FunctionId<'db>,
1362 pub upcast_fns: UnorderedHashSet<ExternFunctionId<'db>>,
1364 pub downcast_fns: UnorderedHashMap<ExternFunctionId<'db>, bool>,
1367 pub u128s_from_felt252: ExternFunctionId<'db>,
1369 unwrap_non_zero: ExternFunctionId<'db>,
1371 pub nz_fns: UnorderedHashSet<ExternFunctionId<'db>>,
1373 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 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 (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
1487pub trait ConstantSemantic<'db>: Database {
1489 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 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 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 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 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#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)]
1524pub struct TypeRange {
1525 pub min: BigInt,
1527 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}