Skip to content

Refactor std.* call signature table in checker#133

Open
kylejryan wants to merge 1 commit into
vercel-labs:mainfrom
kylejryan:kylejryan/checker-stdlib-signature-table
Open

Refactor std.* call signature table in checker#133
kylejryan wants to merge 1 commit into
vercel-labs:mainfrom
kylejryan:kylejryan/checker-stdlib-signature-table

Conversation

@kylejryan
Copy link
Copy Markdown

@kylejryan kylejryan commented May 19, 2026

Summary

Collapses the three parallel strcmp(...) chains in native/zero-c/src/checker.cstd_call_return_type, std_call_arg_count, std_call_arg_type — plus the small std_http_error_code helper into a single sorted lookup table over a new StdSig record, looked up via bsearch. The per-helper signature now lives in exactly one place (native/zero-c/src/std_calls.inc).

Why

The old shape stated each std.* helper's signature three times across three functions, in three (manually maintained) orderings. Updating one and forgetting another was a silent typechecker bug. Each typecheck of a std.* call also paid for up to three full linear strcmp chains (~130 entries each) plus two heap-allocated ZBufs for the dotted name.

What changes

  • native/zero-c/src/std_calls.inc — new file. One row per helper, declared with STD_CALL_<N>(name, return_type, args...). The arity is structurally fixed by the macro name, so arg_count cannot drift from the populated argument list. NULL is used for argument slots whose type is not enforced at the table level (matching prior behavior for std.fs.readAll, std.json.parse{,Bytes}, std.mem.{allocBytes,byteBuf,get,eqlBytes}).
  • native/zero-c/src/checker.c:
    • Replaces the four functions with a single StdSig table, a std_call_lookup binary search, and three thin accessor functions preserving the existing call-site signatures (modulo std_call_return_type switching from const Expr * to const char * for consistency with the other two accessors — the two call sites are updated).
    • Adds a debug-only __attribute__((constructor)) (check_std_sigs_sorted) that asserts the table is sorted at startup. Release builds (NDEBUG) skip it entirely.
  • Includes std.http.error* constants (errorNone, errorConnect, …) explicitly in the table; they were previously implicit via std_http_error_code fall-throughs in two of the functions. ir.c keeps its own independent copy of ir_std_http_error_code and is untouched.

Net diff: +228 / -437 lines.

Performance / space

Before After
Lookups per typechecked call up to 3 linear strcmp chains up to 3 bsearches
Comparisons per lookup ~130 strcmps worst case ~7 strcmps (⌈log₂ 148⌉)
ZBuf allocations at the hot call site 2 per call 1 per call
.text for the lookup tables ~15 KB across three functions ~75 lines of code + ~2 KB of .rodata

The Makefile already picks up src/*.inc via its INCLUDES glob; no build changes required.

Test plan

  • make -C native/zero-c — release build clean with -Wall -Wextra -Wpedantic -Os -std=c11
  • Byte-for-byte snapshot of zero check against all 182 .0 files under examples/zero diff vs. pre-refactor
  • pnpm run native:smoke — pass
  • pnpm run reliability:smoke — pass
  • bash scripts/sanitizer-smoke.sh — ASan + UBSan clean
  • pnpm run test:zero — 10/10 CLI tests pass

Notes / follow-ups (not in this PR)

  • The three accessor functions could be inlined at their five call sites in checker.c to do a single std_call_lookup per typecheck instead of up to three, but that touches the large EXPR_CALL block and was kept out of scope here.
  • Once this lands, ir.c's ir_std_http_error_code could be deduplicated against the same table, but it's a separate module and out of scope.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 19, 2026

@kylejryan is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

Collapse the three parallel strcmp(...) chains in checker.c
(std_call_return_type, std_call_arg_count, std_call_arg_type) plus
std_http_error_code into a single sorted table keyed by name, with a
bsearch lookup. The signature for each std.* helper now lives in one
place (native/zero-c/src/std_calls.inc) and the arity is fixed
structurally by the STD_CALL_<N> macro it is declared with, so
arg_count cannot drift from the populated argument list.

A debug-only constructor (check_std_sigs_sorted) asserts the table
remains sorted at startup; release builds pay nothing for it.

Behavior is preserved: all 182 example check outputs match byte-for-byte
against pre-refactor; pnpm native:smoke, reliability:smoke,
native:sanitize (ASan+UBSan), and test:zero (10 CLI tests) all pass.

Net diff: -437 / +228 lines. Per-typecheck lookup drops from ~130
strcmps to ~7 (log2 of the table).
@kylejryan kylejryan force-pushed the kylejryan/checker-stdlib-signature-table branch from 70b08c3 to ea29413 Compare May 19, 2026 19:07
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