diff --git a/crates/cairo-lang-semantic/src/expr/compute.rs b/crates/cairo-lang-semantic/src/expr/compute.rs index 5547d35cff8..255cfaf6626 100644 --- a/crates/cairo-lang-semantic/src/expr/compute.rs +++ b/crates/cairo-lang-semantic/src/expr/compute.rs @@ -3610,6 +3610,7 @@ pub fn compute_statement_semantic( &rhs_expr, stmt_item_syntax.stable_ptr().untyped(), explicit_type, + false, ); let name_syntax = const_syntax.name(syntax_db); let name = name_syntax.text(db.upcast()); diff --git a/crates/cairo-lang-semantic/src/expr/test_data/constant b/crates/cairo-lang-semantic/src/expr/test_data/constant index bfe434d1b79..80bb4a42a2e 100644 --- a/crates/cairo-lang-semantic/src/expr/test_data/constant +++ b/crates/cairo-lang-semantic/src/expr/test_data/constant @@ -72,16 +72,6 @@ error: The '?' operator is not supported outside of functions. Option::::Some(0)? ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: A numeric literal of type core::bool cannot be created. - --> lib.cairo:6:42 -const WRONG_TYPE_AND_NOT_LITERAL: bool = 1 + 2; - ^ - -error: A numeric literal of type core::bool cannot be created. - --> lib.cairo:6:46 -const WRONG_TYPE_AND_NOT_LITERAL: bool = 1 + 2; - ^ - error: Trait has no implementation in context: core::traits::Add::. --> lib.cairo:6:42 const WRONG_TYPE_AND_NOT_LITERAL: bool = 1 + 2; @@ -109,11 +99,6 @@ foo const DEFAULT_VAR: bool = 1; //! > expected_diagnostics -error: A numeric literal of type core::bool cannot be created. - --> lib.cairo:1:27 -const DEFAULT_VAR: bool = 1; - ^ - error: Mismatched types. The type `core::bool` cannot be created from a numeric literal. --> lib.cairo:1:27 const DEFAULT_VAR: bool = 1; @@ -136,11 +121,6 @@ foo const B: u8 = -1; //! > expected_diagnostics -error: The value does not fit within the range of type core::integer::u8. - --> lib.cairo:1:15 -const B: u8 = -1; - ^^ - error: Trait has no implementation in context: core::traits::Neg::. --> lib.cairo:1:15 const B: u8 = -1; @@ -231,47 +211,6 @@ const COMPLEX_STRUCT: ComplexStruct = ComplexStruct { const CONST_MEMBER: u8 = COMPLEX_STRUCT.a; -//! > semantic_diagnostics -error: The value does not fit within the range of type core::integer::u8. - --> lib.cairo:1:26 -const UNSIGNED_VAR: u8 = 256; - ^*^ - -error: The value does not fit within the range of type core::integer::i8. - --> lib.cairo:2:24 -const SIGNED_VAR: i8 = 128; - ^*^ - -error: The value does not fit within the range of type core::integer::i8. - --> lib.cairo:5:36 -const OUT_OF_RANGE_NEGATIVE1: i8 = -0x81; - ^***^ - -error: The value does not fit within the range of type core::integer::i16. - --> lib.cairo:6:37 -const OUT_OF_RANGE_NEGATIVE2: i16 = -0x8001; - ^*****^ - -error: The value does not fit within the range of type core::zeroable::NonZero::. - --> lib.cairo:7:36 -const ZERO_NON_ZERO: NonZero = 0; - ^ - -error: The value does not fit within the range of type core::integer::i8. - --> lib.cairo:9:39 -const CALCULATION_WITH_OVERFLOW: i8 = 120 + 10; - ^******^ - -error: This expression is not supported as constant. - --> lib.cairo:10:39 -const CALCULATION_WITH_DIVISION: u8 = 120 / 0; - ^*****^ - -error: The value does not fit within the range of type core::zeroable::NonZero::. - --> lib.cairo:28:8 - d: 0, - ^ - //! > expected_diagnostics error: The value does not fit within the range of type core::integer::u8. --> lib.cairo:1:26 diff --git a/crates/cairo-lang-semantic/src/items/constant.rs b/crates/cairo-lang-semantic/src/items/constant.rs index 8824cff4ff1..3f136fe5b1c 100644 --- a/crates/cairo-lang-semantic/src/items/constant.rs +++ b/crates/cairo-lang-semantic/src/items/constant.rs @@ -312,13 +312,10 @@ pub fn constant_semantic_data_helper( &value, constant_ast.stable_ptr().untyped(), constant_type, + true, ) .intern(db); - // Check fully resolved. - ctx.resolver.inference().finalize(ctx.diagnostics, constant_ast.stable_ptr().untyped()); - ctx.apply_inference_rewriter_to_exprs(); - let const_value = ctx .resolver .inference() @@ -371,22 +368,39 @@ pub fn resolve_const_expr_and_evaluate( value: &ExprAndId, const_stable_ptr: SyntaxStablePtrId, target_type: TypeId, + finalize: bool, ) -> ConstValue { + let prev_err_count = ctx.diagnostics.error_count; let inference = &mut ctx.resolver.inference(); if let Err(err_set) = inference.conform_ty(value.ty(), target_type) { inference.report_on_pending_error(err_set, ctx.diagnostics, const_stable_ptr); } - if let Err(err_set) = inference.solve() { + if finalize { + // Check fully resolved. + inference.finalize(ctx.diagnostics, const_stable_ptr); + } else if let Err(err_set) = inference.solve() { inference.report_on_pending_error(err_set, ctx.diagnostics, const_stable_ptr); } + // TODO(orizi): Consider moving this to be called only upon creating const values, other callees + // don't necessarily need it. ctx.apply_inference_rewriter_to_exprs(); match &value.expr { Expr::Constant(ExprConstant { const_value_id, .. }) => const_value_id.lookup_intern(db), // Check that the expression is a valid constant. - _ => evaluate_constant_expr(db, &ctx.arenas, value.id, ctx.diagnostics), + _ if ctx.diagnostics.error_count > prev_err_count => ConstValue::Missing(skip_diagnostic()), + _ => { + let mut eval_ctx = + ConstantEvaluateContext { db, arenas: &ctx.arenas, diagnostics: ctx.diagnostics }; + eval_ctx.validate(value.id); + if eval_ctx.diagnostics.error_count > prev_err_count { + ConstValue::Missing(skip_diagnostic()) + } else { + eval_ctx.evaluate(value.id) + } + } } } @@ -421,233 +435,285 @@ pub fn value_as_const_value( } } -/// evaluate the given const expression value. -pub fn evaluate_constant_expr( - db: &dyn SemanticGroup, - arenas: &Arenas, - expr_id: ExprId, - diagnostics: &mut SemanticDiagnostics, -) -> ConstValue { - let expr = &arenas.exprs[expr_id]; - - match expr { - Expr::Constant(expr) => expr.const_value_id.lookup_intern(db), - Expr::Block(ExprBlock { statements, tail: Some(inner), .. }) if statements.is_empty() => { - evaluate_constant_expr(db, arenas, *inner, diagnostics) - } - Expr::FunctionCall(expr) => evaluate_const_function_call(db, arenas, expr, diagnostics) - .map(|value| { - value_as_const_value(db, expr.ty, &value) - .map_err(|err| { - diagnostics.report( +/// A context for evaluating constant expressions. +struct ConstantEvaluateContext<'a> { + db: &'a dyn SemanticGroup, + arenas: &'a Arenas, + diagnostics: &'a mut SemanticDiagnostics, +} +impl ConstantEvaluateContext<'_> { + /// Validate the given expression can be used as constant. + fn validate(&mut self, expr_id: ExprId) { + match &self.arenas.exprs[expr_id] { + Expr::Var(_) | Expr::Constant(_) | Expr::Missing(_) => {} + Expr::Block(ExprBlock { statements, tail: Some(inner), .. }) + if statements.is_empty() => + { + self.validate(*inner); + } + Expr::FunctionCall(expr) => { + if let Some(value) = try_extract_minus_literal(self.db, &self.arenas.exprs, expr) { + if let Err(err) = validate_literal(self.db, expr.ty, value) { + self.diagnostics.report( expr.stable_ptr.untyped(), SemanticDiagnosticKind::LiteralError(err), - ) - }) - .unwrap_or_else(ConstValue::Missing) - }) - .unwrap_or_else(ConstValue::Missing), - Expr::Literal(expr) => value_as_const_value(db, expr.ty, &expr.value) - .map_err(|err| { - diagnostics - .report(expr.stable_ptr.untyped(), SemanticDiagnosticKind::LiteralError(err)) - }) - .unwrap_or_else(ConstValue::Missing), - Expr::Tuple(expr) => ConstValue::Struct( - expr.items - .iter() - .map(|expr_id| evaluate_constant_expr(db, arenas, *expr_id, diagnostics)) - .collect(), - expr.ty, - ), - Expr::StructCtor(ExprStructCtor { - members, - base_struct: None, - ty, - concrete_struct_id, - .. - }) => { - let member_order = match db.concrete_struct_members(*concrete_struct_id) { - Ok(member_order) => member_order, - Err(diag_add) => return ConstValue::Missing(diag_add), - }; - ConstValue::Struct( - member_order - .values() - .map(|m| { - members - .iter() - .find(|(member_id, _)| m.id == *member_id) - .map(|(_, expr_id)| { - evaluate_constant_expr(db, arenas, *expr_id, diagnostics) - }) - .unwrap_or_else(|| ConstValue::Missing(skip_diagnostic())) - }) - .collect(), - *ty, - ) - } - Expr::EnumVariantCtor(expr) => ConstValue::Enum( - expr.variant.clone(), - Box::new(evaluate_constant_expr(db, arenas, expr.value_expr, diagnostics)), - ), - Expr::MemberAccess(expr) => extract_const_member_access(db, arenas, expr, diagnostics) - .unwrap_or_else(ConstValue::Missing), - Expr::FixedSizeArray(expr) => ConstValue::Struct( - match &expr.items { - crate::FixedSizeArrayItems::Items(items) => items - .iter() - .map(|expr_id| evaluate_constant_expr(db, arenas, *expr_id, diagnostics)) - .collect(), - crate::FixedSizeArrayItems::ValueAndSize(value, count) => { - let value = evaluate_constant_expr(db, arenas, *value, diagnostics); - let count = count.lookup_intern(db); - if let Some(count) = count.into_int() { - (0..count.to_usize().unwrap()).map(|_| value.clone()).collect() - } else { - diagnostics.report( - expr.stable_ptr.untyped(), - SemanticDiagnosticKind::UnsupportedConstant, ); - vec![] } + return; + } + for arg in &expr.args { + match arg { + ExprFunctionCallArg::Value(arg) => self.validate(*arg), + ExprFunctionCallArg::Reference(var) => { + self.diagnostics.report( + var.stable_ptr(), + SemanticDiagnosticKind::UnsupportedConstant, + ); + } + } + if let ExprFunctionCallArg::Value(arg) = arg { + self.validate(*arg); + } + } + if !self.is_function_const(expr.function) { + self.diagnostics.report( + expr.stable_ptr.untyped(), + SemanticDiagnosticKind::UnsupportedConstant, + ); + } + } + Expr::Literal(expr) => { + if let Err(err) = validate_literal(self.db, expr.ty, expr.value.clone()) { + self.diagnostics.report( + expr.stable_ptr.untyped(), + SemanticDiagnosticKind::LiteralError(err), + ); + } + } + Expr::Tuple(expr) => { + for item in &expr.items { + self.validate(*item); + } + } + Expr::StructCtor(ExprStructCtor { members, base_struct: None, .. }) => { + for (_, expr_id) in members { + self.validate(*expr_id); + } + } + Expr::EnumVariantCtor(expr) => self.validate(expr.value_expr), + Expr::MemberAccess(expr) => self.validate(expr.expr), + Expr::FixedSizeArray(expr) => match &expr.items { + crate::FixedSizeArrayItems::Items(items) => { + for item in items { + self.validate(*item); + } + } + crate::FixedSizeArrayItems::ValueAndSize(value, _) => { + self.validate(*value); } }, - expr.ty, - ), - _ if diagnostics.error_count == 0 => ConstValue::Missing( - diagnostics - .report(expr.stable_ptr().untyped(), SemanticDiagnosticKind::UnsupportedConstant), - ), - _ => ConstValue::Missing(skip_diagnostic()), + other => { + self.diagnostics.report( + other.stable_ptr().untyped(), + SemanticDiagnosticKind::UnsupportedConstant, + ); + } + } } -} -/// Returns true if the given function is allowed to be called in constant context. -fn is_function_const(db: &dyn SemanticGroup, function_id: FunctionId) -> bool { - let concrete_function = function_id.get_concrete(db); - let Ok(Some(body)) = concrete_function.body(db) else { return false }; - let GenericFunctionWithBodyId::Impl(imp) = body.generic_function(db) else { return false }; - let impl_def = imp.concrete_impl_id.impl_def_id(db); - if impl_def.parent_module(db.upcast()).owning_crate(db.upcast()) != db.core_crate() { - return false; + /// Returns true if the given function is allowed to be called in constant context. + fn is_function_const(&self, function_id: FunctionId) -> bool { + let db = self.db; + let concrete_function = function_id.get_concrete(db); + let Ok(Some(body)) = concrete_function.body(db) else { return false }; + let GenericFunctionWithBodyId::Impl(imp) = body.generic_function(db) else { return false }; + let impl_def = imp.concrete_impl_id.impl_def_id(db); + if impl_def.parent_module(db.upcast()).owning_crate(db.upcast()) != db.core_crate() { + return false; + } + let Ok(trait_id) = db.impl_def_trait(impl_def) else { + return false; + }; + let expected_trait_name = match imp.function_body.name(db).as_str() { + "neg" => "Neg", + "add" => "Add", + "sub" => "Sub", + "mul" => "Mul", + "div" => "Div", + "rem" => "Rem", + "bitand" => "BitAnd", + "bitor" => "BitOr", + "bitxor" => "BitXor", + _ => return false, + }; + trait_id == get_core_trait(db, CoreTraitContext::TopLevel, expected_trait_name.into()) } - let Ok(trait_id) = db.impl_def_trait(impl_def) else { - return false; - }; - let expected_trait_name = match imp.function_body.name(db.upcast()).as_str() { - "neg" => "Neg", - "add" => "Add", - "sub" => "Sub", - "mul" => "Mul", - "div" => "Div", - "rem" => "Rem", - "bitand" => "BitAnd", - "bitor" => "BitOr", - "bitxor" => "BitXor", - _ => return false, - }; - trait_id == get_core_trait(db, CoreTraitContext::TopLevel, expected_trait_name.into()) -} -/// Attempts to evaluate constants from a function call. -fn evaluate_const_function_call( - db: &dyn SemanticGroup, - arenas: &Arenas, - expr: &ExprFunctionCall, - diagnostics: &mut SemanticDiagnostics, -) -> Maybe { - if let Some(value) = try_extract_minus_literal(db.upcast(), &arenas.exprs, expr) { - return Ok(value); + /// Evaluate the given const expression value. + fn evaluate(&mut self, expr_id: ExprId) -> ConstValue { + let expr = &self.arenas.exprs[expr_id]; + let db = self.db; + match expr { + Expr::Constant(expr) => expr.const_value_id.lookup_intern(db), + Expr::Block(ExprBlock { statements, tail: Some(inner), .. }) + if statements.is_empty() => + { + self.evaluate(*inner) + } + Expr::FunctionCall(expr) => self.evaluate_function_call(expr), + Expr::Literal(expr) => value_as_const_value(db, expr.ty, &expr.value) + .expect("LiteralError should have been caught on `validate`"), + Expr::Tuple(expr) => ConstValue::Struct( + expr.items.iter().map(|expr_id| self.evaluate(*expr_id)).collect(), + expr.ty, + ), + Expr::StructCtor(ExprStructCtor { + members, + base_struct: None, + ty, + concrete_struct_id, + .. + }) => { + let member_order = match db.concrete_struct_members(*concrete_struct_id) { + Ok(member_order) => member_order, + Err(diag_add) => return ConstValue::Missing(diag_add), + }; + ConstValue::Struct( + member_order + .values() + .map(|m| { + members + .iter() + .find(|(member_id, _)| m.id == *member_id) + .map(|(_, expr_id)| self.evaluate(*expr_id)) + .expect("Should have been caught by semantic validation") + }) + .collect(), + *ty, + ) + } + Expr::EnumVariantCtor(expr) => { + ConstValue::Enum(expr.variant.clone(), Box::new(self.evaluate(expr.value_expr))) + } + Expr::MemberAccess(expr) => { + self.evaluate_member_access(expr).unwrap_or_else(ConstValue::Missing) + } + Expr::FixedSizeArray(expr) => ConstValue::Struct( + match &expr.items { + crate::FixedSizeArrayItems::Items(items) => { + items.iter().map(|expr_id| self.evaluate(*expr_id)).collect() + } + crate::FixedSizeArrayItems::ValueAndSize(value, count) => { + let value = self.evaluate(*value); + let count = count.lookup_intern(db); + if let Some(count) = count.into_int() { + (0..count.to_usize().unwrap()).map(|_| value.clone()).collect() + } else { + self.diagnostics.report( + expr.stable_ptr.untyped(), + SemanticDiagnosticKind::UnsupportedConstant, + ); + vec![] + } + } + }, + expr.ty, + ), + _ => ConstValue::Missing(skip_diagnostic()), + } } - let args = expr - .args - .iter() - .filter_map(|arg| try_extract_matches!(arg, ExprFunctionCallArg::Value)) - .map(|arg| { - match evaluate_constant_expr(db, arenas, *arg, diagnostics) { + + /// Attempts to evaluate constants from a const function call. + fn evaluate_function_call(&mut self, expr: &ExprFunctionCall) -> ConstValue { + let db = self.db; + if let Some(value) = try_extract_minus_literal(db.upcast(), &self.arenas.exprs, expr) { + return value_as_const_value(db, expr.ty, &value) + .expect("LiteralError should have been caught on `validate`"); + } + let args = match expr + .args + .iter() + .filter_map(|arg| try_extract_matches!(arg, ExprFunctionCallArg::Value)) + .map(|arg| match self.evaluate(*arg) { ConstValue::Int(v, _ty) => Ok(v), // Handling u256 constants to enable const evaluation of them. ConstValue::Struct(v, _) => { if let [ConstValue::Int(low, _), ConstValue::Int(high, _)] = &v[..] { Ok(low + (high << 128)) } else { - Err(diagnostics.report( - arenas.exprs[*arg].stable_ptr().untyped(), + Err(self.diagnostics.report( + self.arenas.exprs[*arg].stable_ptr().untyped(), SemanticDiagnosticKind::UnsupportedConstant, )) } } ConstValue::Missing(err) => Err(err), - _ => Err(diagnostics.report( - arenas.exprs[*arg].stable_ptr().untyped(), - SemanticDiagnosticKind::UnsupportedConstant, - )), + // Dignostic can be skipped as we would either have a semantic error for a bad arg + // for the function, or the arg itself could'nt have been calculated. + _ => Err(skip_diagnostic()), + }) + .collect_vec() + .into_iter() + .collect::, _>>() + { + Ok(args) => args, + Err(err) => return ConstValue::Missing(err), + }; + + let imp = extract_matches!( + expr.function.get_concrete(db.upcast()).generic_function, + GenericFunctionId::Impl + ); + let is_felt252_ty = expr.ty == db.core_felt252_ty(); + let mut value = match imp.function.name(db.upcast()).as_str() { + "neg" => -&args[0], + "add" => &args[0] + &args[1], + "sub" => &args[0] - &args[1], + "mul" => &args[0] * &args[1], + "div" | "rem" if args[1].is_zero() => { + return ConstValue::Missing( + self.diagnostics + .report(expr.stable_ptr.untyped(), SemanticDiagnosticKind::DivisionByZero), + ); } - }) - .collect_vec() - .into_iter() - .collect::, _>>()?; - - if !is_function_const(db, expr.function) { - return Err(diagnostics - .report(expr.stable_ptr.untyped(), SemanticDiagnosticKind::UnsupportedConstant)); - } - - let imp = extract_matches!( - expr.function.get_concrete(db.upcast()).generic_function, - GenericFunctionId::Impl - ); - let is_felt252_ty = expr.ty == db.core_felt252_ty(); - let mut value = match imp.function.name(db.upcast()).as_str() { - "neg" => -&args[0], - "add" => &args[0] + &args[1], - "sub" => &args[0] - &args[1], - "mul" => &args[0] * &args[1], - "div" | "rem" if args[1].is_zero() => { - return Err(diagnostics - .report(expr.stable_ptr.untyped(), SemanticDiagnosticKind::DivisionByZero)); + "div" if !is_felt252_ty => &args[0] / &args[1], + "rem" if !is_felt252_ty => &args[0] % &args[1], + "bitand" if !is_felt252_ty => &args[0] & &args[1], + "bitor" if !is_felt252_ty => &args[0] | &args[1], + "bitxor" if !is_felt252_ty => &args[0] ^ &args[1], + _ => unreachable!("Unexpected function call in constant lowering: {:?}", expr), + }; + if is_felt252_ty { + // Specifically handling felt252s since their evaluation is more complex. + value %= BigInt::from_str_radix( + "800000000000011000000000000000000000000000000000000000000000001", + 16, + ) + .unwrap(); } - "div" if !is_felt252_ty => &args[0] / &args[1], - "rem" if !is_felt252_ty => &args[0] % &args[1], - "bitand" if !is_felt252_ty => &args[0] & &args[1], - "bitor" if !is_felt252_ty => &args[0] | &args[1], - "bitxor" if !is_felt252_ty => &args[0] ^ &args[1], - _ => unreachable!("Unexpected function call in constant lowering: {:?}", expr), - }; - if is_felt252_ty { - // Specifically handling felt252s since their evaluation is more complex. - value %= BigInt::from_str_radix( - "800000000000011000000000000000000000000000000000000000000000001", - 16, - ) - .unwrap(); + value_as_const_value(db, expr.ty, &value) + .map_err(|err| { + self.diagnostics + .report(expr.stable_ptr.untyped(), SemanticDiagnosticKind::LiteralError(err)) + }) + .unwrap_or_else(ConstValue::Missing) } - Ok(value) -} -/// Extract const member access from a const value. -fn extract_const_member_access( - db: &dyn SemanticGroup, - arenas: &Arenas, - expr: &ExprMemberAccess, - diagnostics: &mut SemanticDiagnostics, -) -> Maybe { - let full_struct = evaluate_constant_expr(db, arenas, expr.expr, diagnostics); - let ConstValue::Struct(mut values, _) = full_struct else { - return Err(diagnostics.report( - arenas.exprs[expr.expr].stable_ptr().untyped(), - SemanticDiagnosticKind::UnsupportedConstant, - )); - }; - let members = db.concrete_struct_members(expr.concrete_struct_id)?; - let Some(member_idx) = members.iter().position(|(_, member)| member.id == expr.member) else { - return Err(diagnostics.report( - arenas.exprs[expr.expr].stable_ptr().untyped(), - SemanticDiagnosticKind::UnsupportedConstant, - )); - }; - Ok(values.swap_remove(member_idx)) + /// Extract const member access from a const value. + fn evaluate_member_access(&mut self, expr: &ExprMemberAccess) -> Maybe { + let full_struct = self.evaluate(expr.expr); + let ConstValue::Struct(mut values, _) = full_struct else { + // A semantic diagnostic should have been reported. + return Err(skip_diagnostic()); + }; + let members = self.db.concrete_struct_members(expr.concrete_struct_id)?; + let Some(member_idx) = members.iter().position(|(_, member)| member.id == expr.member) + else { + // A semantic diagnostic should have been reported. + return Err(skip_diagnostic()); + }; + Ok(values.swap_remove(member_idx)) + } } /// Query implementation of [SemanticGroup::constant_semantic_diagnostics]. diff --git a/crates/cairo-lang-semantic/src/resolve/mod.rs b/crates/cairo-lang-semantic/src/resolve/mod.rs index a2bf4e023f9..98f9d19bd40 100644 --- a/crates/cairo-lang-semantic/src/resolve/mod.rs +++ b/crates/cairo-lang-semantic/src/resolve/mod.rs @@ -1608,6 +1608,7 @@ impl<'db> Resolver<'db> { &value, generic_arg_syntax.stable_ptr().untyped(), const_param.ty, + false, ); // Update `self` data with const_eval_resolver's data. diff --git a/crates/cairo-lang-semantic/src/types.rs b/crates/cairo-lang-semantic/src/types.rs index 789c1f87ea4..f24ae43d2c0 100644 --- a/crates/cairo-lang-semantic/src/types.rs +++ b/crates/cairo-lang-semantic/src/types.rs @@ -655,6 +655,7 @@ pub fn extract_fixed_size_array_size( &size, size_expr_syntax.stable_ptr().untyped(), get_usize_ty(db), + false, ); if matches!(const_value, ConstValue::Int(_, _) | ConstValue::Generic(_)) { Ok(Some(const_value.intern(db)))