Sitelet https://github.com/starkware-libs/cairo/pull/10037
Skip to content

fix(semantic): const_values_eq must canonicalize felt252 inside compounds - #10037

Merged
orizi merged 1 commit into
mainfrom
orizi/06-04-fix_semantic_const_values_eq_must_canonicalize_felt252_inside_compounds
Jun 7, 2026
Merged

orizi merged 1 commit into
mainfrom
orizi/06-04-fix_semantic_const_values_eq_must_canonicalize_felt252_inside_compounds

Conversation

@orizi

@orizi orizi commented Jun 4, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

const_values_eq previously only handled the case where both sides were ConstValue::Int, returning false for any other variant pair. This meant that felt252 values nested inside structs, enums, or NonZero wrappers were compared by identity rather than by field-element value, causing -1 and PRIME - 1 to be treated as unequal even though they represent the same field element.

The function now recurses into ConstValue::Struct, ConstValue::Enum, and ConstValue::NonZero variants, applying field-element canonicalization at every level where a felt252 integer may appear.


Type of change

Please check one:

  • Bug fix (fixes incorrect behavior)
  • New feature
  • Performance improvement
  • Documentation change with concrete technical impact
  • Style, wording, formatting, or typo-only change

Why is this change needed?

The PartialEq operator for felt252 must agree with the field, meaning -1 and 0x800000000000011000000000000000000000000000000000000000000000000 (i.e., PRIME - 1) are the same value. The previous implementation only canonicalized top-level felt252 integers, so equality checks on NonZero<felt252>, tuples containing felt252, or enums wrapping felt252 would incorrectly return false when comparing these two representations.


What was the behavior or documentation before?

Constant expressions such as:

const VALID_EQ_NONZERO_FELT_CROSS_FORM: () = assert(NZ_FELT_NEG_ONE == NZ_FELT_FIELD_NEG_ONE);
const VALID_EQ_TUPLE_FELT_CROSS_FORM: () = assert((-1_felt252, 5) == (0x800...000, 5));
const VALID_EQ_ENUM_FELT_CROSS_FORM: () = assert(Option::Some(-1) == Option::Some(0x800...000));

would fail constant evaluation because the structural wrappers were compared by identity rather than recursively by field value.


What is the behavior or documentation after?

The above constant expressions now evaluate correctly. const_values_eq recurses into structs, enums, and NonZero wrappers, canonicalizing felt252 integers at every nesting level before comparing.


Related issue or discussion (if any)

N/A


Additional context

Test cases for all three new cross-form equality scenarios (NonZero<felt252>, tuple, and enum) have been added to the constant expression test data.

…unds

  const_values_eq only canonicalized cross-representation felt252 for the
  top-level Int case; NonZero/Struct/Enum fell through to `false`. Since the
  PartialEq const-fold shortcut keys on the generic eq trait fn, this mis-folded
  `==`/`!=` on NonZero<felt252>, tuples and Option<felt252> whenever the operands
  were field-equal but stored in different forms (e.g. -1 vs PRIME-1). Recurse
  structurally into Struct/Enum/NonZero. Add regression tests for all three.

  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@reviewable-StarkWare

Copy link
Copy Markdown

This change is Reviewable

orizi commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@orizi
orizi marked this pull request as ready for review June 4, 2026 11:02
@cursor

cursor Bot commented Jun 4, 2026 •

Copy link
Copy Markdown

PR Summary

Low Risk
Localized change to constant-evaluation equality with added tests; no runtime auth or I/O paths.

Overview
Constant evaluation now treats == on compound constants the same as on bare felt252: nested integers are compared after field canonicalization, not only when both sides are top-level ConstValue::Int.

const_values_eq in constant.rs recurses into Struct, Enum, and NonZero pairs (matching type/variant and comparing members or payloads). That fixes compile-time asserts where -1_felt252 and the equivalent PRIME - 1 literal were stored as different ConstValueIds inside tuples, Option::Some, or NonZero<felt252>.

Regression coverage adds three cross-form equality constants in the constant expression test data.

Reviewed by Cursor Bugbot for commit 1304d8d. Bugbot is set up for automated code reviews on this repo. Configure here.

@eytan-starkware eytan-starkware left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eytan-starkware reviewed 2 files and all commit messages, and made 1 comment.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on orizi and TomerStarkware).


crates/cairo-lang-semantic/src/items/constant.rs line 1252 at r1 (raw file):

                self.const_values_eq(*a_value, *b_value)
            }
            _ => false,

For generics we separate -1 and Prime-1 into two different types, correct?

@orizi orizi left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@orizi made 1 comment.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on eytan-starkware and TomerStarkware).


crates/cairo-lang-semantic/src/items/constant.rs line 1252 at r1 (raw file):

Previously, eytan-starkware wrote…

For generics we separate -1 and Prime-1 into two different types, correct?

yes - for the time being.

@eytan-starkware eytan-starkware left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

@eytan-starkware made 1 comment and resolved 1 discussion.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on TomerStarkware).

@orizi
orizi added this pull request to the merge queue Jun 7, 2026
Merged via the queue into main with commit d27be00 Jun 7, 2026
54 checks passed
@orizi
orizi deleted the orizi/06-04-fix_semantic_const_values_eq_must_canonicalize_felt252_inside_compounds branch June 7, 2026 11:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants