issue-0037: @foreign_global from a helper function lowers to undef

Repro found while writing PLAN-FFI step 0.10.

In a single file:

  __stdinp : *void #foreign;
  stdinp_addr :: () -> u64 { xx @__stdinp; }
  main :: () -> s32 {
      a : u64 = xx @__stdinp;     // a = real symbol address
      b := stdinp_addr();         // b = 0
      ...
  }

The emitted IR for the helper is `ret i64 undef`, suggesting the
`address_of(identifier=__stdinp)` branch in lower.zig (~line 1719)
doesn't see `__stdinp` in `global_names` at the moment the helper's
body is being lowered — even though the same lookup succeeds inside
main's body in the same compilation unit.

Likely cause: lazy-body lowering ordering vs. the pass that
registers extern global decls into `global_names`. Worth verifying
which before fixing — could also be per-function scoping of the
map. Phase 1 of the FFI plan doesn't depend on this, so it stays
filed as an open issue and gets addressed when convenient (or when
sx-side `extern` cross-file globals from issue-0030 land and need
the same lookup to work everywhere).
This commit is contained in:
agra
2026-05-19 12:05:55 +03:00
parent efc482a055
commit 161254f5bb
3 changed files with 51 additions and 0 deletions

47
examples/issue-0037.sx Normal file
View File

@@ -0,0 +1,47 @@
// `@__stdinp` (address-of a `#foreign` global) lowers to `undef` /
// 0 when accessed from inside a non-main function body. From `main`
// directly it works:
//
// single file:
// __stdinp : *void #foreign;
// stdinp_addr :: () -> u64 { xx @__stdinp; }
// main :: () -> s32 {
// a : u64 = xx @__stdinp; // a = <real symbol address>
// b := stdinp_addr(); // b = 0
// ...
// }
//
// The emitted IR for the helper is just `ret i64 undef`, suggesting
// the `address_of(identifier=__stdinp)` branch in `lower.zig`
// (~line 1719) doesn't see `__stdinp` in `global_names` at the time
// the helper's body is being lowered — even though the same lookup
// succeeds inside main.
//
// Likely cause: lazy/lazy-deferred body lowering ordering vs. the
// pass that registers extern global decls into `global_names`. Or
// global_names is scoped per-function (?). Worth verifying which
// before fixing.
//
// Expected when fixed: the helper returns the same address as the
// direct read, prints `eq = true`. Today: prints `eq = false`.
//
// Filed during PLAN-FFI step 0.10 (cross-file `#foreign` global —
// the cross-file dimension surfaced the bug, but it reproduces in
// a single file too).
#import "modules/std.sx";
__stdinp : *void #foreign;
stdinp_addr_via_helper :: () -> u64 {
xx @__stdinp;
}
main :: () -> s32 {
direct : u64 = xx @__stdinp;
via_helper := stdinp_addr_via_helper();
print("direct non-null = {}\n", direct != 0);
print("helper non-null = {}\n", via_helper != 0);
print("eq = {}\n", direct == via_helper);
0;
}