Skip to content

fix(semantic): drive a macro expansion block by the repetition at its own depth - #10282

Draft
orizi wants to merge 1 commit into
orizi/07-30-fix_semantic_expand_a_nested_repetition_over_its_own_group_of_capturesfrom
orizi/08-02-fix_semantic_drive_a_macro_expansion_block_by_the_repetition_at_its_own_depth
Draft

fix(semantic): drive a macro expansion block by the repetition at its own depth#10282
orizi wants to merge 1 commit into
orizi/07-30-fix_semantic_expand_a_nested_repetition_over_its_own_group_of_capturesfrom
orizi/08-02-fix_semantic_drive_a_macro_expansion_block_by_the_repetition_at_its_own_depth

Conversation

@orizi

@orizi orizi commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #10281.

A $() expansion block whose only placeholder is bound to a deeper repetition than the block's own level expanded wrongly:

($([$($x:expr),*]),*) => { $($($x +)*)* 0 }
deeper_only!([1, 2], [3, 4])
  before: 1+2+1+2+0    // group [3,4] dropped entirely, group [1,2] emitted twice
  after:  1+2+3+4+0

Nothing was bound at the outer block's depth, so it fell back to driving on $x too and both loops walked the same group.

macro_rules! accepts this shape and expands it correctly at any depth (verified under rustc 1.96), so the fix is to iterate, not to reject. What rustc does reject — ($($a:expr),*) => { $($a + $($a +)*)* 0 }, "attempted to repeat an expression containing no syntax variables matched as repeating at this depth" — is exactly our E2202, and the existing max_path_len_in_block >= curr_rep_depth check is a faithful port of that rule.

Change

A block nested in d blocks drives the pattern repetition at depth d, reached by climbing rep_parents from a placeholder's own repetition (repetition_at_depth). depth is threaded through expand_macro_rule_ex.

This makes the (is_iterated, pattern_depth) ranking from #10270 dead — a block at depth d drives a repetition no enclosing block can be iterating — so find_repetition_driver loses it, and repetition_group's parent lookup tightens to an expect (the no-parent arm stays: every depth-1 block takes it).

register_repetition_placeholders no longer lets a shallower repetition overwrite a deeper binding. It re-runs on every iteration of the enclosing repetition, and a nested repetition matching zero times that iteration is never entered, so it would not restore what was overwritten. Without this the depth rule regresses two_deeper_only!([(1)], []) into expanding not at all.

Tests

Seven goldens in expansion_test_data/inline_macros, every expected value cross-checked against rustc. Four fail at the parent commit; the other three are guards on the new expect and on zero-match inputs, which have masked three separate bugs in this code already.

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