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