Skip to content

(bug fix): prune usage after change promotion in loop usage finalization#10201

Merged
eytan-starkware merged 1 commit into
mainfrom
eytan_graphite/loop-usage-snap-fix
Jul 14, 2026
Merged

(bug fix): prune usage after change promotion in loop usage finalization#10201
eytan-starkware merged 1 commit into
mainfrom
eytan_graphite/loop-usage-snap-fix

Conversation

@eytan-starkware

Copy link
Copy Markdown
Contributor

A loop/closure that reads an ancestor as a snapshot (@a.b) while mutating a
nested field (a.b.c) silently dropped the mutation, returning a stale value
(e.g. 1 instead of 4).

In Usage::finalize_as_scope, when a changed child has an ancestor that is
read as a snapshot, the ancestor is promoted into usage/changes so the loop
reads and writes back the whole ancestor. The usage-pruning pass ran before
the promotion, so the promoted ancestor's descendants survived in usage,
violating the antichain invariant. Downstream the loop signature then listed
both the ancestor and its descendants, and the reconstruction of the returned
ancestor overwrote the mutated child with the stale snapshot input.

Fix: reorder the finalization passes — snap_usage, then changes (promotion),
then usage — so the usage pass prunes descendants subsumed by any promoted
ancestor. Adds an e2e sanity repro.

A loop/closure that reads an ancestor as a snapshot (`@a.b`) while mutating a
nested field (`a.b.c`) silently dropped the mutation, returning a stale value
(e.g. 1 instead of 4).

In `Usage::finalize_as_scope`, when a changed child has an ancestor that is
read as a snapshot, the ancestor is promoted into `usage`/`changes` so the loop
reads and writes back the whole ancestor. The usage-pruning pass ran *before*
the promotion, so the promoted ancestor's descendants survived in `usage`,
violating the antichain invariant. Downstream the loop signature then listed
both the ancestor and its descendants, and the reconstruction of the returned
ancestor overwrote the mutated child with the stale snapshot input.

Fix: reorder the finalization passes — snap_usage, then changes (promotion),
then usage — so the usage pass prunes descendants subsumed by any promoted
ancestor. Adds an e2e sanity repro.
@reviewable-StarkWare

Copy link
Copy Markdown

This change is Reviewable

Copy link
Copy Markdown
Contributor Author

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

@eytan-starkware eytan-starkware marked this pull request as ready for review July 14, 2026 14:34
@cursor

cursor Bot commented Jul 14, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches core loop/closure variable-usage finalization in the semantic layer, which drives loop signatures and write-back; wrong ordering could affect many mutating loops, though scope is a focused reorder plus one regression test.

Overview
Fixes a semantic bug where loops that snapshot an ancestor (@a.b) while mutating a nested field (a.b.c) could return a stale value because mutations were effectively dropped at codegen.

In Usage::finalize_as_scope, the usage-pruning pass now runs after snap_usage pruning and the changes pass (which promotes a snapshotted ancestor into usage/changes). That way, when promotion happens, descendant member paths are pruned from usage, restoring the antichain invariant and ensuring the loop reads/writes the whole promoted ancestor instead of mixing ancestor + child paths that overwrite the mutation.

Comments in the promotion logic are updated to describe ancestor promotion vs. stale child snapshots. An e2e CASM sanity case loop_snap_mut (input 2 → output 4) locks in the behavior.

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

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

@eytan-starkware eytan-starkware added this pull request to the merge queue Jul 14, 2026
Merged via the queue into main with commit ad70feb Jul 14, 2026
55 checks passed
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