From d335d2ff6f24a5b01e0469839ba1b03b1f3269ef Mon Sep 17 00:00:00 2001 From: InauguralPhysicist Date: Wed, 5 Aug 2026 06:12:10 -0500 Subject: [PATCH] build: freestanding profile enforces -Werror=switch; gate audits compile-bearing scripts (#835) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #835. - tools/freestanding_check.sh: both hand-written gcc invocations now carry -Werror=switch (stage-1 runtime set + stage-2 mini-libc/libm) — the freestanding profile was the one compile path where an unhandled ASTType/opcode case only warned. Planted-fault verified: a temporary extra enum value fails make freestanding-check, reverting restores green. - tools/werror_switch_check.sh: the [99i] gate now audits compile-bearing shell scripts directly (comment lines stripped, then the same join/split/classify pipeline and per-unit zero-line hard failure as the Makefile dry runs). SCRIPT_AUDITS = tools/freestanding_check.sh; the gate names the script on a violation (planted-fault verified). New selftest shapes: script compile line caught/clean, commented compile lines not examined. build.sh stays out of scope, documented honestly in the header ($SOURCES-variable compile lines are indistinguishable from link lines to the recognizer). - tests/run_all_tests.sh [99i] text updated; CHANGELOG entry. Validated: gate 320 invocations across 22 targets + 1 script, selftest green; make freestanding-check green; full suite 3758/3758. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 14 ++++++++ tests/run_all_tests.sh | 14 ++++---- tools/freestanding_check.sh | 4 +-- tools/werror_switch_check.sh | 62 +++++++++++++++++++++++++++++++----- 4 files changed, 78 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cd18293..f5872eeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,20 @@ All notable changes to EigenScript are documented here. ### Fixed +- **The freestanding profile now enforces switch exhaustiveness + (#835).** `tools/freestanding_check.sh` was a third compile mechanism + alongside the Makefile and `build.sh`, and neither of its hand-written + `gcc` invocations carried `-Werror=switch` — so an unhandled + `ASTType`/opcode case that is a hard error on every other build path + only warned there. Both invocations are armed (verified with a planted + enum case: `make freestanding-check` now fails on it), and the [99i] + werror-switch gate learned to audit compile-bearing shell scripts — + comment-stripped, through the same classifier as the Makefile dry + runs, with the same zero-line hard failure and new selftest shapes — + so this can't regress silently. `build.sh` stays outside the gate, + stated honestly in the gate header: its compile lines name sources via + `$SOURCES` variables the recognizer cannot tell from a link line. + - **Widget drawing is contained (#823).** `render` now wraps every widget's draw in a clip of its own rect intersected with its ancestors' — `canvas` `on_paint` included, so a paint callback can no diff --git a/tests/run_all_tests.sh b/tests/run_all_tests.sh index ef87cd19..e217cf3f 100755 --- a/tests/run_all_tests.sh +++ b/tests/run_all_tests.sh @@ -4261,15 +4261,17 @@ fi rm -rf "$CONT_DIR" echo "" -# [99i] Uniform -Werror=switch gate (#817 follow-up). Dry-runs every -# compiling Makefile target and asserts every emitted compile line carries -# the flag; --selftest proves the checker catches each planted fault shape -# (and that a zero-line audit is a hard failure, not a silent pass). -echo "[99i] werror-switch compile-line gate (#817)" +# [99i] Uniform -Werror=switch gate (#817 follow-up; #835 extended it to +# compile-bearing shell scripts). Dry-runs every compiling Makefile target +# plus the audited scripts (tools/freestanding_check.sh) and asserts every +# emitted compile line carries the flag; --selftest proves the checker +# catches each planted fault shape (and that a zero-line audit is a hard +# failure, not a silent pass). +echo "[99i] werror-switch compile-line gate (#817/#835)" TOTAL=$((TOTAL + 1)) if bash "$TESTS_DIR/../tools/werror_switch_check.sh" && bash "$TESTS_DIR/../tools/werror_switch_check.sh" --selftest >/dev/null; then PASS=$((PASS + 1)) - echo " PASS: every dry-run compile line carries -Werror=switch (gate self-test green)" + echo " PASS: every dry-run + audited-script compile line carries -Werror=switch (gate self-test green)" else FAIL=$((FAIL + 1)) echo " FAIL: a compile line lacks -Werror=switch, or the gate self-test broke (see lines above)" diff --git a/tools/freestanding_check.sh b/tools/freestanding_check.sh index 32a6c44d..f76aa2d9 100755 --- a/tools/freestanding_check.sh +++ b/tools/freestanding_check.sh @@ -18,7 +18,7 @@ trap 'rm -rf "$BUILD"' EXIT SRC="eigenscript lexer parser builtins builtins_host builtins_tensor hash arena state strbuf ext_store fmt lint chunk compiler vm jit trace eigs_embed" for f in $SRC; do gcc -O2 -ffreestanding -fno-stack-protector -U_FORTIFY_SOURCE \ - -Werror=implicit-function-declaration \ + -Werror=implicit-function-declaration -Werror=switch \ -DEIGENSCRIPT_FREESTANDING=1 \ -DEIGENSCRIPT_EXT_HTTP=0 -DEIGENSCRIPT_EXT_MODEL=0 -DEIGENSCRIPT_EXT_DB=0 \ -c "src/$f.c" -o "$BUILD/$f.o" @@ -53,7 +53,7 @@ echo "OK stage 1: freestanding import surface is within the ledger allowlist" for f in mini_libc mini_libm mini_fmt mini_strtod; do gcc -O2 -ffreestanding -fno-builtin -ffp-contract=off -fno-math-errno \ -fno-stack-protector -U_FORTIFY_SOURCE \ - -Werror=implicit-function-declaration \ + -Werror=implicit-function-declaration -Werror=switch \ -DEIGS_MINI_STANDARD_NAMES=1 \ -c "src/freestanding/$f.c" -o "$BUILD/$f.o" done diff --git a/tools/werror_switch_check.sh b/tools/werror_switch_check.sh index eced8b8c..c56eab8c 100755 --- a/tools/werror_switch_check.sh +++ b/tools/werror_switch_check.sh @@ -29,13 +29,17 @@ # `-Werror=switch-enum` is a different warning and must NOT satisfy # this gate. # -# Blind spots, stated honestly: compiles hidden INSIDE shell scripts are -# invisible to `make -n` and out of scope here — and the exclusion is not -# hypothetical: tools/freestanding_check.sh compiles its runtime set -# WITHOUT the flag (a known gap, tracked separately, deliberately not -# absorbed into this gate's scope); build.sh's three compile lines are -# armed (#786). This gate asserts the invariant over what `make` emits — -# no more, no less. +# Compiles hidden INSIDE shell scripts are invisible to `make -n`, so the +# scripts whose compile lines the classifier can recognize are audited +# DIRECTLY (#835): comment lines stripped, then the script text goes +# through the same join/split/classify pipeline as a dry-run stream, with +# the same per-script zero-line hard failure. Today that list is +# tools/freestanding_check.sh — its gcc lines name "src/$f.c" literally. +# Blind spot, stated honestly: build.sh stays out of scope — its three +# compile lines are armed (#786) but name their sources via +# $SOURCES/$LSP_SOURCES variables, which the recognizer cannot tell from +# a pure link line, so auditing it would assert nothing (and the lsp/ +# minimal lines would trip the zero-line assertion spuriously). # # A gate that silently matches nothing is worse than no gate — it passes # forever. Two assertions prevent that. PER TARGET: a dry run yielding @@ -74,6 +78,15 @@ TARGETS="build full http zlib net gfx asan asan-http tsan valgrind poison \ lsp dap jit-smoke lib embed-smoke embed-smoke-gfx pgo coverage \ fuzz fuzz-libfuzzer freestanding-libc-diff" +# Compile-bearing shell scripts audited directly (#835) — see header. +SCRIPT_AUDITS="tools/freestanding_check.sh" + +# Comment lines must not be examined: a script comment QUOTING a bare +# compile line is not a compile. +strip_comments() { + grep -vE '^[[:space:]]*#' +} + EXAMINED=0 # compile invocations examined, all targets VIOLATIONS=0 # examined invocations missing the flag TARGET_EXAMINED=0 # examined within the current target (reset per target) @@ -266,6 +279,28 @@ EOF st_fail=1 fi + # Script-audit shapes (#835): a compile line inside a shell script is + # classified exactly like a dry-run line once comments are stripped. + expect_caught "script compile line, flag dropped" 'src/$f.c' <<'EOF' +gcc -O2 -ffreestanding -fno-stack-protector -U_FORTIFY_SOURCE -Werror=implicit-function-declaration -c "src/$f.c" -o "$BUILD/$f.o" +EOF + expect_clean "script compile line, flag present" <<'EOF' +gcc -O2 -ffreestanding -Werror=implicit-function-declaration -Werror=switch -c "src/$f.c" -o "$BUILD/$f.o" +EOF + + # A comment quoting a bare compile must not be examined at all. + EXAMINED=0; VIOLATIONS=0; TARGET_EXAMINED=0 + strip_comments <<'EOF' | join_continuations > "$st_joined" +# gcc -O2 -c src/vm.c -o vm.o (quoted in a comment; not a compile) + # indented comment: gcc -c src/jit.c +echo hello +EOF + audit_stream "selftest:script-comment" < "$st_joined" + if [ "$EXAMINED" -ne 0 ] || [ "$VIOLATIONS" -ne 0 ]; then + echo "SELFTEST FAILED: commented compile lines were examined ($EXAMINED/$VIOLATIONS)" + st_fail=1 + fi + # `-Werror=switch-enum` is a DIFFERENT warning: it must not satisfy the # check, and the real flag alongside it must. expect_caught "switch-enum is not switch" "src/vm.c" <<'EOF' @@ -338,6 +373,17 @@ for t in $TARGETS; do audit_target "$t" <<< "$(printf '%s\n' "$dry" | join_continuations)" || empty_failed=1 done +# Compile-bearing scripts (#835): same classifier, same zero-line +# assertion, comment lines stripped first. +for s in $SCRIPT_AUDITS; do + if [ ! -f "$s" ]; then + echo "GATE ERROR: script '$s' not found — cannot audit it" + make_failed=1 + continue + fi + audit_target "script:$s" <<< "$(strip_comments < "$s" | join_continuations)" || empty_failed=1 +done + if [ "$make_failed" -ne 0 ] || [ "$empty_failed" -ne 0 ]; then exit 1 fi @@ -346,5 +392,5 @@ if [ "$VIOLATIONS" -gt 0 ]; then echo "werror-switch gate FAILED: $VIOLATIONS of $EXAMINED compile invocations lack $FLAG" exit 1 fi -echo "werror-switch gate OK: all $EXAMINED compile invocations across $(echo $TARGETS | wc -w | tr -d ' ') dry-run targets carry $FLAG" +echo "werror-switch gate OK: all $EXAMINED compile invocations across $(echo $TARGETS | wc -w | tr -d ' ') dry-run targets + $(echo $SCRIPT_AUDITS | wc -w | tr -d ' ') script(s) carry $FLAG" exit 0