feat(laurel): paren-free field ++/--, and chained-field-access test coverage#1382
Merged
fabiomadge merged 2 commits intoJun 23, 2026
Merged
Conversation
2b9f28f to
175269c
Compare
8e33ae7 to
4eb1238
Compare
Chained field access (`a#b#c`) and paren-free field increment (`c#n++`) were both blocked by the `fieldAccess` grammar op. Combine the fixes: * Grammar: `fieldAccess` becomes `@[prec(95), leftassoc]`. prec(95) binds tighter than the postfix incr/decr ops (prec 90) so `c#n++` parses as `(c#n)++` without parens; `leftassoc` lets `a#b#c` left-recurse. * Resolution: `targetTypeName` is generalized to resolve a chained `.Var (.Field tgt f)` target by recursing on `tgt` and looking the field up in the inner type's scope. The recursion is kept total via fuel (cf. `underlyingBaseType`), not `partial`. The shared type-scope field lookup is factored into `fieldTypeInScope`, which `incrDecrTargetType` now reuses instead of duplicating. * Heap: the field-read arm recurses into its target so a nested `FieldSelect` is eliminated. This single line unblocks *both* chained reads and chained writes: the write arm already calls `recurseOne` on its target, but for a nested target that recursion bottoms out in the read arm, so neither path worked before. Without the fix, an un-eliminated `FieldSelect` hits a downstream guard and aborts with a `strata-bug` (confirmed by ablation). Tests: * T9_ChainedFieldAccess: chained read/write, read-after-inner-assign, must-alias, depth-3 (`a#mid#inner#count`), chained read on both sides of a comparison, paren-free chained incr/decr, plus three negative tests (unconstrained read, two-object may-alias, write isolation) pinning the encoding as non-vacuous and frame-sound. * T23b_IncrDecrField: paren-free field `++`/`--`; header comment updated (parens are no longer required). Implements #1371.
4eb1238 to
dea6bcc
Compare
keyboardDrummer
previously approved these changes
Jun 23, 2026
keyboardDrummer
left a comment
Contributor
There was a problem hiding this comment.
Really nice! Thanks
kadirayk
previously approved these changes
Jun 23, 2026
kadirayk
left a comment
There was a problem hiding this comment.
The fieldAccess 90 → 95 bump enables paren-free c#n++ and keeps precedence equal to call, so a#b(x) still parses correctly. The fieldTypeInScope refactor is a clean, behavior-preserving dedup. Well-scoped change, negative test cases are very nice too. LGTM!
c25fc54
keyboardDrummer
approved these changes
Jun 23, 2026
kadirayk
approved these changes
Jun 23, 2026
Merged
via the queue into
reviewed-kbd-will-merge-to-main
with commit Jun 23, 2026
8d6aa98
18 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two field-access improvements on the single
fieldAccessgrammar op, plus test coverage that closes out #1371.Note: the chained-field-access capability (
a#b#cparsing, resolution, and heap elimination) already landed via #1328. This PR adds the one missing ergonomic piece, hardens the chained-access machinery with the tests it was missing, and a small refactor.Grammar — paren-free field
++/--.fieldAccessprecedence is raised 90 → 95. At 90 it tied with the postfix++/--ops (also 90), forcing(c#n)++; at 95 it binds tighter, soc#n++parses paren-free as(c#n)++. (leftassoc, already present from #1328, is unchanged.) Note the new precedence is shared withcall(also 95, via itscallee:89) — the two must stay equal ora#b(x)parsing shifts; documented at the op.Resolution — refactor only. The duplicated type-scope field lookup in
targetTypeNameandincrDecrTargetTypeis factored into a sharedfieldTypeInScopehelper. No behavior change. (This helper is also a dependency of the stacked compound-assignment PR.)Tests — fill the chained-access coverage gap. #1328 shipped chained access with only a read-side smoke test (no assertions). This adds:
T9_ChainedFieldAccess: chained write (o#inner#count := …), read-after-inner-assign, must-alias, depth-3 (a#mid#inner#count), chained reads on both sides of==, and paren-free chainedo#inner#count++(the one test that exercises this PR's grammar change), plus three negative tests (unconstrained read, two-object may-alias, write isolation) that pin the encoding as non-vacuous and frame-sound.T23b_IncrDecrField: paren-free single-level field++/--.Full
lake buildandlake testpass.