The dup-C-symbol diagnostic (decl.zig) and the resolveFuncByName panic (call.zig)
now say 'extern symbol' instead of 'foreign symbol' — the keyword-neutral internal
wording catches up to the extern-only surface. Intentional snapshot regen of 1172
(the only assertion of this message). Suite green (646/444).
Migrate the two #foreign-bearing diagnostic tests whose assertions survive the
cutover, with INTENTIONAL snapshot regens (reviewed):
- 1172 (foreign-symbol-conflict): decl '#foreign libc "getenv"' → 'extern libc
"getenv"'. Still tests the dup-C-symbol conflict; the 'foreign symbol already
bound' message is the keyword-neutral INTERNAL wording (renamed to 'extern symbol'
in Phase 9.1), so it persists — only the echoed source line + caret moved.
- 1228 (non-transitive C-import visibility): its identity was the #foreign≡extern
equivalence lock, now historical (structural via the A→B gate + unified AST). The
identifier 'c_foreign_abs' itself contained 'foreign' (would fail the Phase 9.4
gate), so converted c.sx/b.sx/main to two foreign-free extern symbols
(c_abs_one/c_abs_two); still pins per-symbol non-transitive visibility.
Reverted the orthogonal 0→1-byte empty-stdout normalization on 1228/1231 (known
writeGolden idempotency quirk, not a behavior change). Suite green (647/444).
Two genuine defects behind the 0128 filing (whose original repros were
both poisoned by binding getenv, which std already declares -> *u8):
1. Re-declaring a C symbol was silent first-wins: every call through
the later declaration was typed by the older signature. Foreign
registration now dedupes — equal signatures share one FuncId,
conflicting ones are diagnosed.
2. Foreign -> string / -> ?string returns read garbage: C returns one
char*, but the LLVM signature declared the fat {ptr,i64} (len =
register garbage), and ?string was mis-declared SRET (the hidden
out-pointer landed in the callee's first arg register). cstrRetKind
now classifies such returns, declares them as plain ptr (never
sret), and the call site synthesizes {ptr, strlen} via a
branch-guarded strlen (NULL -> {null,0} / optional null), wrapping
{string, i1} for ?string.
?[:0]u8 itself resolves fine (it is ?string); the spelling works in
return, param, local, and alias positions.
Regression: examples/1221 (plain + optional non-null + NULL paths) and
examples/1172 (conflict diagnostic); both FAIL pre-fix. The extern
dedupe collapses duplicate libc decls, so affected .ir snapshots were
regenerated. zig build test 426/426; run_examples 602/602;
distribution suite 21/21.