fix(0128): foreign cstring returns + conflicting same-symbol bindings
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.
This commit is contained in:
@@ -1,4 +1,41 @@
|
||||
# 0128: `[:0]u8` at FFI boundaries — silent `u8` returns, unresolvable optional
|
||||
# RESOLVED — 0128: `[:0]u8` at FFI boundaries — conflicting symbol views, garbage string returns
|
||||
|
||||
> **RESOLVED** (2026-06-12). Investigation corrected the filing: the
|
||||
> "silent `u8` return" and the "`?[:0]u8` unresolved panic" were BOTH
|
||||
> artifacts of the reproducers binding the C symbol `getenv`, which
|
||||
> std/process.sx already declares as `-> *u8` — the FIRST registration
|
||||
> of a C symbol silently won and every call through the later
|
||||
> declaration was typed by the older signature (`*u8`), cascading into
|
||||
> the panic. `?[:0]u8` itself resolves correctly (it is `?string`).
|
||||
> The two GENUINE defects, both fixed:
|
||||
>
|
||||
> 1. **Conflicting same-symbol redeclaration was silent.**
|
||||
> `dedupeForeignSymbol` (src/ir/lower/decl.zig) now runs at foreign
|
||||
> registration: an EQUAL signature shares the first registration's
|
||||
> FuncId; a CONFLICTING one is diagnosed ("foreign symbol '<s>' is
|
||||
> already bound with a different signature").
|
||||
> 2. **Foreign `-> string` / `-> ?string` returns read garbage.** The
|
||||
> C side returns ONE `char *`; the LLVM signature declared the fat
|
||||
> `{ptr,i64}` (len = register garbage; bus error on use), and
|
||||
> `?string` (24 B struct) was mis-declared SRET — the hidden
|
||||
> out-pointer landed in the C callee's first argument register.
|
||||
> Now: such returns are classified by `cstrRetKind`
|
||||
> (src/ir/emit_llvm.zig), declared as plain `ptr` returns (never
|
||||
> sret), and the call site synthesizes the sx value via
|
||||
> `cstrReturnToSx`: `{ptr, strlen(ptr)}` with the strlen call
|
||||
> branch-guarded (NULL → `{null,0}`), wrapped in `{string, i1}`
|
||||
> with `has = ptr != null` for the optional.
|
||||
>
|
||||
> Regression tests: `examples/1221-ffi-cstring-returns.sx` (plain +
|
||||
> optional non-null via strerror/strsignal + optional NULL via
|
||||
> dlerror) and `examples/1172-diagnostics-foreign-symbol-conflict.sx`
|
||||
> (the getenv conflict); both FAIL on pre-fix master. The extern
|
||||
> dedupe changes IR snapshots (duplicate libc decls collapse), so the
|
||||
> affected `.ir` files were regenerated. Gates: zig build test
|
||||
> 426/426, tests/run_examples.sh 602/602, distribution repo 21/21.
|
||||
> Boundary: comptime-interp (`#run`) foreign calls are untouched, and
|
||||
> indirect (fn-pointer) foreign calls don't synthesize — both can
|
||||
> follow if ever needed.
|
||||
|
||||
## Design contract (Agra, 2026-06-12)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user