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