1use std::iter::zip;
2use std::sync::Arc;
3
4use cairo_lang_debug::DebugWithDb;
5use cairo_lang_defs::ids::{
6 ConstantId, ExternFunctionId, GenericParamId, LanguageElementId, LookupItemId, ModuleItemId,
7 NamedLanguageElementId, TopLevelLanguageElementId, TraitConstantId, TraitId, VarId,
8};
9use cairo_lang_diagnostics::{
10 DiagnosticAdded, DiagnosticEntry, DiagnosticNote, Diagnostics, Maybe, ToMaybe, skip_diagnostic,
11};
12use cairo_lang_proc_macros::{DebugWithDb, SemanticObject};
13use cairo_lang_syntax::node::ast::ItemConstant;
14use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
15use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode};
16use cairo_lang_utils::ordered_hash_map::OrderedHashMap;
17use cairo_lang_utils::unordered_hash_set::UnorderedHashSet;
18use cairo_lang_utils::{
19 Intern, LookupIntern, define_short_id, extract_matches, require, try_extract_matches,
20};
21use itertools::Itertools;
22use num_bigint::BigInt;
23use num_traits::{Num, ToPrimitive, Zero};
24use smol_str::SmolStr;
25
26use super::functions::{GenericFunctionId, GenericFunctionWithBodyId};
27use super::imp::{ImplId, ImplLongId};
28use crate::corelib::{
29 CoreInfo, LiteralError, core_box_ty, core_nonzero_ty, false_variant, get_core_ty_by_name,
30 true_variant, try_extract_nz_wrapped_type, unit_ty, validate_literal,
31};
32use crate::db::SemanticGroup;
33use crate::diagnostic::{SemanticDiagnosticKind, SemanticDiagnostics, SemanticDiagnosticsBuilder};
34use crate::expr::compute::{
35 ComputationContext, ContextFunction, Environment, ExprAndId, compute_expr_semantic,
36};
37use crate::expr::inference::conform::InferenceConform;
38use crate::expr::inference::{ConstVar, InferenceId};
39use crate::helper::ModuleHelper;
40use crate::items::enm::SemanticEnumEx;
41use crate::resolve::{Resolver, ResolverData};
42use crate::substitution::{GenericSubstitution, SemanticRewriter};
43use crate::types::resolve_type;
44use crate::{
45 Arenas, ConcreteFunction, ConcreteTypeId, ConcreteVariant, Condition, Expr, ExprBlock,
46 ExprConstant, ExprFunctionCall, ExprFunctionCallArg, ExprId, ExprMemberAccess, ExprStructCtor,
47 FunctionId, GenericParam, LogicalOperator, Pattern, PatternId, SemanticDiagnostic, Statement,
48 TypeId, TypeLongId, semantic_object_for_id,
49};
50
51#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
52#[debug_db(dyn SemanticGroup + 'static)]
53pub struct Constant {
54 pub value: ExprId,
56 pub arenas: Arc<Arenas>,
58}
59
60impl Constant {
61 pub fn ty(&self) -> TypeId {
62 self.arenas.exprs[self.value].ty()
63 }
64}
65
66#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb)]
70#[debug_db(dyn SemanticGroup + 'static)]
71pub struct ConstantData {
72 pub diagnostics: Diagnostics<SemanticDiagnostic>,
73 pub constant: Maybe<Constant>,
74 pub const_value: ConstValueId,
75 pub resolver_data: Arc<ResolverData>,
76}
77
78define_short_id!(
79 ConstValueId,
80 ConstValue,
81 SemanticGroup,
82 lookup_intern_const_value,
83 intern_const_value
84);
85semantic_object_for_id!(ConstValueId, lookup_intern_const_value, intern_const_value, ConstValue);
86impl ConstValueId {
87 pub fn format(&self, db: &dyn SemanticGroup) -> String {
88 format!("{:?}", self.lookup_intern(db).debug(db.elongate()))
89 }
90
91 pub fn is_fully_concrete(&self, db: &dyn SemanticGroup) -> bool {
93 self.lookup_intern(db).is_fully_concrete(db)
94 }
95
96 pub fn is_var_free(&self, db: &dyn SemanticGroup) -> bool {
98 self.lookup_intern(db).is_var_free(db)
99 }
100
101 pub fn ty(&self, db: &dyn SemanticGroup) -> Maybe<TypeId> {
103 self.lookup_intern(db).ty(db)
104 }
105}
106
107#[derive(Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
109pub enum ConstValue {
110 Int(#[dont_rewrite] BigInt, TypeId),
111 Struct(Vec<ConstValue>, TypeId),
112 Enum(ConcreteVariant, Box<ConstValue>),
113 NonZero(Box<ConstValue>),
114 Boxed(Box<ConstValue>),
115 Generic(#[dont_rewrite] GenericParamId),
116 ImplConstant(ImplConstantId),
117 Var(ConstVar, TypeId),
118 Missing(#[dont_rewrite] DiagnosticAdded),
120}
121impl ConstValue {
122 pub fn is_fully_concrete(&self, db: &dyn SemanticGroup) -> bool {
124 self.ty(db).unwrap().is_fully_concrete(db)
125 && match self {
126 ConstValue::Int(_, _) => true,
127 ConstValue::Struct(members, _) => {
128 members.iter().all(|member: &ConstValue| member.is_fully_concrete(db))
129 }
130 ConstValue::Enum(_, value)
131 | ConstValue::NonZero(value)
132 | ConstValue::Boxed(value) => value.is_fully_concrete(db),
133 ConstValue::Generic(_)
134 | ConstValue::Var(_, _)
135 | ConstValue::Missing(_)
136 | ConstValue::ImplConstant(_) => false,
137 }
138 }
139
140 pub fn is_var_free(&self, db: &dyn SemanticGroup) -> bool {
142 self.ty(db).unwrap().is_var_free(db)
143 && match self {
144 ConstValue::Int(_, _) | ConstValue::Generic(_) | ConstValue::Missing(_) => true,
145 ConstValue::Struct(members, _) => {
146 members.iter().all(|member| member.is_var_free(db))
147 }
148 ConstValue::Enum(_, value)
149 | ConstValue::NonZero(value)
150 | ConstValue::Boxed(value) => value.is_var_free(db),
151 ConstValue::Var(_, _) => false,
152 ConstValue::ImplConstant(impl_constant) => impl_constant.impl_id().is_var_free(db),
153 }
154 }
155
156 pub fn ty(&self, db: &dyn SemanticGroup) -> Maybe<TypeId> {
158 Ok(match self {
159 ConstValue::Int(_, ty) => *ty,
160 ConstValue::Struct(_, ty) => *ty,
161 ConstValue::Enum(variant, _) => {
162 TypeLongId::Concrete(ConcreteTypeId::Enum(variant.concrete_enum_id)).intern(db)
163 }
164 ConstValue::NonZero(value) => core_nonzero_ty(db, value.ty(db)?),
165 ConstValue::Boxed(value) => core_box_ty(db, value.ty(db)?),
166 ConstValue::Generic(param) => {
167 extract_matches!(db.generic_param_semantic(*param)?, GenericParam::Const).ty
168 }
169 ConstValue::Var(_, ty) => *ty,
170 ConstValue::Missing(_) => TypeId::missing(db, skip_diagnostic()),
171 ConstValue::ImplConstant(impl_constant_id) => {
172 db.impl_constant_concrete_implized_type(*impl_constant_id)?
173 }
174 })
175 }
176
177 pub fn into_int(self) -> Option<BigInt> {
179 match self {
180 ConstValue::Int(value, _) => Some(value.clone()),
181 _ => None,
182 }
183 }
184}
185
186#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, SemanticObject)]
188pub struct ImplConstantId {
189 impl_id: ImplId,
191 trait_constant_id: TraitConstantId,
193}
194
195impl ImplConstantId {
196 pub fn new(
199 impl_id: ImplId,
200 trait_constant_id: TraitConstantId,
201 db: &dyn SemanticGroup,
202 ) -> Self {
203 if let ImplLongId::Concrete(concrete_impl) = impl_id.lookup_intern(db) {
204 let impl_def_id = concrete_impl.impl_def_id(db);
205 assert_eq!(Ok(trait_constant_id.trait_id(db.upcast())), db.impl_def_trait(impl_def_id));
206 }
207
208 ImplConstantId { impl_id, trait_constant_id }
209 }
210 pub fn impl_id(&self) -> ImplId {
211 self.impl_id
212 }
213 pub fn trait_constant_id(&self) -> TraitConstantId {
214 self.trait_constant_id
215 }
216
217 pub fn format(&self, db: &dyn SemanticGroup) -> SmolStr {
218 format!("{}::{}", self.impl_id.name(db.upcast()), self.trait_constant_id.name(db.upcast()))
219 .into()
220 }
221}
222impl DebugWithDb<dyn SemanticGroup> for ImplConstantId {
223 fn fmt(
224 &self,
225 f: &mut std::fmt::Formatter<'_>,
226 db: &(dyn SemanticGroup + 'static),
227 ) -> std::fmt::Result {
228 write!(f, "{}", self.format(db))
229 }
230}
231
232pub fn priv_constant_semantic_data(
234 db: &dyn SemanticGroup,
235 const_id: ConstantId,
236 in_cycle: bool,
237) -> Maybe<ConstantData> {
238 let lookup_item_id = LookupItemId::ModuleItem(ModuleItemId::Constant(const_id));
239 if in_cycle {
240 constant_semantic_data_cycle_helper(
241 db,
242 &db.module_constant_by_id(const_id)?.to_maybe()?,
243 lookup_item_id,
244 None,
245 &const_id,
246 )
247 } else {
248 constant_semantic_data_helper(
249 db,
250 &db.module_constant_by_id(const_id)?.to_maybe()?,
251 lookup_item_id,
252 None,
253 &const_id,
254 )
255 }
256}
257
258pub fn priv_constant_semantic_data_cycle(
260 db: &dyn SemanticGroup,
261 _cycle: &salsa::Cycle,
262 const_id: &ConstantId,
263 _in_cycle: &bool,
264) -> Maybe<ConstantData> {
265 priv_constant_semantic_data(db, *const_id, true)
266}
267
268pub fn constant_semantic_data_helper(
270 db: &dyn SemanticGroup,
271 constant_ast: &ItemConstant,
272 lookup_item_id: LookupItemId,
273 parent_resolver_data: Option<Arc<ResolverData>>,
274 element_id: &impl LanguageElementId,
275) -> Maybe<ConstantData> {
276 let mut diagnostics: SemanticDiagnostics = SemanticDiagnostics::default();
277 let syntax_db = db.upcast();
281
282 let inference_id = InferenceId::LookupItemDeclaration(lookup_item_id);
283
284 let mut resolver = match parent_resolver_data {
285 Some(parent_resolver_data) => {
286 Resolver::with_data(db, parent_resolver_data.clone_with_inference_id(db, inference_id))
287 }
288 None => Resolver::new(db, element_id.module_file_id(db.upcast()), inference_id),
289 };
290 resolver.set_feature_config(element_id, constant_ast, &mut diagnostics);
291
292 let constant_type = resolve_type(
293 db,
294 &mut diagnostics,
295 &mut resolver,
296 &constant_ast.type_clause(syntax_db).ty(syntax_db),
297 );
298
299 let environment = Environment::empty();
300 let mut ctx = ComputationContext::new(
301 db,
302 &mut diagnostics,
303 resolver,
304 None,
305 environment,
306 ContextFunction::Global,
307 );
308
309 let value = compute_expr_semantic(&mut ctx, &constant_ast.value(syntax_db));
310 let const_value = resolve_const_expr_and_evaluate(
311 db,
312 &mut ctx,
313 &value,
314 constant_ast.stable_ptr().untyped(),
315 constant_type,
316 true,
317 )
318 .intern(db);
319
320 let const_value = ctx
321 .resolver
322 .inference()
323 .rewrite(const_value)
324 .unwrap_or_else(|_| ConstValue::Missing(skip_diagnostic()).intern(db));
325 let resolver_data = Arc::new(ctx.resolver.data);
326 let constant = Constant { value: value.id, arenas: Arc::new(ctx.arenas) };
327 Ok(ConstantData {
328 diagnostics: diagnostics.build(),
329 const_value,
330 constant: Ok(constant),
331 resolver_data,
332 })
333}
334
335pub fn constant_semantic_data_cycle_helper(
337 db: &dyn SemanticGroup,
338 constant_ast: &ItemConstant,
339 lookup_item_id: LookupItemId,
340 parent_resolver_data: Option<Arc<ResolverData>>,
341 element_id: &impl LanguageElementId,
342) -> Maybe<ConstantData> {
343 let mut diagnostics: SemanticDiagnostics = SemanticDiagnostics::default();
344
345 let inference_id = InferenceId::LookupItemDeclaration(lookup_item_id);
346
347 let resolver = match parent_resolver_data {
348 Some(parent_resolver_data) => {
349 Resolver::with_data(db, parent_resolver_data.clone_with_inference_id(db, inference_id))
350 }
351 None => Resolver::new(db, element_id.module_file_id(db.upcast()), inference_id),
352 };
353
354 let resolver_data = Arc::new(resolver.data);
355
356 let diagnostic_added = diagnostics.report(constant_ast, SemanticDiagnosticKind::ConstCycle);
357 Ok(ConstantData {
358 constant: Err(diagnostic_added),
359 const_value: ConstValue::Missing(diagnostic_added).intern(db),
360 diagnostics: diagnostics.build(),
361 resolver_data,
362 })
363}
364
365pub fn validate_const_expr(ctx: &mut ComputationContext<'_>, expr_id: ExprId) {
367 let info = ctx.db.const_calc_info();
368 let mut eval_ctx = ConstantEvaluateContext {
369 db: ctx.db,
370 info: info.as_ref(),
371 arenas: &ctx.arenas,
372 vars: Default::default(),
373 generic_substitution: Default::default(),
374 depth: 0,
375 diagnostics: ctx.diagnostics,
376 };
377 eval_ctx.validate(expr_id);
378}
379
380pub fn resolve_const_expr_and_evaluate(
382 db: &dyn SemanticGroup,
383 ctx: &mut ComputationContext<'_>,
384 value: &ExprAndId,
385 const_stable_ptr: SyntaxStablePtrId,
386 target_type: TypeId,
387 finalize: bool,
388) -> ConstValue {
389 let prev_err_count = ctx.diagnostics.error_count;
390 let inference = &mut ctx.resolver.inference();
391 if let Err(err_set) = inference.conform_ty(value.ty(), target_type) {
392 inference.report_on_pending_error(err_set, ctx.diagnostics, const_stable_ptr);
393 }
394
395 if finalize {
396 inference.finalize(ctx.diagnostics, const_stable_ptr);
398 } else if let Err(err_set) = inference.solve() {
399 inference.report_on_pending_error(err_set, ctx.diagnostics, const_stable_ptr);
400 }
401
402 ctx.apply_inference_rewriter_to_exprs();
405
406 match &value.expr {
407 Expr::Constant(ExprConstant { const_value_id, .. }) => const_value_id.lookup_intern(db),
408 _ if ctx.diagnostics.error_count > prev_err_count => ConstValue::Missing(skip_diagnostic()),
410 _ => {
411 let info = db.const_calc_info();
412 let mut eval_ctx = ConstantEvaluateContext {
413 db,
414 info: info.as_ref(),
415 arenas: &ctx.arenas,
416 vars: Default::default(),
417 generic_substitution: Default::default(),
418 depth: 0,
419 diagnostics: ctx.diagnostics,
420 };
421 eval_ctx.validate(value.id);
422 if eval_ctx.diagnostics.error_count > prev_err_count {
423 ConstValue::Missing(skip_diagnostic())
424 } else {
425 eval_ctx.evaluate(value.id)
426 }
427 }
428 }
429}
430
431pub fn value_as_const_value(
433 db: &dyn SemanticGroup,
434 ty: TypeId,
435 value: &BigInt,
436) -> Result<ConstValue, LiteralError> {
437 validate_literal(db.upcast(), ty, value)?;
438 let get_basic_const_value = |ty| {
439 let u256_ty = get_core_ty_by_name(db.upcast(), "u256".into(), vec![]);
440
441 if ty != u256_ty {
442 ConstValue::Int(value.clone(), ty)
443 } else {
444 let u128_ty = get_core_ty_by_name(db.upcast(), "u128".into(), vec![]);
445 let mask128 = BigInt::from(u128::MAX);
446 let low = value & mask128;
447 let high = value >> 128;
448 ConstValue::Struct(
449 vec![(ConstValue::Int(low, u128_ty)), (ConstValue::Int(high, u128_ty))],
450 ty,
451 )
452 }
453 };
454
455 if let Some(inner) = try_extract_nz_wrapped_type(db.upcast(), ty) {
456 Ok(ConstValue::NonZero(Box::new(get_basic_const_value(inner))))
457 } else {
458 Ok(get_basic_const_value(ty))
459 }
460}
461
462struct ConstantEvaluateContext<'a> {
464 db: &'a dyn SemanticGroup,
465 info: &'a ConstCalcInfo,
466 arenas: &'a Arenas,
467 vars: OrderedHashMap<VarId, ConstValue>,
468 generic_substitution: GenericSubstitution,
469 depth: usize,
470 diagnostics: &'a mut SemanticDiagnostics,
471}
472impl ConstantEvaluateContext<'_> {
473 fn validate(&mut self, expr_id: ExprId) {
475 match &self.arenas.exprs[expr_id] {
476 Expr::Var(_) | Expr::Constant(_) | Expr::Missing(_) => {}
477 Expr::Block(ExprBlock { statements, tail: Some(inner), .. }) => {
478 for statement_id in statements {
479 match &self.arenas.statements[*statement_id] {
480 Statement::Let(statement) => {
481 self.validate(statement.expr);
482 }
483 Statement::Expr(expr) => {
484 self.validate(expr.expr);
485 }
486 other => {
487 self.diagnostics.report(
488 other.stable_ptr(),
489 SemanticDiagnosticKind::UnsupportedConstant,
490 );
491 }
492 }
493 }
494 self.validate(*inner);
495 }
496 Expr::FunctionCall(expr) => {
497 for arg in &expr.args {
498 match arg {
499 ExprFunctionCallArg::Value(arg) => self.validate(*arg),
500 ExprFunctionCallArg::Reference(var) => {
501 self.diagnostics.report(
502 var.stable_ptr(),
503 SemanticDiagnosticKind::UnsupportedConstant,
504 );
505 }
506 }
507 if let ExprFunctionCallArg::Value(arg) = arg {
508 self.validate(*arg);
509 }
510 }
511 if !self.is_function_const(expr.function) {
512 self.diagnostics.report(
513 expr.stable_ptr.untyped(),
514 SemanticDiagnosticKind::UnsupportedConstant,
515 );
516 }
517 }
518 Expr::Literal(expr) => {
519 if let Err(err) = validate_literal(self.db, expr.ty, &expr.value) {
520 self.diagnostics.report(
521 expr.stable_ptr.untyped(),
522 SemanticDiagnosticKind::LiteralError(err),
523 );
524 }
525 }
526 Expr::Tuple(expr) => {
527 for item in &expr.items {
528 self.validate(*item);
529 }
530 }
531 Expr::StructCtor(ExprStructCtor { members, base_struct: None, .. }) => {
532 for (_, expr_id) in members {
533 self.validate(*expr_id);
534 }
535 }
536 Expr::EnumVariantCtor(expr) => self.validate(expr.value_expr),
537 Expr::MemberAccess(expr) => self.validate(expr.expr),
538 Expr::FixedSizeArray(expr) => match &expr.items {
539 crate::FixedSizeArrayItems::Items(items) => {
540 for item in items {
541 self.validate(*item);
542 }
543 }
544 crate::FixedSizeArrayItems::ValueAndSize(value, _) => {
545 self.validate(*value);
546 }
547 },
548 Expr::Snapshot(expr) => self.validate(expr.inner),
549 Expr::Desnap(expr) => self.validate(expr.inner),
550 Expr::LogicalOperator(expr) => {
551 self.validate(expr.lhs);
552 self.validate(expr.rhs);
553 }
554 Expr::Match(expr) => {
555 self.validate(expr.matched_expr);
556 for arm in &expr.arms {
557 self.validate(arm.expression);
558 }
559 }
560 Expr::If(expr) => {
561 self.validate(match &expr.condition {
562 Condition::BoolExpr(id) | Condition::Let(id, _) => *id,
563 });
564 self.validate(expr.if_block);
565 if let Some(else_block) = expr.else_block {
566 self.validate(else_block);
567 }
568 }
569 other => {
570 self.diagnostics.report(
571 other.stable_ptr().untyped(),
572 SemanticDiagnosticKind::UnsupportedConstant,
573 );
574 }
575 }
576 }
577
578 fn is_function_const(&self, function_id: FunctionId) -> bool {
580 if function_id == self.panic_with_felt252 {
581 return true;
582 }
583 let db = self.db;
584 let concrete_function = function_id.get_concrete(db);
585 let signature = (|| match concrete_function.generic_function {
586 GenericFunctionId::Free(id) => db.free_function_signature(id),
587 GenericFunctionId::Extern(id) => db.extern_function_signature(id),
588 GenericFunctionId::Impl(id) => {
589 if let ImplLongId::Concrete(impl_id) = id.impl_id.lookup_intern(db) {
590 if let Ok(Some(impl_function_id)) = impl_id.get_impl_function(db, id.function) {
591 return self.db.impl_function_signature(impl_function_id);
592 }
593 }
594 self.db.trait_function_signature(id.function)
595 }
596 })();
597 if signature.map(|s| s.is_const) == Ok(true) {
598 return true;
599 }
600 let Ok(Some(body)) = concrete_function.body(db) else { return false };
601 let GenericFunctionWithBodyId::Impl(imp) = body.generic_function(db) else {
602 return false;
603 };
604 let impl_def = imp.concrete_impl_id.impl_def_id(db);
605 if impl_def.parent_module(db.upcast()).owning_crate(db.upcast()) != db.core_crate() {
606 return false;
607 }
608 let Ok(trait_id) = db.impl_def_trait(impl_def) else {
609 return false;
610 };
611 self.const_traits.contains(&trait_id)
612 }
613
614 fn evaluate(&mut self, expr_id: ExprId) -> ConstValue {
616 let expr = &self.arenas.exprs[expr_id];
617 let db = self.db;
618 match expr {
619 Expr::Var(expr) => self
620 .vars
621 .get(&expr.var)
622 .cloned()
623 .unwrap_or_else(|| ConstValue::Missing(skip_diagnostic())),
624 Expr::Constant(expr) => self
625 .generic_substitution
626 .substitute(self.db, expr.const_value_id.lookup_intern(db))
627 .unwrap_or_else(ConstValue::Missing),
628 Expr::Block(ExprBlock { statements, tail: Some(inner), .. }) => {
629 for statement_id in statements {
630 match &self.arenas.statements[*statement_id] {
631 Statement::Let(statement) => {
632 let value = self.evaluate(statement.expr);
633 self.destructure_pattern(statement.pattern, value);
634 }
635 Statement::Expr(expr) => {
636 self.evaluate(expr.expr);
637 }
638 other => {
639 self.diagnostics.report(
640 other.stable_ptr(),
641 SemanticDiagnosticKind::UnsupportedConstant,
642 );
643 }
644 }
645 }
646 self.evaluate(*inner)
647 }
648 Expr::FunctionCall(expr) => self.evaluate_function_call(expr),
649 Expr::Literal(expr) => value_as_const_value(db, expr.ty, &expr.value)
650 .expect("LiteralError should have been caught on `validate`"),
651 Expr::Tuple(expr) => ConstValue::Struct(
652 expr.items.iter().map(|expr_id| self.evaluate(*expr_id)).collect(),
653 expr.ty,
654 ),
655 Expr::StructCtor(ExprStructCtor {
656 members,
657 base_struct: None,
658 ty,
659 concrete_struct_id,
660 ..
661 }) => {
662 let member_order = match db.concrete_struct_members(*concrete_struct_id) {
663 Ok(member_order) => member_order,
664 Err(diag_add) => return ConstValue::Missing(diag_add),
665 };
666 ConstValue::Struct(
667 member_order
668 .values()
669 .map(|m| {
670 members
671 .iter()
672 .find(|(member_id, _)| m.id == *member_id)
673 .map(|(_, expr_id)| self.evaluate(*expr_id))
674 .expect("Should have been caught by semantic validation")
675 })
676 .collect(),
677 *ty,
678 )
679 }
680 Expr::EnumVariantCtor(expr) => {
681 ConstValue::Enum(expr.variant.clone(), Box::new(self.evaluate(expr.value_expr)))
682 }
683 Expr::MemberAccess(expr) => {
684 self.evaluate_member_access(expr).unwrap_or_else(ConstValue::Missing)
685 }
686 Expr::FixedSizeArray(expr) => ConstValue::Struct(
687 match &expr.items {
688 crate::FixedSizeArrayItems::Items(items) => {
689 items.iter().map(|expr_id| self.evaluate(*expr_id)).collect()
690 }
691 crate::FixedSizeArrayItems::ValueAndSize(value, count) => {
692 let value = self.evaluate(*value);
693 let count = count.lookup_intern(db);
694 if let Some(count) = count.into_int() {
695 (0..count.to_usize().unwrap()).map(|_| value.clone()).collect()
696 } else {
697 self.diagnostics.report(
698 expr.stable_ptr.untyped(),
699 SemanticDiagnosticKind::UnsupportedConstant,
700 );
701 vec![]
702 }
703 }
704 },
705 expr.ty,
706 ),
707 Expr::Snapshot(expr) => self.evaluate(expr.inner),
708 Expr::Desnap(expr) => self.evaluate(expr.inner),
709 Expr::LogicalOperator(expr) => {
710 let lhs = self.evaluate(expr.lhs);
711 if let ConstValue::Enum(v, _) = &lhs {
712 let early_return_variant = match expr.op {
713 LogicalOperator::AndAnd => false_variant(self.db),
714 LogicalOperator::OrOr => true_variant(self.db),
715 };
716 if *v == early_return_variant { lhs } else { self.evaluate(expr.lhs) }
717 } else {
718 ConstValue::Missing(skip_diagnostic())
719 }
720 }
721 Expr::Match(expr) => {
722 let value = self.evaluate(expr.matched_expr);
723 let ConstValue::Enum(variant, value) = value else {
724 return ConstValue::Missing(skip_diagnostic());
725 };
726 for arm in &expr.arms {
727 for pattern_id in &arm.patterns {
728 let pattern = &self.arenas.patterns[*pattern_id];
729 if matches!(pattern, Pattern::Otherwise(_)) {
730 return self.evaluate(arm.expression);
731 }
732 let Pattern::EnumVariant(pattern) = pattern else {
733 continue;
734 };
735 if pattern.variant.idx != variant.idx {
736 continue;
737 }
738 if let Some(inner_pattern) = pattern.inner_pattern {
739 self.destructure_pattern(inner_pattern, *value);
740 }
741 return self.evaluate(arm.expression);
742 }
743 }
744 ConstValue::Missing(
745 self.diagnostics.report(
746 expr.stable_ptr.untyped(),
747 SemanticDiagnosticKind::UnsupportedConstant,
748 ),
749 )
750 }
751 Expr::If(expr) => match &expr.condition {
752 crate::Condition::BoolExpr(id) => {
753 let condition = self.evaluate(*id);
754 let ConstValue::Enum(variant, _) = condition else {
755 return ConstValue::Missing(skip_diagnostic());
756 };
757 if variant == true_variant(self.db) {
758 self.evaluate(expr.if_block)
759 } else if let Some(else_block) = expr.else_block {
760 self.evaluate(else_block)
761 } else {
762 self.unit_const.clone()
763 }
764 }
765 crate::Condition::Let(id, patterns) => {
766 let value = self.evaluate(*id);
767 let ConstValue::Enum(variant, value) = value else {
768 return ConstValue::Missing(skip_diagnostic());
769 };
770 for pattern_id in patterns {
771 let Pattern::EnumVariant(pattern) = &self.arenas.patterns[*pattern_id]
772 else {
773 continue;
774 };
775 if pattern.variant != variant {
776 continue;
777 }
778 if let Some(inner_pattern) = pattern.inner_pattern {
779 self.destructure_pattern(inner_pattern, *value);
780 }
781 return self.evaluate(expr.if_block);
782 }
783 if let Some(else_block) = expr.else_block {
784 self.evaluate(else_block)
785 } else {
786 self.unit_const.clone()
787 }
788 }
789 },
790 _ => ConstValue::Missing(skip_diagnostic()),
791 }
792 }
793
794 fn evaluate_function_call(&mut self, expr: &ExprFunctionCall) -> ConstValue {
796 let db = self.db;
797 let args = expr
798 .args
799 .iter()
800 .filter_map(|arg| try_extract_matches!(arg, ExprFunctionCallArg::Value))
801 .map(|arg| self.evaluate(*arg))
802 .collect_vec();
803 if expr.function == self.panic_with_felt252 {
804 return ConstValue::Missing(self.diagnostics.report(
805 expr.stable_ptr.untyped(),
806 SemanticDiagnosticKind::FailedConstantCalculation,
807 ));
808 }
809 let concrete_function =
810 match self.generic_substitution.substitute(db, expr.function.get_concrete(db)) {
811 Ok(v) => v,
812 Err(err) => return ConstValue::Missing(err),
813 };
814 if let Some(calc_result) =
815 self.evaluate_const_function_call(&concrete_function, &args, expr)
816 {
817 return calc_result;
818 }
819
820 let imp = extract_matches!(concrete_function.generic_function, GenericFunctionId::Impl);
821 let bool_value = |condition: bool| {
822 if condition { self.true_const.clone() } else { self.false_const.clone() }
823 };
824
825 if imp.function == self.eq_fn {
826 return bool_value(args[0] == args[1]);
827 } else if imp.function == self.ne_fn {
828 return bool_value(args[0] != args[1]);
829 } else if imp.function == self.not_fn {
830 return bool_value(args[0] == self.false_const);
831 }
832
833 let args = match args
834 .into_iter()
835 .map(|arg| NumericArg::try_new(db, arg))
836 .collect::<Option<Vec<_>>>()
837 {
838 Some(args) => args,
839 None => return ConstValue::Missing(skip_diagnostic()),
842 };
843 let mut value = match imp.function {
844 id if id == self.neg_fn => -&args[0].v,
845 id if id == self.add_fn => &args[0].v + &args[1].v,
846 id if id == self.sub_fn => &args[0].v - &args[1].v,
847 id if id == self.mul_fn => &args[0].v * &args[1].v,
848 id if (id == self.div_fn || id == self.rem_fn) && args[1].v.is_zero() => {
849 return ConstValue::Missing(
850 self.diagnostics
851 .report(expr.stable_ptr.untyped(), SemanticDiagnosticKind::DivisionByZero),
852 );
853 }
854 id if id == self.div_fn => &args[0].v / &args[1].v,
855 id if id == self.rem_fn => &args[0].v % &args[1].v,
856 id if id == self.bitand_fn => &args[0].v & &args[1].v,
857 id if id == self.bitor_fn => &args[0].v | &args[1].v,
858 id if id == self.bitxor_fn => &args[0].v ^ &args[1].v,
859 id if id == self.lt_fn => return bool_value(args[0].v < args[1].v),
860 id if id == self.le_fn => return bool_value(args[0].v <= args[1].v),
861 id if id == self.gt_fn => return bool_value(args[0].v > args[1].v),
862 id if id == self.ge_fn => return bool_value(args[0].v >= args[1].v),
863 id if id == self.div_rem_fn => {
864 return ConstValue::Struct(
867 vec![
868 value_as_const_value(db, args[0].ty, &(&args[0].v / &args[1].v)).unwrap(),
869 value_as_const_value(db, args[0].ty, &(&args[0].v % &args[1].v)).unwrap(),
870 ],
871 expr.ty,
872 );
873 }
874 _ => {
875 unreachable!("Unexpected function call in constant lowering: {:?}", expr)
876 }
877 };
878 if expr.ty == db.core_info().felt252 {
879 value %= BigInt::from_str_radix(
881 "800000000000011000000000000000000000000000000000000000000000001",
882 16,
883 )
884 .unwrap();
885 }
886 value_as_const_value(db, expr.ty, &value)
887 .map_err(|err| {
888 self.diagnostics
889 .report(expr.stable_ptr.untyped(), SemanticDiagnosticKind::LiteralError(err))
890 })
891 .unwrap_or_else(ConstValue::Missing)
892 }
893
894 fn evaluate_const_function_call(
896 &mut self,
897 concrete_function: &ConcreteFunction,
898 args: &[ConstValue],
899 expr: &ExprFunctionCall,
900 ) -> Option<ConstValue> {
901 let db = self.db;
902 if let GenericFunctionId::Extern(extern_fn) = concrete_function.generic_function {
903 let expr_ty = self.generic_substitution.substitute(db, expr.ty).ok()?;
904 if self.upcast_fns.contains(&extern_fn) {
905 let [ConstValue::Int(value, _)] = args else { return None };
906 return Some(ConstValue::Int(value.clone(), expr_ty));
907 } else if self.downcast_fns.contains(&extern_fn) {
908 let [ConstValue::Int(value, _)] = args else { return None };
909 let TypeLongId::Concrete(ConcreteTypeId::Enum(enm)) = expr_ty.lookup_intern(db)
910 else {
911 return None;
912 };
913 let (some, none) =
914 db.concrete_enum_variants(enm).ok()?.into_iter().collect_tuple()?;
915 let success_ty = some.ty;
916 return Some(match validate_literal(db, success_ty, value) {
917 Ok(()) => {
918 ConstValue::Enum(some, ConstValue::Int(value.clone(), success_ty).into())
919 }
920 Err(LiteralError::OutOfRange(_)) => {
921 ConstValue::Enum(none, self.unit_const.clone().into())
922 }
923 Err(LiteralError::InvalidTypeForLiteral(_)) => unreachable!(
924 "`downcast` is only allowed into types that can be literals. Got `{}`.",
925 success_ty.format(db)
926 ),
927 });
928 } else {
929 unreachable!(
930 "Unexpected extern function in constant lowering: `{}`",
931 extern_fn.full_path(db.upcast())
932 );
933 }
934 }
935 let body_id = concrete_function.body(db).ok()??;
936 let concrete_body_id = body_id.function_with_body_id(db);
937 let signature = db.function_with_body_signature(concrete_body_id).ok()?;
938 require(signature.is_const)?;
939 let generic_substitution = body_id.substitution(db).ok()?;
940 let body = db.function_body(concrete_body_id).ok()?;
941 const MAX_CONST_EVAL_DEPTH: usize = 100;
942 if self.depth > MAX_CONST_EVAL_DEPTH {
943 return Some(ConstValue::Missing(self.diagnostics.report(
944 expr.stable_ptr,
945 SemanticDiagnosticKind::ConstantCalculationDepthExceeded,
946 )));
947 }
948 let mut diagnostics = SemanticDiagnostics::default();
949 let mut inner = ConstantEvaluateContext {
950 db,
951 info: self.info,
952 arenas: &body.arenas,
953 vars: signature
954 .params
955 .into_iter()
956 .map(|p| VarId::Param(p.id))
957 .zip(args.iter().cloned())
958 .collect(),
959 generic_substitution,
960 depth: self.depth + 1,
961 diagnostics: &mut diagnostics,
962 };
963 let value = inner.evaluate(body.body_expr);
964 for diagnostic in diagnostics.build().get_all() {
965 let location = diagnostic.location(db.elongate());
966 let (inner_diag, mut notes) = match diagnostic.kind {
967 SemanticDiagnosticKind::ConstantCalculationDepthExceeded => {
968 self.diagnostics.report(
969 expr.stable_ptr,
970 SemanticDiagnosticKind::ConstantCalculationDepthExceeded,
971 );
972 continue;
973 }
974 SemanticDiagnosticKind::InnerFailedConstantCalculation(inner_diag, notes) => {
975 (inner_diag, notes)
976 }
977 _ => (diagnostic.into(), vec![]),
978 };
979 notes.push(DiagnosticNote::with_location(
980 format!("In `{}`", concrete_function.full_path(db)),
981 location,
982 ));
983 self.diagnostics.report(
984 expr.stable_ptr,
985 SemanticDiagnosticKind::InnerFailedConstantCalculation(inner_diag, notes),
986 );
987 }
988 Some(value)
989 }
990
991 fn evaluate_member_access(&mut self, expr: &ExprMemberAccess) -> Maybe<ConstValue> {
993 let full_struct = self.evaluate(expr.expr);
994 let ConstValue::Struct(mut values, _) = full_struct else {
995 return Err(skip_diagnostic());
997 };
998 let members = self.db.concrete_struct_members(expr.concrete_struct_id)?;
999 let Some(member_idx) = members.iter().position(|(_, member)| member.id == expr.member)
1000 else {
1001 return Err(skip_diagnostic());
1003 };
1004 Ok(values.swap_remove(member_idx))
1005 }
1006
1007 fn destructure_pattern(&mut self, pattern_id: PatternId, value: ConstValue) {
1009 let pattern = &self.arenas.patterns[pattern_id];
1010 match pattern {
1011 Pattern::Literal(_)
1012 | Pattern::StringLiteral(_)
1013 | Pattern::Otherwise(_)
1014 | Pattern::Missing(_) => {}
1015 Pattern::Variable(pattern) => {
1016 self.vars.insert(VarId::Local(pattern.var.id), value);
1017 }
1018 Pattern::Struct(pattern) => {
1019 if let ConstValue::Struct(inner_values, _) = value {
1020 let member_order =
1021 match self.db.concrete_struct_members(pattern.concrete_struct_id) {
1022 Ok(member_order) => member_order,
1023 Err(_) => return,
1024 };
1025 for (member, inner_value) in zip(member_order.values(), inner_values) {
1026 if let Some((_, inner_pattern)) =
1027 pattern.field_patterns.iter().find(|(field, _)| member.id == field.id)
1028 {
1029 self.destructure_pattern(*inner_pattern, inner_value);
1030 }
1031 }
1032 }
1033 }
1034 Pattern::Tuple(pattern) => {
1035 if let ConstValue::Struct(inner_values, _) = value {
1036 for (inner_pattern, inner_value) in zip(&pattern.field_patterns, inner_values) {
1037 self.destructure_pattern(*inner_pattern, inner_value);
1038 }
1039 }
1040 }
1041 Pattern::FixedSizeArray(pattern) => {
1042 if let ConstValue::Struct(inner_values, _) = value {
1043 for (inner_pattern, inner_value) in
1044 zip(&pattern.elements_patterns, inner_values)
1045 {
1046 self.destructure_pattern(*inner_pattern, inner_value);
1047 }
1048 }
1049 }
1050 Pattern::EnumVariant(pattern) => {
1051 if let ConstValue::Enum(variant, inner_value) = value {
1052 if pattern.variant == variant {
1053 if let Some(inner_pattern) = pattern.inner_pattern {
1054 self.destructure_pattern(inner_pattern, *inner_value);
1055 }
1056 }
1057 }
1058 }
1059 }
1060 }
1061}
1062
1063impl std::ops::Deref for ConstantEvaluateContext<'_> {
1064 type Target = ConstCalcInfo;
1065 fn deref(&self) -> &Self::Target {
1066 self.info
1067 }
1068}
1069
1070struct NumericArg {
1072 v: BigInt,
1074 ty: TypeId,
1076}
1077impl NumericArg {
1078 fn try_new(db: &dyn SemanticGroup, arg: ConstValue) -> Option<Self> {
1079 Some(Self { ty: arg.ty(db).ok()?, v: numeric_arg_value(arg)? })
1080 }
1081}
1082
1083fn numeric_arg_value(value: ConstValue) -> Option<BigInt> {
1086 match value {
1087 ConstValue::Int(value, _) => Some(value),
1088 ConstValue::Struct(v, _) => {
1089 if let [ConstValue::Int(low, _), ConstValue::Int(high, _)] = &v[..] {
1090 Some(low + (high << 128))
1091 } else {
1092 None
1093 }
1094 }
1095 ConstValue::NonZero(const_value) => numeric_arg_value(*const_value),
1096 _ => None,
1097 }
1098}
1099
1100pub fn constant_semantic_diagnostics(
1102 db: &dyn SemanticGroup,
1103 const_id: ConstantId,
1104) -> Diagnostics<SemanticDiagnostic> {
1105 db.priv_constant_semantic_data(const_id, false).map(|data| data.diagnostics).unwrap_or_default()
1106}
1107
1108pub fn constant_semantic_data(db: &dyn SemanticGroup, const_id: ConstantId) -> Maybe<Constant> {
1110 db.priv_constant_semantic_data(const_id, false)?.constant
1111}
1112
1113pub fn constant_semantic_data_cycle(
1115 db: &dyn SemanticGroup,
1116 _cycle: &salsa::Cycle,
1117 const_id: &ConstantId,
1118) -> Maybe<Constant> {
1119 db.priv_constant_semantic_data(*const_id, true)?.constant
1121}
1122
1123pub fn constant_resolver_data(
1125 db: &dyn SemanticGroup,
1126 const_id: ConstantId,
1127) -> Maybe<Arc<ResolverData>> {
1128 Ok(db.priv_constant_semantic_data(const_id, false)?.resolver_data)
1129}
1130
1131pub fn constant_resolver_data_cycle(
1133 db: &dyn SemanticGroup,
1134 _cycle: &salsa::Cycle,
1135 const_id: &ConstantId,
1136) -> Maybe<Arc<ResolverData>> {
1137 Ok(db.priv_constant_semantic_data(*const_id, true)?.resolver_data)
1138}
1139
1140pub fn constant_const_value(db: &dyn SemanticGroup, const_id: ConstantId) -> Maybe<ConstValueId> {
1142 Ok(db.priv_constant_semantic_data(const_id, false)?.const_value)
1143}
1144
1145pub fn constant_const_value_cycle(
1147 db: &dyn SemanticGroup,
1148 _cycle: &salsa::Cycle,
1149 const_id: &ConstantId,
1150) -> Maybe<ConstValueId> {
1151 Ok(db.priv_constant_semantic_data(*const_id, true)?.const_value)
1153}
1154
1155pub fn constant_const_type(db: &dyn SemanticGroup, const_id: ConstantId) -> Maybe<TypeId> {
1157 db.priv_constant_semantic_data(const_id, false)?.const_value.ty(db)
1158}
1159
1160pub fn constant_const_type_cycle(
1162 db: &dyn SemanticGroup,
1163 _cycle: &salsa::Cycle,
1164 const_id: &ConstantId,
1165) -> Maybe<TypeId> {
1166 db.priv_constant_semantic_data(*const_id, true)?.const_value.ty(db)
1168}
1169
1170pub fn const_calc_info(db: &dyn SemanticGroup) -> Arc<ConstCalcInfo> {
1172 Arc::new(ConstCalcInfo::new(db))
1173}
1174
1175#[derive(Debug, PartialEq, Eq)]
1177pub struct ConstCalcInfo {
1178 const_traits: UnorderedHashSet<TraitId>,
1180 unit_const: ConstValue,
1182 true_const: ConstValue,
1184 false_const: ConstValue,
1186 panic_with_felt252: FunctionId,
1188 upcast_fns: UnorderedHashSet<ExternFunctionId>,
1190 downcast_fns: UnorderedHashSet<ExternFunctionId>,
1192
1193 core_info: Arc<CoreInfo>,
1194}
1195
1196impl std::ops::Deref for ConstCalcInfo {
1197 type Target = CoreInfo;
1198 fn deref(&self) -> &CoreInfo {
1199 &self.core_info
1200 }
1201}
1202
1203impl ConstCalcInfo {
1204 fn new(db: &dyn SemanticGroup) -> Self {
1206 let core_info = db.core_info();
1207 let unit_const = ConstValue::Struct(vec![], unit_ty(db));
1208 let core = ModuleHelper::core(db);
1209 let integer = core.submodule("integer");
1210 let starknet = core.submodule("starknet");
1211 let class_hash_module = starknet.submodule("class_hash");
1212 let contract_address_module = starknet.submodule("contract_address");
1213 Self {
1214 const_traits: FromIterator::from_iter([
1215 core_info.neg_trt,
1216 core_info.add_trt,
1217 core_info.sub_trt,
1218 core_info.mul_trt,
1219 core_info.div_trt,
1220 core_info.rem_trt,
1221 core_info.div_rem_trt,
1222 core_info.bitand_trt,
1223 core_info.bitor_trt,
1224 core_info.bitxor_trt,
1225 core_info.partialeq_trt,
1226 core_info.partialord_trt,
1227 core_info.not_trt,
1228 ]),
1229 true_const: ConstValue::Enum(true_variant(db), unit_const.clone().into()),
1230 false_const: ConstValue::Enum(false_variant(db), unit_const.clone().into()),
1231 unit_const,
1232 panic_with_felt252: core.function_id("panic_with_felt252", vec![]),
1233 upcast_fns: FromIterator::from_iter([
1234 integer.extern_function_id("upcast"),
1235 integer.extern_function_id("u8_to_felt252"),
1236 integer.extern_function_id("u16_to_felt252"),
1237 integer.extern_function_id("u32_to_felt252"),
1238 integer.extern_function_id("u64_to_felt252"),
1239 integer.extern_function_id("u128_to_felt252"),
1240 integer.extern_function_id("i8_to_felt252"),
1241 integer.extern_function_id("i16_to_felt252"),
1242 integer.extern_function_id("i32_to_felt252"),
1243 integer.extern_function_id("i64_to_felt252"),
1244 integer.extern_function_id("i128_to_felt252"),
1245 class_hash_module.extern_function_id("class_hash_to_felt252"),
1246 contract_address_module.extern_function_id("contract_address_to_felt252"),
1247 ]),
1248 downcast_fns: FromIterator::from_iter([
1249 integer.extern_function_id("downcast"),
1250 integer.extern_function_id("u8_try_from_felt252"),
1251 integer.extern_function_id("u16_try_from_felt252"),
1252 integer.extern_function_id("u32_try_from_felt252"),
1253 integer.extern_function_id("u64_try_from_felt252"),
1254 integer.extern_function_id("i8_try_from_felt252"),
1255 integer.extern_function_id("i16_try_from_felt252"),
1256 integer.extern_function_id("i32_try_from_felt252"),
1257 integer.extern_function_id("i64_try_from_felt252"),
1258 integer.extern_function_id("i128_try_from_felt252"),
1259 class_hash_module.extern_function_id("class_hash_try_from_felt252"),
1260 contract_address_module.extern_function_id("contract_address_try_from_felt252"),
1261 ]),
1262 core_info,
1263 }
1264 }
1265}