Sitelet https://github.com/starkware-libs/cairo/commit/1850335dda46c7f486037dc4cf75fb538e5a0616
Skip to content

Commit 1850335

Browse files
committed
fix(semantic): validate inferred numeric literal values against their type
A suffix-less numeric literal (`let _x: u8 = 256;`, `match x { 256 => .. }`) got a deferred `NumericLiteral` type var and was never value-validated — only suffixed (`256_u8`) and const literals were. Out-of-range inferred literals were silently accepted at semantic analysis and only caught later at lowering (E3009). Validate in `apply_inference_rewriter`: once a literal's type var is resolved, report `E2008` for an out-of-range value (`LiteralError::OutOfRange` only — an invalid literal type is already reported at conform time). The same `handle_literal_rewrite` path covers both expression and pattern literals, so these now error at semantic analysis instead of lowering. This is now the single validation point for source literals in all contexts, so the redundant per-literal check in const evaluation is dropped (const eval bails on a prior error; computed const values are still validated where they are evaluated). Add a regression test and refresh the lowering `literal` golden (E3009 → E2008).
1 parent 09bdf5e commit 1850335

9 files changed

Lines changed: 133 additions & 125 deletions

File tree

‎crates/cairo-lang-lowering/src/diagnostic.rs‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use cairo_lang_diagnostics::{
44
};
55
use cairo_lang_filesystem::ids::SpanInFile;
66
use cairo_lang_semantic as semantic;
7-
use cairo_lang_semantic::corelib::LiteralError;
87
use cairo_lang_semantic::expr::inference::InferenceError;
98
use cairo_lang_syntax::node::ids::SyntaxStablePtrId;
109
use salsa::Database;
@@ -43,7 +42,7 @@ pub struct LoweringDiagnostic<'db> {
4342
}
4443

4544
impl<'db> DiagnosticEntry<'db> for LoweringDiagnostic<'db> {
46-
fn format(&self, db: &'db dyn Database) -> String {
45+
fn format(&self, _db: &'db dyn Database) -> String {
4746
match &self.kind {
4847
LoweringDiagnosticKind::Unreachable { .. } => "Unreachable code".into(),
4948
LoweringDiagnosticKind::VariableMoved { .. } => "Variable was previously moved.".into(),
@@ -67,7 +66,6 @@ impl<'db> DiagnosticEntry<'db> for LoweringDiagnostic<'db> {
6766
LoweringDiagnosticKind::NoPanicFunctionCycle => {
6867
"Call cycle of `nopanic` functions is not allowed.".into()
6968
}
70-
LoweringDiagnosticKind::LiteralError(literal_error) => literal_error.format(db),
7169
LoweringDiagnosticKind::RefutablePattern => "Refutable pattern in irrefutable \
7270
binding. Refutable patterns are only \
7371
supported in `match`, `if let`, \
@@ -118,7 +116,6 @@ impl<'db> DiagnosticEntry<'db> for LoweringDiagnostic<'db> {
118116
LoweringDiagnosticKind::MemberPathLoop => error_code!(E3006),
119117
LoweringDiagnosticKind::UnexpectedError => error_code!(E3007),
120118
LoweringDiagnosticKind::NoPanicFunctionCycle => error_code!(E3008),
121-
LoweringDiagnosticKind::LiteralError(_) => error_code!(E3009),
122119
LoweringDiagnosticKind::RefutablePattern => error_code!(E3010),
123120
LoweringDiagnosticKind::Unsupported => error_code!(E3011),
124121
LoweringDiagnosticKind::FixedSizeArrayNonCopyableType => error_code!(E3012),
@@ -217,7 +214,6 @@ pub enum LoweringDiagnosticKind<'db> {
217214
CannotInlineFunctionThatMightCallItself,
218215
MemberPathLoop,
219216
NoPanicFunctionCycle,
220-
LiteralError(LiteralError<'db>),
221217
FixedSizeArrayNonCopyableType,
222218
EmptyRepeatedElementFixedSizeArray,
223219
RefutablePattern,

‎crates/cairo-lang-lowering/src/lower/flow_control/create_graph/patterns.rs‎

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use cairo_lang_debug::DebugWithDb;
22
use cairo_lang_defs::ids::NamedLanguageElementId;
3-
use cairo_lang_diagnostics::{DiagnosticNote, Maybe};
3+
use cairo_lang_diagnostics::DiagnosticNote;
44
use cairo_lang_filesystem::flag::FlagsGroup;
5-
use cairo_lang_semantic::corelib::{CorelibSemantic, validate_literal};
5+
use cairo_lang_semantic::corelib::CorelibSemantic;
66
use cairo_lang_semantic::expr::compute::unwrap_pattern_type;
77
use cairo_lang_semantic::items::enm::SemanticEnumEx;
88
use cairo_lang_semantic::items::structure::StructSemantic;
@@ -647,10 +647,10 @@ fn create_node_for_tuple_inner<'db>(
647647
let current_member = struct_members.map(|members| members[item_idx]);
648648

649649
// Collect the patterns on the current item.
650-
let mut patterns_on_current_item = Vec::<Option<semantic::Pattern<'db>>>::default();
651-
let mut live_filter = FilteredPatterns::default();
652-
for (idx, pattern) in patterns.iter().enumerate() {
653-
let item_pattern = match pattern {
650+
let mut patterns_on_current_item =
651+
Vec::<Option<semantic::Pattern<'db>>>::with_capacity(patterns.len());
652+
for pattern in patterns {
653+
patterns_on_current_item.push(match pattern {
654654
Some(semantic::Pattern::Tuple(PatternTuple { field_patterns, .. }))
655655
if current_member.is_none() =>
656656
{
@@ -671,11 +671,7 @@ fn create_node_for_tuple_inner<'db>(
671671
Some(semantic::Pattern::Literal(pattern_literal))
672672
if pattern_literal.literal.ty == ctx.db.core_info().u256 =>
673673
{
674-
match handle_u256_literal(ctx, graph, pattern_literal, item_idx) {
675-
Ok(inner_pattern) => Some(inner_pattern),
676-
// Out-of-range `u256` literal, drop the arm.
677-
Err(_) => continue,
678-
}
674+
Some(handle_u256_literal(ctx, pattern_literal, item_idx))
679675
}
680676
Some(semantic::Pattern::FixedSizeArray(semantic::PatternFixedSizeArray {
681677
elements_patterns,
@@ -698,9 +694,7 @@ fn create_node_for_tuple_inner<'db>(
698694
LoweringDiagnosticKind::UnexpectedError,
699695
);
700696
}
701-
};
702-
live_filter.add(idx);
703-
patterns_on_current_item.push(item_pattern);
697+
});
704698
}
705699

706700
// Create a node to handle the current item. The callback will handle the rest of the tuple.
@@ -711,9 +705,6 @@ fn create_node_for_tuple_inner<'db>(
711705
graph,
712706
patterns: &patterns_ref,
713707
build_node_callback: &mut |graph, pattern_indices, path_head| {
714-
// Lift the survivors from the compacted (dropped-arm) indexing back to this level's
715-
// arm indexing. When no arm was dropped this is the identity.
716-
let pattern_indices = pattern_indices.lift(&live_filter);
717708
// Call `create_node_for_tuple_inner` recursively to handle the rest of the tuple.
718709
create_node_for_tuple_inner(
719710
CreateNodeParams {
@@ -775,18 +766,13 @@ fn add_item_to_path<'db>(
775766
/// pattern.
776767
fn handle_u256_literal<'db>(
777768
ctx: &LoweringContext<'db, '_>,
778-
graph: &mut FlowControlGraphBuilder<'db>,
779769
pattern_literal: &semantic::PatternLiteral<'db>,
780770
item_idx: usize,
781-
) -> Maybe<semantic::Pattern<'db>> {
771+
) -> semantic::Pattern<'db> {
782772
let PatternLiteral {
783-
literal: ExprNumericLiteral { value, ty, stable_ptr: expr_stable_ptr },
773+
literal: ExprNumericLiteral { value, ty: _, stable_ptr: expr_stable_ptr },
784774
stable_ptr,
785775
} = pattern_literal;
786-
if let Err(err) = validate_literal(ctx.db, *ty, value) {
787-
return Err(graph.report(*stable_ptr, LoweringDiagnosticKind::LiteralError(err)));
788-
}
789-
790776
let inner_value = if item_idx == 0 {
791777
value.clone() & ((BigInt::from(1) << 128) - 1)
792778
} else if item_idx == 1 {
@@ -795,14 +781,14 @@ fn handle_u256_literal<'db>(
795781
unreachable!("Unexpected number of members for u256.")
796782
};
797783

798-
Ok(semantic::Pattern::Literal(semantic::PatternLiteral {
784+
semantic::Pattern::Literal(semantic::PatternLiteral {
799785
literal: semantic::ExprNumericLiteral {
800786
value: inner_value,
801787
ty: ctx.db.core_info().u128,
802788
stable_ptr: *expr_stable_ptr,
803789
},
804790
stable_ptr: *stable_ptr,
805-
}))
791+
})
806792
}
807793

808794
/// Creates a node for matching over numeric values, using a combination of [EnumMatch] and
@@ -825,10 +811,6 @@ fn create_node_for_value<'db>(
825811
for (pattern_index, pattern) in patterns.iter().enumerate() {
826812
match pattern {
827813
Some(semantic::Pattern::Literal(semantic::PatternLiteral { literal, .. })) => {
828-
if let Err(err) = validate_literal(ctx.db, var_ty, &literal.value) {
829-
graph.report(literal.stable_ptr, LoweringDiagnosticKind::LiteralError(err));
830-
continue;
831-
}
832814
literals_map
833815
.entry(literal.value.clone())
834816
.or_insert((otherwise_filter.clone(), literal.stable_ptr))

‎crates/cairo-lang-lowering/src/lower/flow_control/graph.rs‎

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -479,15 +479,6 @@ impl<'db> FlowControlGraphBuilder<'db> {
479479
self.graph.var_locations[input_var.0]
480480
}
481481

482-
/// Reports a diagnostic.
483-
pub fn report(
484-
&mut self,
485-
stable_ptr: impl Into<SyntaxStablePtrId<'db>>,
486-
kind: LoweringDiagnosticKind<'db>,
487-
) -> DiagnosticAdded {
488-
self.diagnostics.report(stable_ptr, kind)
489-
}
490-
491482
/// Reports a diagnostic, and returns a new [FlowControlNode::Missing] node.
492483
pub fn report_with_missing_node(
493484
&mut self,

‎crates/cairo-lang-lowering/src/lower/flow_control/test_data/match‎

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,26 +1498,28 @@ fn foo(a: u8) -> bool {
14981498
}
14991499

15001500
//! > graph
1501-
Root: 4
1502-
4 EvaluateExpr { expr: ExprId(0), var_id: v0, next: NodeId(3) }
1503-
3 Upcast { input: v0, output: v1, next: NodeId(2) }
1504-
2 EqualsLiteral { input: v1, literal: 255, true_branch: NodeId(0), false_branch: NodeId(1) }
1501+
Root: 6
1502+
6 EvaluateExpr { expr: ExprId(0), var_id: v0, next: NodeId(5) }
1503+
5 Upcast { input: v0, output: v1, next: NodeId(4) }
1504+
4 EqualsLiteral { input: v1, literal: 255, true_branch: NodeId(0), false_branch: NodeId(3) }
1505+
3 EqualsLiteral { input: v1, literal: 256, true_branch: NodeId(0), false_branch: NodeId(2) }
1506+
2 EqualsLiteral { input: v1, literal: 257, true_branch: NodeId(0), false_branch: NodeId(1) }
15051507
1 ArmExpr { expr: ExprId(4) }
15061508
0 ArmExpr { expr: ExprId(2) }
15071509

15081510
//! > semantic_diagnostics
1509-
1510-
//! > lowering_diagnostics
1511-
error[E3009]: The value does not fit within the range of type core::integer::u8.
1511+
error[E2008]: The value does not fit within the range of type core::integer::u8.
15121512
--> lib.cairo:3:15
15131513
255 | 256 | 257 => false,
15141514
^^^
15151515

1516-
error[E3009]: The value does not fit within the range of type core::integer::u8.
1516+
error[E2008]: The value does not fit within the range of type core::integer::u8.
15171517
--> lib.cairo:3:21
15181518
255 | 256 | 257 => false,
15191519
^^^
15201520

1521+
//! > lowering_diagnostics
1522+
15211523
//! > lowered
15221524

15231525
//! > ==========================================================================
@@ -2032,13 +2034,13 @@ Root: 19
20322034
0 ArmExpr { expr: ExprId(1) }
20332035

20342036
//! > semantic_diagnostics
2035-
2036-
//! > lowering_diagnostics
2037-
error[E3009]: The value does not fit within the range of type core::integer::u256.
2037+
error[E2008]: The value does not fit within the range of type core::integer::u256.
20382038
--> lib.cairo:10:9
20392039
0x10000000000000000000000000000000000000000000000000000000000000000 => 7,
20402040
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20412041

2042+
//! > lowering_diagnostics
2043+
20422044
//! > lowered
20432045

20442046
//! > ==========================================================================
@@ -2058,20 +2060,24 @@ fn foo(x: u256) -> felt252 {
20582060
}
20592061

20602062
//! > graph
2061-
Root: 3
2062-
3 EvaluateExpr { expr: ExprId(0), var_id: v0, next: NodeId(2) }
2063-
2 Deconstruct { input: v0, outputs: [v1, v2], next: NodeId(1) }
2063+
Root: 7
2064+
7 EvaluateExpr { expr: ExprId(0), var_id: v0, next: NodeId(6) }
2065+
6 Deconstruct { input: v0, outputs: [v1, v2], next: NodeId(5) }
2066+
5 Upcast { input: v1, output: v3, next: NodeId(4) }
2067+
4 EqualsLiteral { input: v3, literal: 0, true_branch: NodeId(3), false_branch: NodeId(1) }
2068+
3 Upcast { input: v2, output: v4, next: NodeId(2) }
2069+
2 EqualsLiteral { input: v4, literal: 340282366920938463463374607431768211456, true_branch: NodeId(0), false_branch: NodeId(1) }
20642070
1 ArmExpr { expr: ExprId(2) }
20652071
0 ArmExpr { expr: ExprId(1) }
20662072

20672073
//! > semantic_diagnostics
2068-
2069-
//! > lowering_diagnostics
2070-
error[E3009]: The value does not fit within the range of type core::integer::u256.
2074+
error[E2008]: The value does not fit within the range of type core::integer::u256.
20712075
--> lib.cairo:4:9
20722076
0x10000000000000000000000000000000000000000000000000000000000000000 => 1,
20732077
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20742078

2079+
//! > lowering_diagnostics
2080+
20752081
//! > lowered
20762082

20772083
//! > ==========================================================================
@@ -2090,20 +2096,23 @@ fn foo(x: u256) -> felt252 {
20902096
}
20912097

20922098
//! > graph
2093-
Root: 3
2094-
3 EvaluateExpr { expr: ExprId(0), var_id: v0, next: NodeId(2) }
2095-
2 Deconstruct { input: v0, outputs: [v1, v2], next: NodeId(1) }
2099+
Root: 7
2100+
7 EvaluateExpr { expr: ExprId(0), var_id: v0, next: NodeId(6) }
2101+
6 Deconstruct { input: v0, outputs: [v1, v2], next: NodeId(5) }
2102+
5 Upcast { input: v1, output: v3, next: NodeId(4) }
2103+
4 EqualsLiteral { input: v3, literal: 0, true_branch: NodeId(3), false_branch: NodeId(1) }
2104+
3 Upcast { input: v2, output: v4, next: NodeId(2) }
2105+
2 EqualsLiteral { input: v4, literal: 340282366920938463463374607431768211456, true_branch: NodeId(0), false_branch: NodeId(1) }
20962106
1 Missing
20972107
0 ArmExpr { expr: ExprId(1) }
20982108

20992109
//! > semantic_diagnostics
2100-
2101-
//! > lowering_diagnostics
2102-
error[E3009]: The value does not fit within the range of type core::integer::u256.
2110+
error[E2008]: The value does not fit within the range of type core::integer::u256.
21032111
--> lib.cairo:4:9
21042112
0x10000000000000000000000000000000000000000000000000000000000000000 => 1,
21052113
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21062114

2115+
//! > lowering_diagnostics
21072116
error[E3004]: Match is non-exhaustive: `u256{low: _, high: _}` not covered.
21082117
--> lib.cairo:2:5-5:5
21092118
match x {

‎crates/cairo-lang-lowering/src/lower/mod.rs‎

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use cairo_lang_filesystem::ids::SmolStrId;
66
use cairo_lang_semantic as semantic;
77
use cairo_lang_semantic::corelib::{
88
CorelibSemantic, ErrorPropagationType, bounded_int_ty, get_enum_concrete_variant,
9-
try_get_ty_by_name, unwrap_error_propagation_type, validate_literal,
9+
try_get_ty_by_name, unwrap_error_propagation_type,
1010
};
1111
use cairo_lang_semantic::expr::compute::unwrap_pattern_type;
1212
use cairo_lang_semantic::items::constant::ConstValueId;
@@ -55,7 +55,7 @@ use self::context::{
5555
use self::external::{extern_facade_expr, extern_facade_return_tys};
5656
use self::logical_op::lower_logical_op;
5757
use crate::blocks::Blocks;
58-
use crate::diagnostic::LoweringDiagnosticKind::{self, *};
58+
use crate::diagnostic::LoweringDiagnosticKind::*;
5959
use crate::diagnostic::LoweringDiagnosticsBuilder;
6060
use crate::ids::{
6161
EnrichedSemanticSignature, FunctionLongId, FunctionWithBodyId, FunctionWithBodyLongId,
@@ -960,14 +960,7 @@ fn lower_expr_literal_to_var_usage<'db>(
960960
value: &BigInt,
961961
builder: &mut BlockBuilder<'db>,
962962
) -> VarUsage<'db> {
963-
let value = if let Err(err) = validate_literal(ctx.db, ty, value) {
964-
ConstValue::Missing(
965-
ctx.diagnostics.report(stable_ptr, LoweringDiagnosticKind::LiteralError(err)),
966-
)
967-
.intern(ctx.db)
968-
} else {
969-
ConstValueId::from_int(ctx.db, ty, value)
970-
};
963+
let value = ConstValueId::from_int(ctx.db, ty, value);
971964
let location = ctx.get_location(stable_ptr);
972965
generators::Const { value, ty, location }.add(ctx, &mut builder.statements)
973966
}

0 commit comments

Comments
 (0)