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