Skip to content

test(codegen): match the alloca TYPE, not its whole def text (#7675 follow-up) - #7681

Merged
proggeramlug merged 3 commits into
mainfrom
fix/7675-followup-alloca-type
Aug 9, 2026
Merged

test(codegen): match the alloca TYPE, not its whole def text (#7675 follow-up)#7681
proggeramlug merged 3 commits into
mainfrom
fix/7675-followup-alloca-type

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Follow-up to #7675, which was squash-merged while these review fixes were being applied. Three findings from the review on that PR; the first is the same defect class #7675 exists to remove, so it should not sit unlanded.

The align suffix would have made every negative gate vacuous again

testing::temp_slots::temp_root_slots filtered slots by comparing the alloca's whole def text:

matches!(defs.get(slot), Some(&"alloca i64") | Some(&"alloca ptr addrspace(1)"))

One align 8 away from matching nothing. And the failure mode is the silent one: the filter empties the result, temp_root_slots returns [], and every assert_no_temp_rooting in the tree passes for a program that roots — exactly the vacuity #7503 was opened about. The positives would have gone red, so the risk was bounded today; the negatives would have lost their meaning without warning.

Now matched by alloca type, through one shared root_slots::alloca_type, applied in all three places that were comparing whole def text:

  • temp_slots::temp_root_slots (the silent one),
  • root_slots::classify (loud — an unmatched type hits the panic arm — but it would have panicked on a spelling that is perfectly valid), and
  • root_slots::value_slot_barriers.

Sabotage, run. With alloca_type temporarily replaced by the old exact-text compare, both new tests fail and nothing else does:

test testing::root_slots::tests::an_aligned_alloca_is_still_classified ... FAILED
test testing::temp_slots::tests::an_aligned_alloca_is_still_recognised_as_a_temp_slot ... FAILED
test result: FAILED. 0 passed; 2 failed; 776 filtered out

Both tests assert their own substitution actually applied (assert_ne!(aligned, MIXED)), so neither can pass by testing an unmodified fixture.

The write-back check was the one clause not using the shared derivation rule

temp_root_coverage's accumulator test required the store-back value to equal the push result verbatim, while every neighbouring clause goes through slot_holding, which deliberately tolerates one boxing step — and a raw allocation result is NaN-boxed before it reaches a slot. A lowering that boxed the push result would have failed a test whose contract still held. Now slot_holding(&ir, &push).as_deref() == Some(slot.as_str()).

A redundant pass that read as coverage it did not add

zero_seeded_slots had a load-specific second pass over three load …, ptr prefixes. Verified redundant: the generic , ptr %s scan above it already catches %d = load i64, ptr %s and both other spellings. Removed, with a comment stating why there is deliberately no load-specific pass.

Declined, with reasons

  • Broaden derives_from to follow every operand. It backs the positive clauses ("this consumer's operand WAS re-read from the slot"), so a breadth-first walk makes that claim easier to satisfy and could let a genuinely unrooted operand pass. The current narrow walk errs toward a loud false alarm. On this of all subjects, trading a false alarm for a possible silent pass is the wrong direction; if it ever fires in practice the fix is to name the operand index at the call site.
  • Dedup entry_opts / module_with_init. Fair, but there are already ~6 copies across crates/perry-codegen/tests/. Deduplicating 2 of 6 does not remove the "add every new CompileOptions field twice" hazard, and doing all 6 is a repo-wide refactor.

Verification

cargo fmt --all -- --check clean. cargo test -p perry-codegen --lib 778 passed (776 + the two new regression tests); --test temp_root_operand_temporaries 19/19, --test scalar_replaced_slot_roots 11/11, --test shadow_slot_hygiene 12/12. ./scripts/check_file_size.sh OK.

Test-only: every changed file is a #[cfg(test)] / feature = "testing" module, so the shipped compiler is unchanged.

An `align` suffix would have emptied `temp_root_slots` and made every
`assert_no_temp_rooting` vacuous again — the exact failure this PR removes.
Matched through one shared `root_slots::alloca_type` in all three places that
compared whole def text, with a regression test each that fails against the old
compare. Also routes the accumulator write-back check through `slot_holding` so
it tolerates the same boxing step its neighbours do, and drops a redundant
load-specific pass in `zero_seeded_slots` that re-inserted what the generic
`, ptr %s` scan already caught. Raised by review on #7675.

Claude-Session: https://claude.ai/code/session_01Y1QZ5wUP9gRSwpiweT4Wix
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@proggeramlug, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 35 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 45b41844-9283-4fe8-bd66-a7685ffe0553

📥 Commits

Reviewing files that changed from the base of the PR and between 6cdcd79 and 1e99994.

📒 Files selected for processing (4)
  • changelog.d/7681-alloca-type-prefix-match.md
  • crates/perry-codegen/src/temp_root_coverage/mod.rs
  • crates/perry-codegen/src/testing/root_slots.rs
  • crates/perry-codegen/src/testing/temp_slots.rs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@proggeramlug

Copy link
Copy Markdown
Contributor Author

Audit — merging as v0.5.1386

This is the fix that keeps #7675 from re-creating the defect it removed, so it should not have been left on overnight — my merge of #7675 landed while you were mid-fix, which is what stranded it.

Sabotage-verified independently: reverting alloca_type to the whole-def-text compare reddens 8 tests, error[ 0, test binary reached — including an_aligned_alloca_is_still_classified, which is the one that names the actual hazard. Green again after restore (with touch, per the mtime trap).

The failure mode was worth landing on its own: temp_root_slots compared Some(&"alloca i64") against the whole definition text, so one align 8 suffix would have emptied the slot set and made every assert_no_temp_rooting in the tree vacuous — silently, and in exactly the way #7503 exists to prevent. That this appeared inside the change written to eliminate it is the honest measure of the shape.

Your two declines are both right, and the first is the important one: broadening derives_from to every operand backs the positive "was re-read" clauses, so widening the walk makes them easier to satisfy — trading a loud false alarm for a possible silent pass. On this PR specifically that is the wrong direction.

Two process findings I'm keeping, and both are mine to own:

  • Closes #A, #B, #C closes only the first. GitHub needs the keyword per issue. Three issues stayed open until you closed them by hand.
  • Pushing to a branch whose PR has been squash-merged silently re-creates the deleted branch. You caught it by checking the PR head SHA rather than trusting the push output — which is the same discipline as everything else here: check the thing, not the thing's report of itself.

Gates: 22/22 lint, fmt clean, perry-codegen --lib 778, perry-runtime --lib 1917.

@proggeramlug
proggeramlug merged commit 175c5fd into main Aug 9, 2026
@proggeramlug
proggeramlug deleted the fix/7675-followup-alloca-type branch August 9, 2026 05:55
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.

1 participant