1use std::iter::zip;
2use std::sync::Arc;
3
4use cairo_lang_debug::DebugWithDb;
5use cairo_lang_defs::db::DefsGroup;
6use cairo_lang_defs::ids::{
7 ConstantId, ExternFunctionId, GenericParamId, LanguageElementId, LookupItemId, ModuleItemId,
8 NamedLanguageElementId, TopLevelLanguageElementId, TraitConstantId, TraitId, VarId,
9};
10use cairo_lang_diagnostics::{
11 DiagnosticAdded, DiagnosticEntry, DiagnosticNote, Diagnostics, Maybe, MaybeAsRef,
12 skip_diagnostic,
13};
14use cairo_lang_proc_macros::{DebugWithDb, HeapSize, SemanticObject};
15use cairo_lang_syntax::node::ast::ItemConstant;
16use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
17use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode};
18use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
19use cairo_lang_utils::ordered_hash_set::OrderedHashSet;
20use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
21use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
22use cairo_lang_utils::{Intern, define_short_id, extract_matches, require, try_extract_matches};
23use itertools::Itertools;
24use num_bigint::BigInt;
25use num_traits::{ToPrimitive, Zero};
26use salsa::Database;
27use starknet_types_core::felt::{CAIRO_PRIME_BIGINT, Felt as Felt252};
28
29use super::functions::{GenericFunctionId, GenericFunctionWithBodyId};
30use super::imp::{ImplId, ImplLongId};
31use crate::corelib::{
32 CoreInfo, CorelibSemantic, core_nonzero_ty, false_variant, true_variant,
33 try_extract_bounded_int_type_ranges, try_extract_nz_wrapped_type, unit_ty, validate_literal,
34};
35use crate::diagnostic::{SemanticDiagnosticKind, SemanticDiagnostics, SemanticDiagnosticsBuilder};
36use crate::expr::compute::{ComputationContext, ExprAndId, compute_expr_semantic};
37use crate::expr::inference::conform::InferenceConform;
38use crate::expr::inference::{ConstVar, InferenceId};
39use crate::helper::ModuleHelper;
40use crate::items::enm::SemanticEnumEx;
41use crate::items::extern_function::ExternFunctionSemantic;
42use crate::items::free_function::FreeFunctionSemantic;
43use crate::items::function_with_body::FunctionWithBodySemantic;
44use crate::items::generics::GenericParamSemantic;
45use crate::items::imp::ImplSemantic;
46use crate::items::structure::StructSemantic;
47use crate::items::trt::TraitSemantic;
48use crate::resolve::{Resolver, ResolverData};
49use crate::substitution::{GenericSubstitution, SemanticRewriter};
50use crate::types::resolve_type;
51use crate::{
52 Arenas, ConcreteFunction, ConcreteTypeId, ConcreteVariant, Condition, Expr, ExprBlock,
53 ExprConstant, ExprFunctionCall, ExprFunctionCallArg, ExprId, ExprMemberAccess, ExprStructCtor,
54 FunctionId, GenericParam, LogicalOperator, Pattern, PatternId, SemanticDiagnostic, Statement,
55 TypeId, TypeLongId, semantic_object_for_id,
56};
57
58#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
59#[debug_db(dyn Database)]
60pub struct Constant<'db> {
61 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(
150 &self,
151 db: &'db dyn Database,
152 generic_parameters: &mut OrderedHashSet<GenericParamId<'db>>,
153 ) -> Maybe<()> {
154 match self.long(db) {
155 ConstValue::Int(_, type_id) | ConstValue::Struct(_, type_id) => {
156 type_id.long(db).extract_generic_params(db, generic_parameters)?
157 }
158 ConstValue::Enum(_, const_value_id) => {
159 const_value_id.ty(db)?.long(db).extract_generic_params(db, generic_parameters)?
160 }
161 ConstValue::NonZero(const_value_id) => {
162 const_value_id.extract_generic_params(db, generic_parameters)?
163 }
164 ConstValue::Generic(generic_param_id) => {
165 generic_parameters.insert(*generic_param_id);
166 }
167 ConstValue::ImplConstant(impl_constant_id) => {
168 for garg in impl_constant_id.impl_id().concrete_trait(db)?.generic_args(db) {
169 garg.extract_generic_params(db, generic_parameters)?;
170 }
171 }
172 ConstValue::Var(_, type_id) => {
173 type_id.long(db).extract_generic_params(db, generic_parameters)?
174 }
175 ConstValue::Missing(diagnostic_added) => return Err(*diagnostic_added),
176 }
177 Ok(())
178 }
179}
180
181pub fn felt252_for_downcast(value: &BigInt, range_min: &BigInt) -> BigInt {
183 Felt252::from(value - range_min).to_bigint() + range_min
184}
185
186pub fn canonical_felt252(value: &BigInt) -> BigInt {
188 value % &*CAIRO_PRIME_BIGINT
189}
190
191#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject, HeapSize, salsa::Update)]
193pub enum ConstValue<'db> {
194 Int(#[dont_rewrite] BigInt, TypeId<'db>),
195 Struct(Vec<ConstValueId<'db>>, TypeId<'db>),
196 Enum(ConcreteVariant<'db>, ConstValueId<'db>),
197 NonZero(ConstValueId<'db>),
198 Generic(#[dont_rewrite] GenericParamId<'db>),
199 ImplConstant(ImplConstantId<'db>),
200 Var(ConstVar<'db>, TypeId<'db>),
201 Missing(#[dont_rewrite] DiagnosticAdded),
203}
204impl<'db> ConstValue<'db> {
205 pub fn is_fully_concrete(&self, db: &dyn Database) -> bool {
207 self.ty(db).unwrap().is_fully_concrete(db)
208 && match self {
209 ConstValue::Int(_, _) => true,
210 ConstValue::Struct(members, _) => {
211 members.iter().all(|member| member.is_fully_concrete(db))
212 }
213 ConstValue::Enum(_, val) | ConstValue::NonZero(val) => val.is_fully_concrete(db),
214 ConstValue::Generic(_)
215 | ConstValue::Var(_, _)
216 | ConstValue::Missing(_)
217 | ConstValue::ImplConstant(_) => false,
218 }
219 }
220
221 pub fn is_var_free(&self, db: &dyn Database) -> bool {
223 self.ty(db).unwrap().is_var_free(db)
224 && match self {
225 ConstValue::Int(_, _) | ConstValue::Generic(_) | ConstValue::Missing(_) => true,
226 ConstValue::Struct(members, _) => {
227 members.iter().all(|member| member.is_var_free(db))
228 }
229 ConstValue::Enum(_, val) | ConstValue::NonZero(val) => val.is_var_free(db),
230 ConstValue::Var(_, _) => false,
231 ConstValue::ImplConstant(impl_constant) => impl_constant.impl_id().is_var_free(db),
232 }
233 }
234
235 pub fn ty(&self, db: &'db dyn Database) -> Maybe<TypeId<'db>> {
237 Ok(match self {
238 ConstValue::Int(_, ty) => *ty,
239 ConstValue::Struct(_, ty) => *ty,
240 ConstValue::Enum(variant, _) => {
241 TypeLongId::Concrete(ConcreteTypeId::Enum(variant.concrete_enum_id)).intern(db)
242 }
243 ConstValue::NonZero(value) => core_nonzero_ty(db, value.ty(db)?),
244 ConstValue::Generic(param) => {
245 extract_matches!(db.generic_param_semantic(*param)?, GenericParam::Const).ty
246 }
247 ConstValue::Var(_, ty) => *ty,
248 ConstValue::Missing(_) => TypeId::missing(db, skip_diagnostic()),
249 ConstValue::ImplConstant(impl_constant_id) => {
250 db.impl_constant_concrete_implized_type(*impl_constant_id)?
251 }
252 })
253 }
254
255 pub fn to_int(&self) -> Option<&BigInt> {
257 match self {
258 ConstValue::Int(value, _) => Some(value),
259 _ => None,
260 }
261 }
262}
263
264#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject, HeapSize, salsa::Update)]
266pub struct ImplConstantId<'db> {
267 impl_id: ImplId<'db>,
269 trait_constant_id: TraitConstantId<'db>,
271}
272
273impl<'db> ImplConstantId<'db> {
274 pub fn new(
277 impl_id: ImplId<'db>,
278 trait_constant_id: TraitConstantId<'db>,
279 db: &dyn Database,
280 ) -> Self {
281 if let ImplLongId::Concrete(concrete_impl) = impl_id.long(db) {
282 let impl_def_id = concrete_impl.impl_def_id(db);
283 assert_eq!(Ok(trait_constant_id.trait_id(db)), db.impl_def_trait(impl_def_id));
284 }
285
286 ImplConstantId { impl_id, trait_constant_id }
287 }
288 pub fn impl_id(&self) -> ImplId<'db> {
289 self.impl_id
290 }
291 pub fn trait_constant_id(&self) -> TraitConstantId<'db> {
292 self.trait_constant_id
293 }
294
295 pub fn format(&self, db: &dyn Database) -> String {
296 format!("{}::{}", self.impl_id.name(db), self.trait_constant_id.name(db).long(db))
297 }
298}
299impl<'db> DebugWithDb<'db> for ImplConstantId<'db> {
300 type Db = dyn Database;
301
302 fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
303 write!(f, "{}", self.format(db))
304 }
305}
306
307#[salsa::tracked(returns(ref), cycle_result=constant_semantic_data_cycle)]
309fn constant_semantic_data<'db>(
310 db: &'db dyn Database,
311 const_id: ConstantId<'db>,
312 in_cycle: bool,
313) -> Maybe<ConstantData<'db>> {
314 let lookup_item_id = LookupItemId::ModuleItem(ModuleItemId::Constant(const_id));
315 if in_cycle {
316 constant_semantic_data_cycle_helper(
317 db,
318 &db.module_constant_by_id(const_id)?,
319 lookup_item_id,
320 None,
321 &const_id,
322 )
323 } else {
324 constant_semantic_data_helper(
325 db,
326 &db.module_constant_by_id(const_id)?,
327 lookup_item_id,
328 None,
329 &const_id,
330 )
331 }
332}
333
334fn constant_semantic_data_cycle<'db>(
336 db: &'db dyn Database,
337 _id: salsa::Id,
338 const_id: ConstantId<'db>,
339 _in_cycle: bool,
340) -> Maybe<ConstantData<'db>> {
341 let lookup_item_id = LookupItemId::ModuleItem(ModuleItemId::Constant(const_id));
342 constant_semantic_data_cycle_helper(
343 db,
344 &db.module_constant_by_id(const_id)?,
345 lookup_item_id,
346 None,
347 &const_id,
348 )
349}
350
351pub fn constant_semantic_data_helper<'db>(
353 db: &'db dyn Database,
354 constant_ast: &ItemConstant<'db>,
355 lookup_item_id: LookupItemId<'db>,
356 parent_resolver_data: Option<Arc<ResolverData<'db>>>,
357 element_id: &impl LanguageElementId<'db>,
358) -> Maybe<ConstantData<'db>> {
359 let module_id = element_id.parent_module(db);
360 let mut diagnostics: SemanticDiagnostics<'_> = SemanticDiagnostics::new(module_id);
361 let inference_id = InferenceId::LookupItemDeclaration(lookup_item_id);
366
367 let mut resolver = match parent_resolver_data {
368 Some(parent_resolver_data) => {
369 Resolver::with_data(db, parent_resolver_data.clone_with_inference_id(db, inference_id))
370 }
371 None => Resolver::new(db, module_id, inference_id),
372 };
373 resolver.set_feature_config(element_id, constant_ast, &mut diagnostics);
374
375 let ty_syntax = constant_ast.type_clause(db).ty(db);
376 let constant_type = resolve_type(db, &mut diagnostics, &mut resolver, &ty_syntax);
377 if !constant_type.is_var_free(db) {
380 diagnostics.report(
381 ty_syntax.stable_ptr(db).untyped(),
382 SemanticDiagnosticKind::ConstTypeNotVarFree,
383 );
384 }
385
386 let mut ctx = ComputationContext::new_global(db, &mut diagnostics, &mut resolver);
387
388 let value = compute_expr_semantic(&mut ctx, &constant_ast.value(db));
389 let const_value = resolve_const_expr_and_evaluate(
390 db,
391 &mut ctx,
392 &value,
393 constant_ast.stable_ptr(db).untyped(),
394 constant_type,
395 true,
396 );
397 let constant = Ok(Constant { value: value.id, arenas: Arc::new(ctx.arenas) });
398 let const_value = resolver
399 .inference()
400 .rewrite(const_value)
401 .unwrap_or_else(|_| ConstValue::Missing(skip_diagnostic()).intern(db));
402 let resolver_data = Arc::new(resolver.data);
403 Ok(ConstantData { diagnostics: diagnostics.build(), const_value, constant, resolver_data })
404}
405
406pub fn constant_semantic_data_cycle_helper<'db>(
408 db: &'db dyn Database,
409 constant_ast: &ItemConstant<'db>,
410 lookup_item_id: LookupItemId<'db>,
411 parent_resolver_data: Option<Arc<ResolverData<'db>>>,
412 element_id: &impl LanguageElementId<'db>,
413) -> Maybe<ConstantData<'db>> {
414 let module_id = element_id.parent_module(db);
415 let mut diagnostics: SemanticDiagnostics<'_> = SemanticDiagnostics::new(module_id);
416
417 let inference_id = InferenceId::LookupItemDeclaration(lookup_item_id);
418
419 let resolver = match parent_resolver_data {
420 Some(parent_resolver_data) => {
421 Resolver::with_data(db, parent_resolver_data.clone_with_inference_id(db, inference_id))
422 }
423 None => Resolver::new(db, module_id, inference_id),
424 };
425
426 let resolver_data = Arc::new(resolver.data);
427
428 let diagnostic_added =
429 diagnostics.report(constant_ast.stable_ptr(db), SemanticDiagnosticKind::ConstCycle);
430 Ok(ConstantData {
431 constant: Err(diagnostic_added),
432 const_value: ConstValue::Missing(diagnostic_added).intern(db),
433 diagnostics: diagnostics.build(),
434 resolver_data,
435 })
436}
437
438pub fn validate_const_expr<'db>(ctx: &mut ComputationContext<'db, '_>, expr_id: ExprId) {
440 let info = ctx.db.const_calc_info();
441 let mut eval_ctx = ConstantEvaluateContext {
442 db: ctx.db,
443 info: info.as_ref(),
444 arenas: &ctx.arenas,
445 vars: Default::default(),
446 generic_substitution: Default::default(),
447 depth: 0,
448 diagnostics: ctx.diagnostics,
449 };
450 eval_ctx.validate(expr_id);
451}
452
453pub fn resolve_const_expr_and_evaluate<'db, 'mt>(
455 db: &'db dyn Database,
456 ctx: &'mt mut ComputationContext<'db, '_>,
457 value: &ExprAndId<'db>,
458 const_stable_ptr: SyntaxStablePtrId<'db>,
459 target_type: TypeId<'db>,
460 finalize: bool,
461) -> ConstValueId<'db> {
462 let prev_err_count = ctx.diagnostics.error_count;
463 let mut_ref = &mut ctx.resolver;
464 let mut inference: crate::expr::inference::Inference<'db, '_> = mut_ref.inference();
465 if let Err(err_set) = inference.conform_ty(value.ty(), target_type) {
466 inference.report_on_pending_error(err_set, ctx.diagnostics, const_stable_ptr);
467 }
468
469 if finalize {
470 inference.finalize(ctx.diagnostics, const_stable_ptr);
472 } else if let Err(err_set) = inference.solve() {
473 inference.report_on_pending_error(err_set, ctx.diagnostics, const_stable_ptr);
474 }
475
476 ctx.apply_inference_rewriter_to_exprs();
479
480 match &value.expr {
481 Expr::Constant(ExprConstant { const_value_id, .. }) => *const_value_id,
482 _ if ctx.diagnostics.error_count > prev_err_count => {
484 ConstValue::Missing(skip_diagnostic()).intern(db)
485 }
486 _ => {
487 let info = db.const_calc_info();
488 let mut eval_ctx = ConstantEvaluateContext {
489 db,
490 info: info.as_ref(),
491 arenas: &ctx.arenas,
492 vars: Default::default(),
493 generic_substitution: Default::default(),
494 depth: 0,
495 diagnostics: ctx.diagnostics,
496 };
497 eval_ctx.validate(value.id);
498 if eval_ctx.diagnostics.error_count > prev_err_count {
499 ConstValue::Missing(skip_diagnostic()).intern(db)
500 } else {
501 eval_ctx.evaluate(value.id)
502 }
503 }
504 }
505}
506
507struct ConstantEvaluateContext<'a, 'r, 'mt> {
509 db: &'a dyn Database,
510 info: &'r ConstCalcInfo<'a>,
511 arenas: &'r Arenas<'a>,
512 vars: OrderedHashMap<VarId<'a>, ConstValueId<'a>>,
513 generic_substitution: GenericSubstitution<'a>,
514 depth: usize,
515 diagnostics: &'mt mut SemanticDiagnostics<'a>,
516}
517impl<'a, 'r, 'mt> ConstantEvaluateContext<'a, 'r, 'mt> {
518 fn validate(&mut self, expr_id: ExprId) {
520 match &self.arenas.exprs[expr_id] {
521 Expr::Var(_) | Expr::Constant(_) | Expr::Missing(_) => {}
522 Expr::Block(ExprBlock { statements, tail: Some(inner), .. }) => {
523 for statement_id in statements {
524 match &self.arenas.statements[*statement_id] {
525 Statement::Let(statement) => {
526 self.validate(statement.expr);
527 }
528 Statement::Expr(expr) => {
529 self.validate(expr.expr);
530 }
531 other => {
532 self.diagnostics.report(
533 other.stable_ptr(),
534 SemanticDiagnosticKind::UnsupportedConstant,
535 );
536 }
537 }
538 }
539 self.validate(*inner);
540 }
541 Expr::FunctionCall(expr) => {
542 for arg in &expr.args {
543 match arg {
544 ExprFunctionCallArg::Value(arg) => self.validate(*arg),
545 ExprFunctionCallArg::Reference(var) => {
546 self.diagnostics.report(
547 var.stable_ptr(),
548 SemanticDiagnosticKind::UnsupportedConstant,
549 );
550 }
551 ExprFunctionCallArg::TempReference(expr_id) => {
552 self.diagnostics.report(
553 self.arenas.exprs[*expr_id].stable_ptr(),
554 SemanticDiagnosticKind::UnsupportedConstant,
555 );
556 }
557 }
558 }
559 if !self.is_function_const(expr.function) {
560 self.diagnostics.report(
561 expr.stable_ptr.untyped(),
562 SemanticDiagnosticKind::UnsupportedConstant,
563 );
564 }
565 }
566 Expr::Literal(expr) => {
567 if let Err(err) = validate_literal(self.db, expr.ty, &expr.value) {
568 self.diagnostics.report(
569 expr.stable_ptr.untyped(),
570 SemanticDiagnosticKind::LiteralError(err),
571 );
572 }
573 }
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 self.destructure_pattern(statement.pattern, value);
686 }
687 Statement::Expr(expr) => {
688 self.evaluate(expr.expr);
689 }
690 other => {
691 self.diagnostics.report(
692 other.stable_ptr(),
693 SemanticDiagnosticKind::UnsupportedConstant,
694 );
695 }
696 }
697 }
698 self.evaluate(*inner)
699 }
700 Expr::FunctionCall(expr) => self.evaluate_function_call(expr),
701 Expr::Literal(expr) => ConstValueId::from_int(db, expr.ty, &expr.value),
702 Expr::Tuple(expr) => ConstValue::Struct(
703 expr.items.iter().map(|expr_id| self.evaluate(*expr_id)).collect(),
704 expr.ty,
705 )
706 .intern(db),
707 Expr::StructCtor(ExprStructCtor {
708 members,
709 base_struct: None,
710 ty,
711 concrete_struct_id,
712 ..
713 }) => {
714 let member_order = match db.concrete_struct_members(*concrete_struct_id) {
715 Ok(member_order) => member_order,
716 Err(diag_add) => return to_missing(diag_add),
717 };
718 ConstValue::Struct(
719 member_order
720 .values()
721 .map(|m| {
722 members
723 .iter()
724 .find(|(_, member_id)| m.id == *member_id)
725 .map(|(expr_id, _)| self.evaluate(*expr_id))
726 .expect("Should have been caught by semantic validation")
727 })
728 .collect(),
729 *ty,
730 )
731 .intern(db)
732 }
733 Expr::EnumVariantCtor(expr) => {
734 ConstValue::Enum(expr.variant, self.evaluate(expr.value_expr)).intern(db)
735 }
736 Expr::MemberAccess(expr) => {
737 self.evaluate_member_access(expr).unwrap_or_else(to_missing)
738 }
739 Expr::FixedSizeArray(expr) => ConstValue::Struct(
740 match &expr.items {
741 crate::FixedSizeArrayItems::Items(items) => {
742 items.iter().map(|expr_id| self.evaluate(*expr_id)).collect()
743 }
744 crate::FixedSizeArrayItems::ValueAndSize(value, count) => {
745 let value = self.evaluate(*value);
746 if let Some(count) = count.long(db).to_int() {
747 vec![value; count.to_usize().unwrap()]
748 } else {
749 self.diagnostics.report(
750 expr.stable_ptr.untyped(),
751 SemanticDiagnosticKind::UnsupportedConstant,
752 );
753 vec![]
754 }
755 }
756 },
757 expr.ty,
758 )
759 .intern(db),
760 Expr::Snapshot(expr) => self.evaluate(expr.inner),
761 Expr::Desnap(expr) => self.evaluate(expr.inner),
762 Expr::LogicalOperator(expr) => {
763 let lhs = self.evaluate(expr.lhs);
764 if let ConstValue::Enum(v, _) = lhs.long(db) {
765 let early_return_variant = match expr.op {
766 LogicalOperator::AndAnd => false_variant(self.db),
767 LogicalOperator::OrOr => true_variant(self.db),
768 };
769 if *v == early_return_variant { lhs } else { self.evaluate(expr.rhs) }
770 } else {
771 to_missing(skip_diagnostic())
772 }
773 }
774 Expr::Match(expr) => {
775 let value = self.evaluate(expr.matched_expr);
776 let ConstValue::Enum(variant, value) = value.long(db) else {
777 return to_missing(skip_diagnostic());
778 };
779 for arm in &expr.arms {
780 for pattern_id in &arm.patterns {
781 let pattern = &self.arenas.patterns[*pattern_id];
782 if matches!(pattern, Pattern::Otherwise(_)) {
783 return self.evaluate(arm.expression);
784 }
785 let Pattern::EnumVariant(pattern) = pattern else {
786 continue;
787 };
788 if pattern.variant.idx != variant.idx {
789 continue;
790 }
791 if let Some(inner_pattern) = pattern.inner_pattern {
792 self.destructure_pattern(inner_pattern, *value);
793 }
794 return self.evaluate(arm.expression);
795 }
796 }
797 to_missing(
798 self.diagnostics.report(
799 expr.stable_ptr.untyped(),
800 SemanticDiagnosticKind::UnsupportedConstant,
801 ),
802 )
803 }
804 Expr::If(expr) => {
805 let mut if_condition: bool = true;
806 for condition in &expr.conditions {
807 match condition {
808 crate::Condition::BoolExpr(id) => {
809 let condition = self.evaluate(*id);
810 let ConstValue::Enum(variant, _) = condition.long(db) else {
811 return to_missing(skip_diagnostic());
812 };
813 if *variant != true_variant(self.db) {
814 if_condition = false;
815 break;
816 }
817 }
818 crate::Condition::Let(id, patterns) => {
819 let value = self.evaluate(*id);
820 let ConstValue::Enum(variant, value) = value.long(db) else {
821 return to_missing(skip_diagnostic());
822 };
823 let mut found_pattern = false;
824 for pattern_id in patterns {
825 let Pattern::EnumVariant(pattern) =
826 &self.arenas.patterns[*pattern_id]
827 else {
828 continue;
829 };
830 if pattern.variant != *variant {
831 continue;
833 }
834 if let Some(inner_pattern) = pattern.inner_pattern {
835 self.destructure_pattern(inner_pattern, *value);
836 }
837 found_pattern = true;
838 break;
839 }
840 if !found_pattern {
841 if_condition = false;
842 break;
843 }
844 }
845 }
846 }
847
848 if if_condition {
849 self.evaluate(expr.if_block)
850 } else if let Some(else_block) = expr.else_block {
851 self.evaluate(else_block)
852 } else {
853 self.unit_const
854 }
855 }
856 _ => to_missing(skip_diagnostic()),
857 }
858 }
859
860 fn evaluate_function_call(&mut self, expr: &ExprFunctionCall<'a>) -> ConstValueId<'a> {
862 let db = self.db;
863 let to_missing = |diag_added| ConstValue::Missing(diag_added).intern(db);
864 let args = expr
865 .args
866 .iter()
867 .filter_map(|arg| try_extract_matches!(arg, ExprFunctionCallArg::Value))
868 .map(|arg| self.evaluate(*arg))
869 .collect_vec();
870 if expr.function == self.panic_with_felt252 {
871 return to_missing(self.diagnostics.report(
872 expr.stable_ptr.untyped(),
873 SemanticDiagnosticKind::FailedConstantCalculation,
874 ));
875 }
876 let concrete_function =
877 match self.generic_substitution.substitute(db, expr.function.get_concrete(db)) {
878 Ok(v) => v,
879 Err(err) => return to_missing(err),
880 };
881 if let Some(calc_result) =
882 self.evaluate_const_function_call(&concrete_function, &args, expr)
883 {
884 return calc_result;
885 }
886
887 let imp = extract_matches!(concrete_function.generic_function, GenericFunctionId::Impl);
888 let bool_value = |condition: bool| {
889 if condition { self.true_const } else { self.false_const }
890 };
891
892 if imp.function == self.eq_fn {
893 return bool_value(args[0] == args[1]);
894 } else if imp.function == self.ne_fn {
895 return bool_value(args[0] != args[1]);
896 } else if imp.function == self.not_fn {
897 return bool_value(args[0] == self.false_const);
898 }
899
900 let args = match args
901 .into_iter()
902 .map(|arg| NumericArg::try_new(db, arg))
903 .collect::<Option<Vec<_>>>()
904 {
905 Some(args) => args,
906 None => return to_missing(skip_diagnostic()),
909 };
910 let value = match imp.function {
911 id if id == self.neg_fn => -&args[0].v,
912 id if id == self.add_fn => &args[0].v + &args[1].v,
913 id if id == self.sub_fn => &args[0].v - &args[1].v,
914 id if id == self.mul_fn => &args[0].v * &args[1].v,
915 id if (id == self.div_fn || id == self.rem_fn) && args[1].v.is_zero() => {
916 return to_missing(
917 self.diagnostics
918 .report(expr.stable_ptr.untyped(), SemanticDiagnosticKind::DivisionByZero),
919 );
920 }
921 id if id == self.div_fn => &args[0].v / &args[1].v,
922 id if id == self.rem_fn => &args[0].v % &args[1].v,
923 id if id == self.bitand_fn => &args[0].v & &args[1].v,
924 id if id == self.bitor_fn => &args[0].v | &args[1].v,
925 id if id == self.bitxor_fn => &args[0].v ^ &args[1].v,
926 id if id == self.lt_fn => return bool_value(args[0].v < args[1].v),
927 id if id == self.le_fn => return bool_value(args[0].v <= args[1].v),
928 id if id == self.gt_fn => return bool_value(args[0].v > args[1].v),
929 id if id == self.ge_fn => return bool_value(args[0].v >= args[1].v),
930 id if id == self.div_rem_fn => {
931 return ConstValue::Struct(
934 vec![
935 ConstValueId::from_int(db, args[0].ty, &(&args[0].v / &args[1].v)),
936 ConstValueId::from_int(db, args[0].ty, &(&args[0].v % &args[1].v)),
937 ],
938 expr.ty,
939 )
940 .intern(db);
941 }
942 _ => {
943 unreachable!("Unexpected function call in constant lowering: {:?}", expr)
944 }
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.long(db).to_int()?));
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_for_downcast(arg.long(db).to_int()?, &BigInt::ZERO);
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_ranges(db, success_ty)?;
1018 Some(TypeRange::new(min, max))
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 unreachable!(
1070 "Unexpected extern function in constant lowering: `{}`",
1071 extern_fn.full_path(db)
1072 );
1073 }
1074 }
1075 let body_id = concrete_function.body(db).ok()??;
1076 let concrete_body_id = body_id.function_with_body_id(db);
1077 let signature = db.function_with_body_signature(concrete_body_id).ok()?;
1078 require(signature.is_const)?;
1079 let generic_substitution = body_id.substitution(db).ok()?;
1080 let body = db.function_body(concrete_body_id).ok()?;
1081 const MAX_CONST_EVAL_DEPTH: usize = 100;
1082 if self.depth > MAX_CONST_EVAL_DEPTH {
1083 return Some(
1084 ConstValue::Missing(self.diagnostics.report(
1085 expr.stable_ptr,
1086 SemanticDiagnosticKind::ConstantCalculationDepthExceeded,
1087 ))
1088 .intern(db),
1089 );
1090 }
1091 let mut diagnostics = SemanticDiagnostics::new(concrete_body_id.parent_module(db));
1092 let mut inner = ConstantEvaluateContext {
1093 db,
1094 info: self.info,
1095 arenas: &body.arenas,
1096 vars: signature
1097 .params
1098 .iter()
1099 .map(|p| VarId::Param(p.id))
1100 .zip(args.iter().cloned())
1101 .collect(),
1102 generic_substitution,
1103 depth: self.depth + 1,
1104 diagnostics: &mut diagnostics,
1105 };
1106 let value = inner.evaluate(body.body_expr);
1107 for diagnostic in diagnostics.build().get_all() {
1108 let location = diagnostic.location(db);
1109 let (inner_diag, mut notes) = match diagnostic.kind {
1110 SemanticDiagnosticKind::ConstantCalculationDepthExceeded => {
1111 self.diagnostics.report(
1112 expr.stable_ptr,
1113 SemanticDiagnosticKind::ConstantCalculationDepthExceeded,
1114 );
1115 continue;
1116 }
1117 SemanticDiagnosticKind::InnerFailedConstantCalculation(inner_diag, notes) => {
1118 (inner_diag, notes)
1119 }
1120 _ => (diagnostic.into(), vec![]),
1121 };
1122 notes.push(DiagnosticNote::with_location(
1123 format!("In `{}`", concrete_function.full_path(db)),
1124 location,
1125 ));
1126 self.diagnostics.report(
1127 expr.stable_ptr,
1128 SemanticDiagnosticKind::InnerFailedConstantCalculation(inner_diag, notes),
1129 );
1130 }
1131 Some(value)
1132 }
1133
1134 fn evaluate_member_access(&mut self, expr: &ExprMemberAccess<'a>) -> Maybe<ConstValueId<'a>> {
1136 let full_struct = self.evaluate(expr.expr);
1137 let ConstValue::Struct(values, _) = full_struct.long(self.db) else {
1138 return Err(skip_diagnostic());
1140 };
1141 let members = self.db.concrete_struct_members(expr.concrete_struct_id)?;
1142 let Some(member_idx) = members.iter().position(|(_, member)| member.id == expr.member)
1143 else {
1144 return Err(skip_diagnostic());
1146 };
1147 Ok(values[member_idx])
1148 }
1149
1150 fn destructure_pattern(&mut self, pattern_id: PatternId, value: ConstValueId<'a>) {
1152 let pattern = &self.arenas.patterns[pattern_id];
1153 let db = self.db;
1154 match pattern {
1155 Pattern::Literal(_)
1156 | Pattern::StringLiteral(_)
1157 | Pattern::Otherwise(_)
1158 | Pattern::Missing(_) => {}
1159 Pattern::Variable(pattern) => {
1160 self.vars.insert(VarId::Local(pattern.var.id), value);
1161 }
1162 Pattern::Struct(pattern) => {
1163 if let ConstValue::Struct(inner_values, _) = value.long(db) {
1164 let member_order = match db.concrete_struct_members(pattern.concrete_struct_id)
1165 {
1166 Ok(member_order) => member_order,
1167 Err(_) => return,
1168 };
1169 for (member, inner_value) in zip(member_order.values(), inner_values) {
1170 if let Some((inner_pattern, _)) =
1171 pattern.field_patterns.iter().find(|(_, field)| member.id == field.id)
1172 {
1173 self.destructure_pattern(*inner_pattern, *inner_value);
1174 }
1175 }
1176 }
1177 }
1178 Pattern::Tuple(pattern) => {
1179 if let ConstValue::Struct(inner_values, _) = value.long(db) {
1180 for (inner_pattern, inner_value) in zip(&pattern.field_patterns, inner_values) {
1181 self.destructure_pattern(*inner_pattern, *inner_value);
1182 }
1183 }
1184 }
1185 Pattern::FixedSizeArray(pattern) => {
1186 if let ConstValue::Struct(inner_values, _) = value.long(db) {
1187 for (inner_pattern, inner_value) in
1188 zip(&pattern.elements_patterns, inner_values)
1189 {
1190 self.destructure_pattern(*inner_pattern, *inner_value);
1191 }
1192 }
1193 }
1194 Pattern::EnumVariant(pattern) => {
1195 if let ConstValue::Enum(variant, inner_value) = value.long(db)
1196 && pattern.variant == *variant
1197 && let Some(inner_pattern) = pattern.inner_pattern
1198 {
1199 self.destructure_pattern(inner_pattern, *inner_value);
1200 }
1201 }
1202 }
1203 }
1204}
1205
1206impl<'db, 'r> std::ops::Deref for ConstantEvaluateContext<'db, 'r, '_> {
1207 type Target = ConstCalcInfo<'db>;
1208 fn deref(&self) -> &Self::Target {
1209 self.info
1210 }
1211}
1212
1213struct NumericArg<'db> {
1215 v: BigInt,
1217 ty: TypeId<'db>,
1219}
1220impl<'db> NumericArg<'db> {
1221 fn try_new(db: &'db dyn Database, arg: ConstValueId<'db>) -> Option<Self> {
1222 Some(Self { ty: arg.ty(db).ok()?, v: numeric_arg_value(db, arg)? })
1223 }
1224}
1225
1226fn numeric_arg_value<'db>(db: &'db dyn Database, value: ConstValueId<'db>) -> Option<BigInt> {
1229 match value.long(db) {
1230 ConstValue::Int(value, _) => Some(value.clone()),
1231 ConstValue::Struct(v, _) => {
1232 if let [low, high] = &v[..] {
1233 Some(low.long(db).to_int()? + (high.long(db).to_int()? << 128))
1234 } else {
1235 None
1236 }
1237 }
1238 ConstValue::NonZero(const_value) => numeric_arg_value(db, *const_value),
1239 _ => None,
1240 }
1241}
1242
1243fn const_calc_info<'db>(db: &'db dyn Database) -> Arc<ConstCalcInfo<'db>> {
1245 Arc::new(ConstCalcInfo::new(db))
1246}
1247
1248#[salsa::tracked]
1250fn const_calc_info_tracked<'db>(db: &'db dyn Database) -> Arc<ConstCalcInfo<'db>> {
1251 const_calc_info(db)
1252}
1253
1254#[derive(Debug, PartialEq, Eq, salsa::Update)]
1256pub struct ConstCalcInfo<'db> {
1257 const_traits: UnorderedHashSet<TraitId<'db>>,
1259 unit_const: ConstValueId<'db>,
1261 true_const: ConstValueId<'db>,
1263 false_const: ConstValueId<'db>,
1265 panic_with_felt252: FunctionId<'db>,
1267 pub upcast_fns: UnorderedHashSet<ExternFunctionId<'db>>,
1269 pub downcast_fns: UnorderedHashMap<ExternFunctionId<'db>, bool>,
1272 pub u128s_from_felt252: ExternFunctionId<'db>,
1274 unwrap_non_zero: ExternFunctionId<'db>,
1276 pub nz_fns: UnorderedHashSet<ExternFunctionId<'db>>,
1278 pub type_value_ranges: UnorderedHashMap<TypeId<'db>, TypeRange>,
1280
1281 core_info: Arc<CoreInfo<'db>>,
1282}
1283
1284impl<'db> std::ops::Deref for ConstCalcInfo<'db> {
1285 type Target = CoreInfo<'db>;
1286 fn deref(&self) -> &CoreInfo<'db> {
1287 &self.core_info
1288 }
1289}
1290
1291impl<'db> ConstCalcInfo<'db> {
1292 fn new(db: &'db dyn Database) -> Self {
1294 let core_info = db.core_info();
1295 let unit_const = ConstValue::Struct(vec![], unit_ty(db)).intern(db);
1296 let core = ModuleHelper::core(db);
1297 let bounded_int = core.submodule("internal").submodule("bounded_int");
1298 let integer = core.submodule("integer");
1299 let zeroable = core.submodule("zeroable");
1300 let starknet = core.submodule("starknet");
1301 let class_hash_module = starknet.submodule("class_hash");
1302 let class_hash_ty = class_hash_module.ty("ClassHash", vec![]);
1303 let contract_address_module = starknet.submodule("contract_address");
1304 let contract_address_ty = contract_address_module.ty("ContractAddress", vec![]);
1305 Self {
1306 const_traits: FromIterator::from_iter([
1307 core_info.neg_trt,
1308 core_info.add_trt,
1309 core_info.sub_trt,
1310 core_info.mul_trt,
1311 core_info.div_trt,
1312 core_info.rem_trt,
1313 core_info.div_rem_trt,
1314 core_info.bitand_trt,
1315 core_info.bitor_trt,
1316 core_info.bitxor_trt,
1317 core_info.partialeq_trt,
1318 core_info.partialord_trt,
1319 core_info.not_trt,
1320 ]),
1321 true_const: ConstValue::Enum(true_variant(db), unit_const).intern(db),
1322 false_const: ConstValue::Enum(false_variant(db), unit_const).intern(db),
1323 unit_const,
1324 panic_with_felt252: core.function_id("panic_with_felt252", vec![]),
1325 upcast_fns: FromIterator::from_iter([
1326 bounded_int.extern_function_id("upcast"),
1327 integer.extern_function_id("u8_to_felt252"),
1328 integer.extern_function_id("u16_to_felt252"),
1329 integer.extern_function_id("u32_to_felt252"),
1330 integer.extern_function_id("u64_to_felt252"),
1331 integer.extern_function_id("u128_to_felt252"),
1332 integer.extern_function_id("i8_to_felt252"),
1333 integer.extern_function_id("i16_to_felt252"),
1334 integer.extern_function_id("i32_to_felt252"),
1335 integer.extern_function_id("i64_to_felt252"),
1336 integer.extern_function_id("i128_to_felt252"),
1337 class_hash_module.extern_function_id("class_hash_to_felt252"),
1338 contract_address_module.extern_function_id("contract_address_to_felt252"),
1339 ]),
1340 downcast_fns: FromIterator::from_iter([
1341 (bounded_int.extern_function_id("downcast"), false),
1342 (bounded_int.extern_function_id("bounded_int_trim_min"), true),
1343 (bounded_int.extern_function_id("bounded_int_trim_max"), true),
1344 (integer.extern_function_id("u8_try_from_felt252"), false),
1345 (integer.extern_function_id("u16_try_from_felt252"), false),
1346 (integer.extern_function_id("u32_try_from_felt252"), false),
1347 (integer.extern_function_id("u64_try_from_felt252"), false),
1348 (integer.extern_function_id("i8_try_from_felt252"), false),
1349 (integer.extern_function_id("i16_try_from_felt252"), false),
1350 (integer.extern_function_id("i32_try_from_felt252"), false),
1351 (integer.extern_function_id("i64_try_from_felt252"), false),
1352 (integer.extern_function_id("i128_try_from_felt252"), false),
1353 (class_hash_module.extern_function_id("class_hash_try_from_felt252"), false),
1354 (
1355 contract_address_module.extern_function_id("contract_address_try_from_felt252"),
1356 false,
1357 ),
1358 ]),
1359 u128s_from_felt252: integer.extern_function_id("u128s_from_felt252"),
1360 unwrap_non_zero: zeroable.extern_function_id("unwrap_non_zero"),
1361 nz_fns: FromIterator::from_iter([
1362 core.extern_function_id("felt252_is_zero"),
1363 bounded_int.extern_function_id("bounded_int_is_zero"),
1364 integer.extern_function_id("u8_is_zero"),
1365 integer.extern_function_id("u16_is_zero"),
1366 integer.extern_function_id("u32_is_zero"),
1367 integer.extern_function_id("u64_is_zero"),
1368 integer.extern_function_id("u128_is_zero"),
1369 integer.extern_function_id("u256_is_zero"),
1370 ]),
1371 type_value_ranges: FromIterator::from_iter([
1372 (core_info.u8, TypeRange::new(u8::MIN, u8::MAX)),
1373 (core_info.u16, TypeRange::new(u16::MIN, u16::MAX)),
1374 (core_info.u32, TypeRange::new(u32::MIN, u32::MAX)),
1375 (core_info.u64, TypeRange::new(u64::MIN, u64::MAX)),
1376 (core_info.u128, TypeRange::new(u128::MIN, u128::MAX)),
1377 (core_info.u256, TypeRange::new(BigInt::ZERO, BigInt::from(1) << 256)),
1378 (core_info.i8, TypeRange::new(i8::MIN, i8::MAX)),
1379 (core_info.i16, TypeRange::new(i16::MIN, i16::MAX)),
1380 (core_info.i32, TypeRange::new(i32::MIN, i32::MAX)),
1381 (core_info.i64, TypeRange::new(i64::MIN, i64::MAX)),
1382 (core_info.i128, TypeRange::new(i128::MIN, i128::MAX)),
1383 (class_hash_ty, TypeRange::new(BigInt::ZERO, BigInt::from(1) << 251)),
1384 (contract_address_ty, TypeRange::new(BigInt::ZERO, BigInt::from(1) << 251)),
1385 ]),
1386 core_info,
1387 }
1388 }
1389}
1390
1391pub trait ConstantSemantic<'db>: Database {
1393 fn constant_semantic_diagnostics(
1395 &'db self,
1396 const_id: ConstantId<'db>,
1397 ) -> Diagnostics<'db, SemanticDiagnostic<'db>> {
1398 let db = self.as_dyn_database();
1399 constant_semantic_data(db, const_id, false)
1400 .as_ref()
1401 .map(|data| data.diagnostics.clone())
1402 .unwrap_or_default()
1403 }
1404 fn constant_semantic_data(&'db self, use_id: ConstantId<'db>) -> Maybe<Constant<'db>> {
1406 let db = self.as_dyn_database();
1407 constant_semantic_data(db, use_id, false).maybe_as_ref()?.constant.clone()
1408 }
1409 fn constant_resolver_data(&'db self, use_id: ConstantId<'db>) -> Maybe<Arc<ResolverData<'db>>> {
1411 let db = self.as_dyn_database();
1412 Ok(constant_semantic_data(db, use_id, false).maybe_as_ref()?.resolver_data.clone())
1413 }
1414 fn constant_const_value(&'db self, const_id: ConstantId<'db>) -> Maybe<ConstValueId<'db>> {
1416 let db = self.as_dyn_database();
1417 Ok(constant_semantic_data(db, const_id, false).maybe_as_ref()?.const_value)
1418 }
1419 fn const_calc_info(&'db self) -> Arc<ConstCalcInfo<'db>> {
1421 const_calc_info_tracked(self.as_dyn_database())
1422 }
1423}
1424impl<'db, T: Database + ?Sized> ConstantSemantic<'db> for T {}
1425
1426#[derive(Clone, Debug, PartialEq, Eq, salsa::Update)]
1428pub struct TypeRange {
1429 pub min: BigInt,
1431 pub max: BigInt,
1433}
1434impl TypeRange {
1435 pub fn new(min: impl Into<BigInt>, max: impl Into<BigInt>) -> Self {
1436 Self { min: min.into(), max: max.into() }
1437 }
1438}