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

fix(starknet): handle empty enum in Store derive - #10069

Merged
orizi merged 1 commit into
mainfrom
orizi/06-08-fix_starknet_handle_empty_enum_in_store_derive
Jun 9, 2026
Merged

orizi merged 1 commit into
mainfrom
orizi/06-08-fix_starknet_handle_empty_enum_in_store_derive

Conversation

@orizi

@orizi orizi commented Jun 8, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Fixes malformed Cairo code generated by the #[derive(starknet::Store)] macro for enums. Match arm strings were being joined with ,\n at the join site while also having commas appended individually to each arm, resulting in double commas. The fix moves the trailing comma into each arm's format string and joins with \n only. Additionally, adds support for empty enums (enum Foo {}): the size() method previously emitted a dangling + operator when no variants were present; it now falls back to 0 in that case, matching the struct handler's behavior.


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 Store derive macro for enums was joining match arms with ,\n at the join call while each arm string already lacked a trailing comma, causing the last arm to be missing its comma. Separately, when an enum had zero variants, match_size remained empty and was interpolated directly into 1_u8 + in size(), producing invalid Cairo syntax with a dangling +.


What was the behavior or documentation before?

  • Generated write and write_at_offset match arms were missing trailing commas on the last arm.
  • Deriving starknet::Store on an empty enum caused a compiler error due to 1_u8 + with nothing following the operator in the generated size() implementation.

What is the behavior or documentation after?

  • Each match arm string now carries its own trailing comma, and arms are joined with \n alone, producing well-formed Cairo match expressions.
  • Empty enums derive starknet::Store without a syntax error; size() emits 1_u8 + 0 instead of 1_u8 + .

Related issue or discussion (if any)

N/A


Additional context

Test data snapshots for new_storage_interface and user_defined_types have been updated to reflect the corrected output, including a new EmptyEnum test case and its expected generated Store implementation and diagnostics.

orizi commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator Author

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

@reviewable-StarkWare

Copy link
Copy Markdown

This change is Reviewable

@orizi
orizi marked this pull request as ready for review June 9, 2026 05:59
@cursor

cursor Bot commented Jun 9, 2026 •

Copy link
Copy Markdown

PR Summary

Low Risk
Compiler plugin output fix for macro-generated storage code; behavior change is limited to previously broken empty enums and corrected match syntax for all enum derives.

Overview
Fixes #[derive(starknet::Store)] code generation for enums in handle_enum (store.rs).

Empty enums (enum Foo {}): when there are no variants, the macro now emits a dedicated impl instead of building variant matches. Read / read_at_offset return SyscallResult::Err with 'Read of an uninhabited type'; write / write_at_offset use empty match value {}; size() returns 0 (avoiding invalid 1_u8 + with no payload term).

Non-empty enums: match arms for read/write now carry their own trailing comma, and arms are joined with \n only (not ,\n), so generated match expressions are valid Cairo—including a comma after the last variant arm before the 0 | _ / _ catch-all.

Plugin snapshots (new_storage_interface, user_defined_types) add EmptyEnum and update expected enum Store output (trailing commas on write arms).

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8df03ffe6f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/cairo-lang-starknet/src/plugin/derive/store.rs Outdated
@orizi
orizi force-pushed the orizi/06-08-fix_starknet_handle_empty_enum_in_store_derive branch from 8df03ff to 1c09023 Compare June 9, 2026 07:39

@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 resolved 1 discussion.
Reviewable status: 0 of 3 files reviewed, all discussions resolved (waiting on eytan-starkware and TomerStarkware).

@orizi
orizi enabled auto-merge June 9, 2026 07:39
  `#[derive(starknet::Store)]` on a zero-variant enum generated invalid
  code: the per-variant `match` arms collapsed to a stray leading `,`, and
  `size()` emitted a dangling `1_u8 + ` with no right operand. Both failed
  to parse.

  Carry the arm-terminating comma per arm (so an empty variant list yields
  no comma at all), and fall back to `0` for the accumulated max-size when
  there are no variants, mirroring the struct handler.

  Cover the empty-enum case in the user_defined_types expansion test.

  Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@orizi
orizi force-pushed the orizi/06-08-fix_starknet_handle_empty_enum_in_store_derive branch from 1c09023 to c879cdc Compare June 9, 2026 07:42
@eytan-starkware

Copy link
Copy Markdown
Contributor

The empty-enum handling can be a lot smaller. For a variant-less enum the existing template only breaks in two spots, and the comma fix already covers one of them:

  1. size() emits a dangling 1_u8 + — the exact case the struct handler already guards one function up (if sizes.is_empty() { "0" }). A 3-line fallback after the loop is enough:
    if match_size.is_empty() {
        match_size = "0".to_string();
    }
    → 1_u8 + 0.
  2. match idx { , 0 | _ => … } leading comma — already handled by moving the trailing comma into each arm (the comma fix in this PR).

With just those, the existing template generates valid code for empty enums:

fn read(...)  { let idx = Store::<felt252>::read(...)?; match idx { 0 | _ => Err(...) } }
fn write(..., value) { match value {}; Ok(()) }   // uninhabited → exhaustive
fn size() -> u8 { 1_u8 + 0 }

so the separate ~35-line impl isn't needed. Net is +16/−10 in store.rs instead of +45/−10, verified compiling via the golden test (EmptyEnum gets only the usual "no default variant" warning, no error).

Minor: this reuses the normal read (one extra felt read before erroring) and size is 1 not 0 — both moot for an uninhabited type, and 1 is arguably more consistent since every enum reserves the discriminant byte.

If we'd rather not pretend an uninhabited type is storable, the even-smaller option is to reject it outright with a diagnostic at the top of handle_enum and return None.

@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.

this generates unreachable code - handling to create trivial code is more sensible.

@orizi made 1 comment.
Reviewable status: 0 of 3 files reviewed, all discussions resolved (waiting on eytan-starkware and TomerStarkware).

@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 reviewed 3 files and all commit messages, and made 1 comment.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on TomerStarkware).

@orizi
orizi added this pull request to the merge queue Jun 9, 2026
Merged via the queue into main with commit 21bffc5 Jun 9, 2026
54 checks passed
@orizi
orizi deleted the orizi/06-08-fix_starknet_handle_empty_enum_in_store_derive branch June 9, 2026 12:58
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