Repository navigation
port(gen_canvas): the caller owns the pixel storage, so t27b runs it (Closes #7658) - #7670
Merged
Merged
Conversation
Canvas_init filled a local [10000]Color and returned a slice of it. The slice pointed into Canvas_init's own frame, so every test read its pixels from a frame that had already ended. The reference passed only because no call reused that stack before the reads; t27b refuses the return as ExprReturn(frame address) since #7660. The trinity original takes an allocator. Here the caller passes the pixel storage instead, as the sibling port gen_image.t27 does: Canvas_init(storage: []Color, width, height) fills the first width * height entries and returns them as the canvas's pixels. Deinit has nothing to free. Each test owns its storage, filled with 9s so only what Canvas_init writes reads back as black. Every assert is kept. Canvas_init_basic also checks pixels.len == 100. Canvas_deinit's `assert(true)` executed no runtime assert (a vacuous pass); it now checks that deinit leaves the caller's pixels in place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The port no longer returns a slice of its own frame, so t27b runs it: 5 of 5 tests, 53 runtime asserts, each test equal to the reference. The row was blocked, ExprReturn(frame address), reason reference-bug. Row from `tri t27b ratchet --bless --accept-new` of the t27b lab run of 9b1b3c8, put onto master 2eabc3e's ledger alone. The counts follow the one row: pass 870 -> 871, not_pass 52 -> 51, and max_not_pass 52 -> 51, as bless sets the cap. Against the branch run, the result leaves exactly master's own 29 findings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gHashTag
enabled auto-merge (squash)
October 8, 2026 03:48
Contributor
gHashTag
pushed a commit
that referenced
this pull request
Oct 8, 2026
Merged from master's side. The only conflict was the ledger's counts header: master's #7670 moved gen_canvas.t27 to pass, and this branch added four pass rows and moved zig_test_shadowing.t27's blocker. Both sides' rows are kept: pass 875, pass_vacuous 150, not_pass 51, max_not_pass 51; 1076 rows, no duplicate path. Closes #7422 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Closes #7658. Refs #7550 #6063.
What was wrong
Canvas_initinspecs/port/trinity/src/tri/gen_canvas.t27filled a localvar pixels: [10000]Colorand returnedCanvas{ .pixels = pixels[0..count], ... }. That slice pointed intoCanvas_init's own frame, so it dangled at the return, and every test read its pixels from a frame that had already ended.ExprReturn(frame address). The ledger had the row asblocked, reasonreference-bug.What changed in the port
The trinity original,
Canvas.init(allocator, width, height), gets its pixels from the caller's allocator. In this port the caller now passes the storage, the same way the sibling portgen_image.t27does (it already passes on t27b):Canvas_init(storage: []Color, width, height)writes opaque black (0, 0, 0, 255) to the firstwidth * heightentries and returns them ascanvas.pixels. A buffer that is too short traps on the index, where the Zig's alloc would fail.Canvas_deinithas nothing to free, because the caller owns the storage.Color,Canvas,Canvas_setPixelandCanvas_clearare unchanged.width * height. The storage starts filled with 9s, so only whatCanvas_initwrote reads back as black.Canvas_init_basicalso checkscanvas.pixels.len == 100.Canvas_deinitended inassert(true), which runs no runtime assert, so it was a vacuous pass. It now checks that deinit leaves the caller's pixels in place:pixels.len == 4andstorage[3].a == 255.t27c gennow printsreturn Canvas{ .pixels = storage[0 .. count], ... }, wherestorageis the caller's slice.This PR changes only
.t27and ledger data. It adds and changes no hand-written foreign code.The bug is gone: a call between init and the reads
I tested two scratch copies, neither committed. Each inserts
var junk_k = clobber(); assert(junk_k == 7);right afterCanvas_initin all 5 tests.clobber()fills a 40032-byte local array with 7, the same filler as #7658.t27c test-report, t27c from master 2eabc3e, zig 0.16.0)Canvas_init_basicandCanvas_setPixel_out_of_boundsFAIL. These are the two tests that read a pixel before writing one.On the t27b lab, with master's t27b,
t27b test --check:ExprReturn(frame address).Reference:
t27c test-reporton the spec (t27c lab)Canvas_deinit)t27b on the spec
t27b test --check(t27b lab, master's t27b, aarch64 under qemu): 5 passed, 0 failed, 53 runtime asserts. JIT and interpreter agree.The corpus run below records each test as equal to the reference, test by test.
t27b lab: full corpus with the reference
How:
/tmpdirectory on the t27b lab imported/opt/t27b-lab/lab.py(lab_py_sha 94254f63f) and ran itslab_run.T27_SRVandT27_WORKpointed into that directory, so the lab's/srv,/workand scheduled runs were not touched.--jobs 12, 3 reference workers, fuzz off.qemu-aarch64 t27b corpus specs --json --runner qemu-aarch64 --timeout-ms 60000 --jobs 12.t27c test-report(zig 0.16.0).cargo test --release -p t27b(aarch64, qemu)Per file, master against branch: exactly one file differs.
gen_canvas.t27goes from blocked (ExprReturn(frame address)) to pass: 5 tests, 53 runtime asserts. Its per-test verdicts equal the reference's on all 5 tests.Ledger
One row changes:
gen_canvas.t27goes fromblocked,ExprReturn(frame address),reference-bugtopass.How it was made:
tri t27b ratchet --bless --accept-newof the branch run produced the row.--accept-newonly lets the whole-run bless go through: master's own unlisted specs would raise the cap. None of their rows is taken.pass870 -> 871 andnot_pass52 -> 51.max_not_passgoes 52 -> 51, which is how bless sets the cap.Check: against the branch run, the new ledger leaves exactly master's own 29 findings and none from this branch. Against the same ledger, the master run has the same 29 findings.
Seal
gen_canvas.t27has no seal under.trinity/seals/, so there was nothing to reseal. All checks below ran on the t27c lab, with the t27c built from master 2eabc3e:tri seals twins: exit 0.tools/check_seal_currency.py(TRI_T27Cset): 0 stale generated-code hashes, exit 0.tools/check_seal_coverage.py(that t27c at<repo>/target/release/t27c):OK: 1604 seals, 1483 hold, 121 known-broken ... listed in seal_baseline.txt, exit 0.Gates (t27c lab)
check_all(): master'sgen/c/policy/own_language.cwithlefthook.yml's one-line C main.origin/master:tools/policy/foreign-exceptions.txtplusgit diff --name-status origin/master...HEAD.--) and CI (--ci) forms.cli/t27b/src/new_glue.rsis denied, exit 1.check_budget(): overgit diff --numstat --no-renames origin/master...HEAD, exit 0.lower.rsis denied, exit 1.tri hooks pre-commiton the staged diff: PASSED.tri hooks commit-msgon both messages: L1 PASSED.t27c suite --repo-root . --ratchet --corpus-only: RATCHET CLEAN.tools/check_assertionless_spec_tests.py: ok, 3761 in 30 files, the same as the baseline.Lines (origin/master...HEAD,
git diff --numstat --no-renames)specs/port/trinity/src/tri/gen_canvas.t27docs/reports/t27b_expectations.jsonWhat this does not do
#7658 also names a second fix: make t27c's Zig backend refuse a return of a local's address, as gen-c already does (#3445). That is a t27c change and is not in this PR.
The conformance spec
frame_address_return.t27from #7550 staysblockedwith reasonreference-bug. It exists to hold that shape.Generated with Claude Code