From 161254f5bbfbc7d340caacd603deb2ea7989850d Mon Sep 17 00:00:00 2001 From: agra Date: Tue, 19 May 2026 12:05:55 +0300 Subject: [PATCH] issue-0037: @foreign_global from a helper function lowers to undef MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- examples/issue-0037.sx | 47 ++++++++++++++++++++++++++++++++++ tests/expected/issue-0037.exit | 1 + tests/expected/issue-0037.txt | 3 +++ 3 files changed, 51 insertions(+) create mode 100644 examples/issue-0037.sx create mode 100644 tests/expected/issue-0037.exit create mode 100644 tests/expected/issue-0037.txt diff --git a/examples/issue-0037.sx b/examples/issue-0037.sx new file mode 100644 index 0000000..5701859 --- /dev/null +++ b/examples/issue-0037.sx @@ -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 = +// 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; +} diff --git a/tests/expected/issue-0037.exit b/tests/expected/issue-0037.exit new file mode 100644 index 0000000..573541a --- /dev/null +++ b/tests/expected/issue-0037.exit @@ -0,0 +1 @@ +0 diff --git a/tests/expected/issue-0037.txt b/tests/expected/issue-0037.txt new file mode 100644 index 0000000..eca5a14 --- /dev/null +++ b/tests/expected/issue-0037.txt @@ -0,0 +1,3 @@ +direct non-null = true +helper non-null = false +eq = false