diff --git a/examples/issue-0030.sx b/examples/issue-0030.sx deleted file mode 100644 index 08ee85b..0000000 --- a/examples/issue-0030.sx +++ /dev/null @@ -1,57 +0,0 @@ -// issue-0030: Feature — support `extern` global declarations so a global -// declared in one sx source file can be referenced from another without -// parameter threading. -// -// ── Use case from the Metal port ────────────────────────────────────────── -// -// // game/main.sx -// g_metal_gpu : *MetalGPU = null; -// -// // game/chess/pieces.sx -// extern g_metal_gpu : *MetalGPU; -// -// load :: (self: *ChessPieces, path: [:0]u8) { -// ... -// inline if OS == .ios { -// tex := g_metal_gpu.create_texture(w, h, .rgba8, xx pixels); -// } else { -// // GL path -// } -// } -// -// Today, pieces.load takes `has_gpu: bool, gpu: GPU` parameters and -// game/main.sx threads them through. Cross-file `extern` globals would -// let us drop those parameters. -// -// ── Implementation sketch ───────────────────────────────────────────────── -// -// Mirror how foreign function declarations work — declared in one file, -// defined elsewhere, linker resolves. Globals already have first-class -// addresses in the IR; just add an "extern" flag that says "don't emit -// storage, emit a reference." -// -// Files: -// - parser (sx surface syntax for `extern G : T;`) -// - src/ir/lower.zig (record an extern global stub that resolves at -// module-link time) -// - src/ir/emit_llvm.zig (emit an `external` LLVM global) -// -// ── Syntax constraint ───────────────────────────────────────────────────── -// -// `extern G : T;` is a NEW top-level form. Must not clash with: -// - `G :: T;` (type alias) -// - `G : T = ---;` (uninitialized global with explicit type) -// - `G : T;` (does this currently parse as anything?) -// -// The parser MUST reject `extern G : T = expr;` — extern cannot have an -// initializer (the definition lives elsewhere). -// -// ── Caveat ──────────────────────────────────────────────────────────────── -// -// Encourages spaghetti globals. Documentation should steer callers toward -// explicit parameter passing where reasonable. Useful for genuine -// process-singletons (the active GPU, the active platform, etc.) where -// threading them through every call site is more noise than signal. - -#import "modules/std.sx"; -main :: () -> s32 { 0; } diff --git a/issues/0019-import-non-transitive-c-scope.md b/issues/0019-import-non-transitive-c-scope.md new file mode 100644 index 0000000..92f4a1b --- /dev/null +++ b/issues/0019-import-non-transitive-c-scope.md @@ -0,0 +1,27 @@ +# 0019 — `#import` is non-transitive (C-function scope across files) + +> **Status: superseded — kept for reference.** Relocated from the old +> `examples/issue-0019/` fixture during the test-layout migration. The behavior +> it probed (A imports B and C; C must NOT see B's `#foreign` C functions just +> because A imported B) is now covered by the passing test +> `examples/0706-modules-import-non-transitive.sx`. + +## What it probed + +`main` imports both `c_wrapper.sx` (which declares C `#foreign` functions) and +`other.sx`. `other.sx` should *not* gain access to `c_wrapper`'s C functions +transitively — using one should produce the "not visible; #import the module that +declares it" diagnostic. + +- `main_good.sx` — the valid arrangement. +- `main_bad.sx` — the arrangement that must be rejected. +- `c_wrapper.sx`, `other.sx` — the imported modules. + +## Caveat (why it doesn't run as-is) + +The fixture uses **relative** imports (`#import "../modules/std.sx"`), which only +resolve relative to a specific working directory and violate the project's +"always `package:`/module-path imports, never relative" rule. It is not runnable +from the repo root and is not wired into the suite. If revived, rewrite the +imports to the standard `modules/...` form and pin expected output; otherwise it +can be deleted (the scenario is already covered by `0706-modules-import-non-transitive`). diff --git a/examples/issue-0019/c_wrapper.sx b/issues/0019-import-non-transitive-c-scope/c_wrapper.sx similarity index 100% rename from examples/issue-0019/c_wrapper.sx rename to issues/0019-import-non-transitive-c-scope/c_wrapper.sx diff --git a/examples/issue-0019/main_bad.sx b/issues/0019-import-non-transitive-c-scope/main_bad.sx similarity index 100% rename from examples/issue-0019/main_bad.sx rename to issues/0019-import-non-transitive-c-scope/main_bad.sx diff --git a/examples/issue-0019/main_good.sx b/issues/0019-import-non-transitive-c-scope/main_good.sx similarity index 100% rename from examples/issue-0019/main_good.sx rename to issues/0019-import-non-transitive-c-scope/main_good.sx diff --git a/examples/issue-0019/other.sx b/issues/0019-import-non-transitive-c-scope/other.sx similarity index 100% rename from examples/issue-0019/other.sx rename to issues/0019-import-non-transitive-c-scope/other.sx diff --git a/issues/0030-extern-global-declarations.md b/issues/0030-extern-global-declarations.md new file mode 100644 index 0000000..e0c8a72 --- /dev/null +++ b/issues/0030-extern-global-declarations.md @@ -0,0 +1,63 @@ +# 0030 — `extern G : T;` cross-file sx global declarations (feature request) + +> **Status: OPEN feature request** (not a bug). Relocated from the old +> `examples/issue-0030.sx` placeholder during the test-layout migration. Repro: +> `issues/0030-extern-global-declarations.sx` (currently a parse error — the +> syntax doesn't exist yet). + +## Symptom / request + +Support an `extern G : T;` top-level form so a global **defined** in one sx +source file can be **referenced** from another without threading it through +parameters — mirroring how `#foreign` function declarations work (declared in one +place, defined elsewhere, resolved at link time). + +```sx +// game/main.sx +g_metal_gpu : *MetalGPU = null; + +// game/chess/pieces.sx +extern g_metal_gpu : *MetalGPU; // ← parse error today + +load :: (self: *ChessPieces, path: [:0]u8) { + inline if OS == .ios { + tex := g_metal_gpu.create_texture(w, h, .rgba8, xx pixels); + } +} +``` + +Today `pieces.load` takes `has_gpu: bool, gpu: GPU` params and `main.sx` threads +them through; cross-file `extern` globals would drop that ceremony. Distinct from +the existing `name : T #foreign;` form (an *external C* data symbol from +libsystem etc. — see `examples/1205-ffi-foreign-global.sx`); this request is for +sx-defined globals shared across sx modules. + +## Reproduction + +`issues/0030-extern-global-declarations.sx`: + +```sx +#import "modules/std.sx"; +extern g_x : *void; // want: a reference to a global defined elsewhere +main :: () -> s32 { 0; } +``` + +`./zig-out/bin/sx run …` → `error: expected '::', ':=', or ':' after identifier` +(the `extern` keyword/form is unparsed). + +## Implementation sketch + +- **parser** — surface syntax for `extern G : T;`. Must not clash with `G :: T;` + (type alias), `G : T = ---;` (uninitialized global), `G : T;` (typed global). + Reject `extern G : T = expr;` (an extern can't carry an initializer). +- **src/ir/lower.zig** — record an extern-global stub that resolves at + module-link time. +- **src/ir/emit_llvm.zig** — emit an `external` LLVM global (no storage, just a + reference). Globals already have first-class IR addresses; this adds an + "extern" flag meaning "emit a reference, not storage." + +## Caveat + +Encourages process-global state. Steer callers toward explicit parameter passing +where reasonable; reserve for genuine process singletons (active GPU, active +platform) where threading through every call site is more noise than signal. diff --git a/issues/0030-extern-global-declarations.sx b/issues/0030-extern-global-declarations.sx new file mode 100644 index 0000000..810288e --- /dev/null +++ b/issues/0030-extern-global-declarations.sx @@ -0,0 +1,14 @@ +// Repro for issue 0030 (OPEN feature request): cross-file sx `extern` globals. +// Want: `extern G : T;` declares a reference to a global defined in another sx +// file (resolved at link time), mirroring `#foreign` functions. Today this is a +// parse error — the form doesn't exist. Distinct from `name : T #foreign;` +// (an external C data symbol; see examples/1205-ffi-foreign-global.sx). +// +// Expected (once implemented): parses; `g_x` resolves to a global defined +// elsewhere. Actual: error "expected '::', ':=', or ':' after identifier". + +#import "modules/std.sx"; + +extern g_x : *void; + +main :: () -> s32 { 0; }