← board

C corpus step 2: zlib v1.3.1 bring-up

Done

Blockers

  1. bug-c-typedef-name-as-uninitialized-localFIXED 2026-07-06 (commit 0e2740ee): shadowed-typedef-name-as-local now parses; trees.c clears.
  2. [[bug-c-gzgetc-fnlike-macro-call]] (open blocker) — crtl + zlib.h + example.c fails: Expected: ), but got: (Kind: 74) near: gzgetc >>> file. example.c calls the gzgetc(file) macro that zlib.h defines to inline the fast path (expands to a comma/ternary expr referencing gzFile internals). Not yet reduced to a minimal repro — do that first in the fix session (candidates: function-like macro whose body is a parenthesized comma-expression; or the (gzgetc)(file) parenthesized-function-name call in the fallback arm). File as its own bug-c-* ticket once isolated.

Gate

Both blockers fixed → make test-zlib: pxx runner output byte-identical to the gcc oracle, exit 0. Then graduate to a fuller vector set (minigzip round-trip) and cross targets. Bonus: cross-check against the Pascal zlib in lib-test.

gzgetc function-like-macro parse blocker RESOLVED (bug-c-gzgetc-fnlike-macro-call: (name)(args) paren-function-name call). zlib example.c now parses, compiles and LINKS the runner. make test-zlib next blocker: runtime undefined symbol: gz_error — a zlib-internal function not being compiled/linked into the runner (a zlib .c TU missing from the build set, or an internal symbol not emitted). Next: find which zlib TU defines gz_error (gzlib.c) and why the runner build omits it; then re-diff vs the gcc oracle (8 lines expected).

2026-07-07 — 4/8 output lines now match (was parse-blocked)

Fixed a cascade of general string-literal-decay + linkage bugs, each advancing the runner (all landed on master with regressions b171-b174, self-host byte-identical, c-conformance 195/0):

inflate -2 sharpened (2026-07-07)

Isolated with a minimal deflate+inflate harness: inflateInit=0 (state non-null), then inflate returns Z_STREAM_ERROR (-2) MID-STREAM at total_in=7 under the byte-at-a-time buffers (avail_in=avail_out=1). uncompress() works because it inflates in ONE call and never suspends; test_inflate forces suspend/resume every byte. So the bug is in inflate's multi-call SUSPEND/RESUME path — the LOAD/RESTORE of state->{hold,bits,next,put,have,left} across calls and the NEEDBITS/PULLBYTE goto-driven resume. Likely a pxx codegen issue in inflate.c's large single function (local caching of state fields, or a goto/label save), not a frontend string bug. Next: instrument inflate()'s mode switch to log where mode goes invalid on the 2nd/3rd call; compare state->hold/bits save-restore vs gcc.

2026-07-07 — 6/8 lines; inflate WORKS (COPY macro leak fixed)

The inflate -2 was NOT a codegen bug: zutil.c includes gzguts.h, whose private #define COPY 1 is never #undef'd, and in pxx's single-TU unity build it macro-replaced inflate.h's COPY enum constant (16195) with 1 → inflate() corrupted state->mode after a STORED block and returned Z_STREAM_ERROR under byte-at-a-time buffers. Real zlib compiles each .c separately so never sees it. Fixed in test/zlib/runner.c: #undef COPY before inflate.c (gzguts.h is guardless so gz*.c re-define it). Diagnosis method: bisected mode via printf at inf_leave → mode went 16193(STORED)→1 at state->mode = COPY; PROBE showed #ifdef COPY true = a leaked macro; only COPY collides (gzguts vs inflate enum). GENERAL LESSON: pxx's no-linker unity C build leaks every private macro across all files; a porter must #undef colliding names (or pxx would need per-file macro scoping — big preproc change). Now passes version/uncompress/gzread/gzgets/ inflate/large_inflate.

NEXT BLOCKER: inflateSync error: -3 (Z_DATA_ERROR). inflateSync returns -3 when the 00 00 FF FF flush marker isn't found (state->have != 4). syncsearch is correct; the marker comes from test_flush's deflate(Z_FULL_FLUSH) — so suspect the DEFLATE Z_FULL_FLUSH output (deflate.c/trees.c flush path), not inflate. Also re-check for further gzguts/other private-macro collisions in deflate.c (none found for inflate enum, but deflate has its own constants). Then inflate-with-dictionary (blocked behind sync).

inflateSync -3 isolated to deflate Z_FULL_FLUSH (2026-07-07)

Direct probe: our deflate(strm, Z_FULL_FLUSH) on "hello" emits 17 bytes with NO 00 00 FF FF sync marker, so inflateSync's syncsearch never finds have==4 → Z_DATA_ERROR. syncsearch and inflateSync are correct; the bug is DEFLATE's full-flush path not emitting the empty stored block / sync marker (deflate.c deflate() flush switch + _tr_stored_block/_tr_align in trees.c). Not inflate, not a COPY-style macro leak (checked: only COPY collided with the inflate enum). Focused deflate-internals session: verify the flush-level switch recognizes Z_FULL_FLUSH (=3; note Z_RLE strategy is also 3 — different namespace, shouldn't matter) and that _tr_stored_block writes the 4-byte marker. inflate-with- dictionary is blocked behind this. zlib now 6/8 lines (was parse-blocked).

Pinpointed to trees.c bit-buffer (2026-07-07)

Instrumented deflate: it DOES reach the FULL_FLUSH marker branch (bstate=block_done, flush=3) and calls _tr_stored_block(s,0,0,0) which writes 5 bytes to pending — but the bytes are wrong. deflate("hello",Z_FULL_FLUSH) emits: 78 9c 00 05 00 fa ff 05 00 00 00 00 00 00 00 00 00 (17 bytes). Expected a clean stored "hello" block + a 00 00 00 ff ff empty-block marker; instead the data/ marker region is corrupt (no 00 00 ff ff anywhere). So the bug is in trees.c's bit-buffer primitives used by _tr_stored_block: send_bits (3-bit block header), bi_windup (byte align + flush bit buffer), put_short (LEN/NLEN). Suspect a codegen issue in the bit-accumulator (s->bi_buf/s->bi_valid ush arithmetic) or a macro (send_bits/put_short are macros). Focused trees.c session; verify each primitive with a standalone bit-write test vs gcc. NOTE: Huffman blocks WORK (uncompress/inflate pass), so the fault is specifically the STORED-block path — _tr_stored_block's data copy AND the empty-block marker (deflate("hello") chose a stored block: output shows the 00 05 00 fa ff header but the 'hello' data bytes are missing/zeroed, then no clean 00 00 ff ff marker). Start at _tr_stored_block

RESOLVED 2026-07-07 (Track A+C, sole-A) — zlib byte-identical to gcc

make test-zlib PASSES: the pxx-built libc-free runner (all of zlib compiled as one TU) produces output byte-identical to the gcc oracle for every example.c test (version, uncompress, gzread, gzgets, inflate, large_inflate, inflateSync, inflate -with-dictionary). Reached by a cascade of general cfront fixes this session (each with a regression, all self-host byte-identical, c-conformance 194→198):