Skip to content

fix(semantic): expand a nested repetition over its own group of captures - #10281

Draft
orizi wants to merge 1 commit into
claude/macro-repetition-driverfrom
orizi/07-30-fix_semantic_expand_a_nested_repetition_over_its_own_group_of_captures
Draft

fix(semantic): expand a nested repetition over its own group of captures#10281
orizi wants to merge 1 commit into
claude/macro-repetition-driverfrom
orizi/07-30-fix_semantic_expand_a_nested_repetition_over_its_own_group_of_captures

Conversation

@orizi

@orizi orizi commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #10270.

A nested repetition now expands over its own group of captures rather than the whole flat list.

Captures are stored flat, one list per placeholder, so a nested repetition had no way to say "this iteration's slice". With more than one group at a level the inner block re-walked every capture on every outer step:

($($a:expr => [$($b:expr),*]),*) => { $($a + $($b +)*)* 0 }
groups!(1 => [7, 8], 2 => [9])
  before: 1 + 7+8+9+2 + 7+8+9+0
  after:  1 + 7+8+2 + 9+0

MatcherContext gains rep_ids, rep_parents and rep_group_lens. A repetition is entered once per iteration of its parent, so the parent's current index selects the group and the lengths of preceding groups give the offset. rep_ids is keyed by the pattern's syntax node rather than by traversal order — a nested repetition is re-walked on every outer iteration and would otherwise get a fresh id, and no stable identity, each time.

Tests

Goldens in expansion_test_data/inline_macros, all expected values cross-checked against macro_rules! under rustc:

  • nested repetition matching a different number of times per outer iteration
  • three repetition levels, each with several groups
  • three_levels strengthened to repeat at every level (per review) — 1 => [2 => (3, 4), 5 => (6, 7)], 8 => [9 => (10, 11)]
  • unsorted_lhs — a pattern whose placeholders are not ordered by repetition depth
  • multi_vars — several placeholders bound at each of two depths

The last three were requested on #10270 but cannot live there: that commit still walks the flat list, so they emit wrong output and landing them there would mean blessing it.

A repetition is re-walked once per iteration of its parent, so the Repetition
arm minted a fresh RepetitionId on every outer iteration. A nested repetition
therefore had no stable identity: placeholder_to_rep_id ended up pointing at the
last traversal's id, and expansion iterated the whole flat capture list on every
outer iteration. m!(1 => [7], 2 => [9]) expanded to 1 + 7+9+2 + 7+9+0.

Ids now come from the repetition's pattern site, so one id spans all iterations
of the enclosing block, and each entry into a repetition records its match count
in rep_group_lens. A repetition is entered once per iteration of its parent, so
the parent's current index selects the group and the preceding group lengths
give its offset into the flat captures - the leaf lookup is unchanged.

is_macro_rule_match now returns the MatcherContext instead of a tuple of the
fields expansion happens to need. The tuple silently dropped rep_depths when it
was added, and dropped these two tables as well; returning the context is only
safe now because the dead match-time repetition_indices loop, which left that
map populated, is removed here - it was write-only, as the context was discarded
before expansion.

Still unhandled, and unchanged: a block whose only placeholder is bound to a
deeper repetition, as in $($($x +)*)* 0, has nothing bound at its own level and
re-walks the group. macro_rules! rejects that shape; making it an error is the
follow-up, not making it iterate.

orizi commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

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

@reviewable-StarkWare

Copy link
Copy Markdown

This change is Reviewable

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.

2 participants