Skip to content

fix(semantic): add salsa cycle recovery to the item generic-params queries - #10272

Merged
orizi merged 1 commit into
mainfrom
claude/salsa-generic-params-cycles
Aug 2, 2026
Merged

fix(semantic): add salsa cycle recovery to the item generic-params queries#10272
orizi merged 1 commit into
mainfrom
claude/salsa-generic-params-cycles

Conversation

@orizi

@orizi orizi commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds cycle_fn and cycle_initial handlers to all *_generic_params_data salsa queries (enum, struct, free_function, extern_function, impl_def, module_type_alias, trait_function). The shared handlers (generic_params_data_cycle, generic_params_data_initial) are defined in generics.rs and return the freshly computed value on cycle recovery rather than freezing at the initial provisional value, allowing salsa to fixpoint-iterate to the correct result. Also fixes module_type_alias_semantic_data_cycle to short-circuit with skip_diagnostic() when in_cycle is true, preventing infinite re-entry.


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?

When an item-level macro call uses a path that names an enum variant (e.g. E::A!(());), the compiler resolves the path through macro_call_module_id / priv_macro_call_data, which reads the enum's variants, which resolves the variant's type. If that type references a generic item with a trait bound (e.g. W<felt252> where W<T, +Drop<T>>), resolving it re-enters the *_generic_params_data query for that item while it is still on the stack. Without cycle handlers, salsa would panic with an ICE (internal compiler error) instead of recovering gracefully.


What was the behavior or documentation before?

The compiler would ICE (panic) when encountering item-level macro calls whose path resolved through an enum variant whose type referenced a generic item with a trait bound, because the resulting query cycle had no registered handler.


What is the behavior or documentation after?

The compiler recovers from the cycle, emits a E2028: Cycle detected while resolving generic param diagnostic (and E2156: Inline macro not found for the unresolvable macro), and continues without panicking. Golden test files are added for enum, struct, free_function, extern_function, impl_def, trait, and type_alias to cover each affected query.


Related issue or discussion (if any)


Additional context

Convergence is bounded: the fixpoint settles within three iterations on the cycle-triggering inputs covered by the golden tests, well within salsa's MAX_ITERATIONS ceiling of 200.

@reviewable-StarkWare

Copy link
Copy Markdown

This change is Reviewable

orizi commented Jul 29, 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 July 29, 2026 10:05
@cursor

cursor Bot commented Jul 29, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches core semantic query cycling for many item kinds; incorrect cycle recovery could hide or mis-report diagnostics, but behavior is bounded by fixpoint iteration and covered by new golden tests.

Overview
Adds Salsa cycle recovery for all *_generic_params_data queries (enums, structs, free/extern functions, impl defs, module type aliases, trait functions) so item-level macro paths that re-enter generic-param resolution no longer ICE.

Shared handlers in generics.rsgeneric_params_data_initial (provisional Err(skip_diagnostic())) and generic_params_data_cycle (return the freshly computed value for fixpoint iteration) — are wired via cycle_fn / cycle_initial on each tracked query. That preserves real diagnostics (e.g. E2028) instead of freezing at the initial provisional result.

Module type alias cycle handling now short-circuits with skip_diagnostic() when in_cycle is true to avoid infinite re-entry through module_type_alias_generic_params_data. If module_type_alias_semantic_data fails during recovery, diagnostics fall back to the generic-params query so cycle-related errors still surface.

Golden tests cover the macro → enum variant → generic item with trait bound pattern across the affected item kinds.

Reviewed by Cursor Bugbot for commit 7a5f5b7. 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.

:lgtm:

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


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

/// silently drop them.
///
/// Convergence is bounded and observed: the fixpoint settles within three iterations on the

Why 3 iterations?
I believe this comment can be less prose and more concise if it had some structure x -> y -> z

orizi commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Re: generics.rs line 295 — "Why 3 iterations? I believe this comment can be less prose and more concise if it had some structure x -> y -> z"

Why 3: iteration i recomputes the cycle head with the re-entrant read seeing iteration i-1's provisional value, and the computed result only depends on whether that read saw the initial Err or a real value. So the sequence is: initial Err -> value computed against that Err -> value computed against a real value (changes once) -> unchanged (fixpoint). Salsa needs the last iteration to observe equality, hence 3. The number was also measured directly via Cycle::iteration() on the golden tests.

Restructured the whole doc comment into arrow chains (both the cycle path and the convergence argument) and pushed it to claude/issue-10272-response-sa2zw4 (9db7950) for cherry-picking into this PR.


Generated by Claude Code

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 9db7950. Configure here.

Comment thread crates/cairo-lang-semantic/src/items/module_type_alias.rs
@orizi
orizi force-pushed the claude/salsa-generic-params-cycles branch from 9db7950 to 9e773a7 Compare July 30, 2026 13:06
Comment thread crates/cairo-lang-semantic/src/items/generics.rs

@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 1 file and all commit messages, made 1 comment, and resolved 1 discussion.
Reviewable status: all files reviewed, 2 unresolved discussions (waiting on orizi and TomerStarkware).

…eries

Follow-up to #10268, which fixed enum_definition_data. Seven sibling queries had
the same gap and each panicked with its own cycle head:

  struct_generic_params_data, enum_generic_params_data,
  free_function_generic_params_data,
  extern_function_declaration_generic_params_data, impl_def_generic_params_data,
  module_type_alias_generic_params_data, priv_trait_function_generic_params_data

The trigger needs no malformed input, contrary to how the original was
described. `E::A!(());` is well-formed item-level macro syntax whose path names
<enum>::<variant>; combined with any generic item carrying a trait bound, the
macro-call expansion resolves the enum, the enum resolves a variant type, and
that re-enters whichever generic-params query is already on the stack. The
re-entered query need not be reachable from priv_macro_call_data at all - it
only has to be on the stack.

One generic handler pair in generics.rs serves all seven, with salsa inferring
the key type, rather than fourteen near-identical functions.

Use the fixpoint shape - cycle_fn returns the computed value, not the
provisional. Measured on these goldens, the provisional shape is worse than just
losing information: five of seven change, it drops E2028 and (for extern fns)
E2165, and for struct/enum it invents a spurious E2006 "Unknown type".
Convergence measured per key via Cycle::iteration(): at most 3 iterations,
against MAX_ITERATIONS 200.

Also guard module_type_alias_semantic_data_cycle. It recovers by re-calling
module_type_alias_semantic_data(id, true), but that body reads
module_type_alias_generic_params_data before branching on in_cycle, so once that
query recovers, the handler re-enters itself and stack-overflows. Without the
guard this change trades an ICE for a worse one. The guard is inert for genuine
alias cycles - it fires only on re-entry of the in_cycle key.

The guard leaves module_type_alias_semantic_data(id, false) as Err in this
scenario, which module_type_alias_semantic_diagnostics mapped to no diagnostics,
dropping the alias's own E2028 (caught by Cursor Bugbot). Its Err path now falls
back to the generic-params diagnostics, which no other query surfaces; the
type_alias golden now shows E2028 like the sibling item kinds.

impl_alias_semantic_data has the same unsafe shape and is latent only because
impl_alias_generic_params_data still lacks recovery; adding it there later must
guard that handler too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01E43saQzYdQ7UC9UyyGx5Sr
@orizi
orizi force-pushed the claude/salsa-generic-params-cycles branch from 9e773a7 to 7a5f5b7 Compare July 30, 2026 14:27

@TomerStarkware TomerStarkware left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

:lgtm:

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

@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: :shipit: complete! all files reviewed, all discussions resolved (waiting on orizi).

@orizi
orizi added this pull request to the merge queue Aug 2, 2026
Merged via the queue into main with commit 7ba59bf Aug 2, 2026
55 checks passed
@orizi
orizi deleted the claude/salsa-generic-params-cycles branch August 2, 2026 12:07
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.

5 participants