diff --git a/issues/0019-import-non-transitive-c-scope.md b/issues/0019-import-non-transitive-c-scope.md index 4d31455f..edfbe966 100644 --- a/issues/0019-import-non-transitive-c-scope.md +++ b/issues/0019-import-non-transitive-c-scope.md @@ -1,5 +1,13 @@ # 0019 — `#import` is non-transitive (C-function scope across files) +> **RESOLVED.** The `.md`'s own repro is non-runnable (relative `../modules/std.sx` +> imports) and superseded; the scenario — A importing B does NOT transitively expose +> a third module's top-level names — is enforced by the non-transitive `#import` +> visibility check in `lowerCall` (src/ir/lower/call.zig:106-118, gated on +> `isNameVisible`, defined in src/ir/lower/decl.zig:2502), with the const-reference +> path mirrored in src/ir/lower/expr.zig. Covered by the passing regression test +> `examples/0706-modules-import-non-transitive.sx`. + > **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 `extern` C functions just diff --git a/issues/0042-const-decl-type-aliases-not-resolved-as-identifier.md b/issues/0042-const-decl-type-aliases-not-resolved-as-identifier.md index 9b36fd80..12fe3acf 100644 --- a/issues/0042-const-decl-type-aliases-not-resolved-as-identifier.md +++ b/issues/0042-const-decl-type-aliases-not-resolved-as-identifier.md @@ -1,5 +1,10 @@ # issue-0042 — Const-decl type aliases (`MyInt :: i32;`) silently return `.i64` from `size_of` / `align_of` +> **RESOLVED.** Root cause: the bare-name type-arg resolver consulted only `findByName`, falling back to `.i64` (8 bytes) for const-decl aliases (`MyInt :: i32;`) recorded in the alias map — silently mis-sizing any non-8-byte alias. +> Fix: the nominal-leaf resolver now consults the alias maps before falling back — `resolveNominalLeaf` → `selectNominalLeaf` (`src/ir/lower/decl.zig`) returns `.resolved` with the alias's real `TypeId` from `program_index.type_aliases_by_source` (own-author const_decl branch ~L1820 / unwired branch ~L1801); aliases are registered via `putTypeAlias` (`decl.zig:560`). +> The old `resolveTypeArg` `.identifier` arm in `lower.zig` is gone — the source-keyed `selectNominalLeaf` superseded it. +> Covering regression test: `examples/0116-types-type-alias-size-align.sx` (alias, chain, and struct-name alias through `size_of`/`align_of`). + **FIXED.** `MyInt :: i32; size_of(MyInt)` now returns `4` correctly. The `resolveTypeArg` `.identifier` branch consults `type_alias_map` before falling through. The fix landed diff --git a/issues/0043-lazy-lower-loses-runtime-class-method-dispatch.md b/issues/0043-lazy-lower-loses-runtime-class-method-dispatch.md index d80c0890..5c72f1dc 100644 --- a/issues/0043-lazy-lower-loses-runtime-class-method-dispatch.md +++ b/issues/0043-lazy-lower-loses-runtime-class-method-dispatch.md @@ -1,5 +1,9 @@ # issue-0043: lazy-lowered function bodies don't resolve runtime-class method dispatch +> **RESOLVED.** Root cause: chained runtime-class dispatch (`Cls.alloc().init...(x)`) collapsed the inner call's return type, so the outer `.method(...)` couldn't find the receiver's `#objc_class` declaration — and on the lazy-lower path (inside an `inline if OS == .ios` branch) the runtime-class map was unavailable. +> Fix: the runtime-class instance/static dispatch now resolves each call's declared return type via `resolveRuntimeMethodReturnType` against the runtime_class_map (`src/ir/calls.zig`, runtime-class branches ~L244-269 / L385-398), and that map lives on the shared `ProgramIndex` (`src/ir/program_index.zig:668`) so it is equally visible to eager and lazy lowering (`lazyLowerFunction`, `src/ir/lower/decl.zig:2508`). +> Covering regression: `examples/1306-ffi-objc-runtime-class-chained-dispatch.sx` (header cites issue 0043) exercises both `*ClassName` and `*Self` chained shapes and passes. + **FIXED.** The original repro (`sx build --target ios-sim issue-0043.sx` with a `UIWindow.alloc().initWithWindowScene(scene)` call inside an diff --git a/issues/0044-sx-defined-objc-class-method-self-param-must-be-named-self.md b/issues/0044-sx-defined-objc-class-method-self-param-must-be-named-self.md index b093d978..8fee5091 100644 --- a/issues/0044-sx-defined-objc-class-method-self-param-must-be-named-self.md +++ b/issues/0044-sx-defined-objc-class-method-self-param-must-be-named-self.md @@ -1,4 +1,12 @@ **FIXED.** Root cause was NOT the parameter name; the original `this` + +> **RESOLVED.** A runtime-class (`#objc_class`) UFCS call site had no entry in +> `resolveCallParamTypes`, so per-arg `target_type` leaked the enclosing method's +> return type (e.g. BOOL→i8), truncating `xx this` to its low byte at the receiver. +> Fixed by adding the `runtime_class_map.get(sname)` + `findRuntimeMethodInChain` +> path in `resolveCallParamTypes` (now `src/ir/lower/call.zig`, lines 2533-2556), +> which threads each runtime-class method's declared param types. Regression test: +> `examples/1321-ffi-objc-defined-class-method-self.sx`. rename surfaced an unrelated `target_type` leak in `resolveCallParamTypes`. See "Root cause + fix" below. diff --git a/issues/0045-pack-fn-call-llvm-verifier-failure.md b/issues/0045-pack-fn-call-llvm-verifier-failure.md index acf65e69..d750c6db 100644 --- a/issues/0045-pack-fn-call-llvm-verifier-failure.md +++ b/issues/0045-pack-fn-call-llvm-verifier-failure.md @@ -1,4 +1,13 @@ **FIXED.** `lowerComptimeCall` now allocates a result slot when + +> **RESOLVED.** A comptime/pack-fn with a non-void return type and a block body +> containing `return X;` previously emitted a `ret` in the middle of the caller's +> basic block (LLVM verifier: "Terminator found in the middle of a basic block"). +> Fixed in `lowerComptimeCall` (src/ir/lower/comptime.zig:822-869): when the body +> has a `return`, it allocates a result slot + shared `ct.ret_done` block and sets +> `inline_return_target`, so `lowerReturn` (src/ir/lower/stmt.zig:442) stores into +> the slot and branches to `ret_done` instead of emitting an inline `ret`. +> Regression test: examples/0525-packs-pack-fn-comptime-return.sx (prints "42"). the body contains a `return` statement and reroutes `lowerReturn` to store into it instead of emitting `ret` into the caller's basic block. Regression test: diff --git a/issues/0046-comptime-fn-nested-print-with-return.md b/issues/0046-comptime-fn-nested-print-with-return.md index 4e811f37..1305294a 100644 --- a/issues/0046-comptime-fn-nested-print-with-return.md +++ b/issues/0046-comptime-fn-nested-print-with-return.md @@ -1,4 +1,9 @@ **FIXED.** `createComptimeFunction` now saves/restores the + +> **RESOLVED.** A nested comptime call inside a comptime fn body that also had a `return X;` lowered the wrapper fn built by `createComptimeFunction` while it still inherited the outer caller's `inline_return_target` (and pack / comptime-param bindings), so the interp stored into a slot belonging to a different basic block — null-pointer store at `storeAtRawPtr`. +> Fixed in `src/ir/lower/comptime.zig:createComptimeFunctionWithPrelude` (which `createComptimeFunction` delegates to): it now snapshots-and-clears `inline_return_target`, `pack_arg_nodes`, `pack_param_count`, `pack_arg_types`, `comptime_param_nodes`, `block_terminated`, `target_type`, and `func_defer_base`, restoring them via `defer` so the wrapper runs in isolation. The same protection is generalized as `Lowering.FnBodyReentry` (`src/ir/lower.zig`). +> Face 2 (pack-fn `..$args`) was fixed incidentally when pack-fn calls moved off the inline-return path onto the mono path. +> Covered by regression test `examples/0607-comptime-nested-comptime-return.sx`. outer `lowerComptimeCall`'s state — specifically `inline_return_target`, `pack_arg_nodes`, `pack_param_count`, `pack_arg_types`, `comptime_param_nodes`, `block_terminated`, diff --git a/issues/0047-run-output-on-stderr-runtime-on-stdout.md b/issues/0047-run-output-on-stderr-runtime-on-stdout.md index b0c2fcd9..5e60fe66 100644 --- a/issues/0047-run-output-on-stderr-runtime-on-stdout.md +++ b/issues/0047-run-output-on-stderr-runtime-on-stdout.md @@ -1,4 +1,8 @@ **FIXED** in commit `0119c9c`. Both `#run` output and the + +> **RESOLVED.** `#run` (compile-time) `print` output landed on stderr while runtime `print` went to stdout, so separated-stream captures split user output across two fds. +> Fixed by routing both compile-time `print` output and the phase delimiter to fd 1 via direct libc `write`: the `--- build done ---` marker now goes through `std.c.write(1, ...)` at `src/main.zig:333` (cited to issue-0047), and `#run` `out`/print output writes straight to fd 1 on the comptime VM path (see `src/ir/comptime_vm.zig:1083` and the "now-direct libc write" notes in `src/ir/emit_llvm.zig:2967`). +> Covered by the regression test `examples/0600-comptime-run.sx`, whose expected `.stdout` carries the `#run` output, the delimiter, and runtime output together with an empty `.stderr`. `--- build done ---` delimiter now write to fd 1 (stdout) via `std.c.write` from `core.flushInterpOutput` and main.zig's delimiter site. Test runner uses `2>&1` so snapshots are diff --git a/issues/0048-bare-pack-args-slice-loses-len-across-call.md b/issues/0048-bare-pack-args-slice-loses-len-across-call.md index a04196e5..6c2eb64e 100644 --- a/issues/0048-bare-pack-args-slice-loses-len-across-call.md +++ b/issues/0048-bare-pack-args-slice-loses-len-across-call.md @@ -1,5 +1,15 @@ # 0048 — bare `$args` slice loses `.len` (reads 0) when passed across a call +> **RESOLVED.** Root cause: a callee (`walk`/`describe`) lazily lowered *inside* a +> pack-fn mono inherited the active pack's `pack_param_count`, so the +> `.len` intercept in `lowerFieldAccess` constant-folded `args.len` +> to the outer pack's arity — every shape's cross-call read returned that one +> baked constant (originally observed as 0). Fix: `FnBodyReentry.enter` in +> `src/ir/lower.zig` (used by `lowerFunctionBodyInto` / `lazyLowerFunction` in +> `src/ir/lower/decl.zig`) now nulls `pack_param_count` (and the sibling +> `pack_arg_nodes` / `pack_arg_types`) for the nested body and restores them on +> exit. Covered by regression test `examples/0522-packs-pack-bare-args-cross-call.sx`. + ## Symptom Bare `$args` evaluated inside a pack-fn body has the correct `.len` diff --git a/issues/0049-new-form-variadic-cross-module-llvm-emit-crash.md b/issues/0049-new-form-variadic-cross-module-llvm-emit-crash.md index 7d04107a..fe33fc12 100644 --- a/issues/0049-new-form-variadic-cross-module-llvm-emit-crash.md +++ b/issues/0049-new-form-variadic-cross-module-llvm-emit-crash.md @@ -1,5 +1,9 @@ # 0049 — new-form variadic `..name: []Type` defined in an imported module crashes LLVM emit +> **RESOLVED.** Root cause: the new-form variadic `..name: []T` had its already-sliced declared type double-wrapped to `[][]T` (helpers treated it like legacy `name: ..T` and added a slice level), so the callee's stored param shape mismatched the call-site's `[N x T]` marshalling and emitted null/undef Refs that crashed `LLVMBuildExtractValue` in `emitStrCmp`. +> Fix: `resolveParamType` (src/ir/lower.zig:642) now returns `declared_ty` as-is when it is already a slice instead of re-wrapping, and the companion `packVariadicCallArgs` (src/ir/lower/pack.zig:298) unwraps the new-form `[]T` back to element `T` for per-element packing so both surface forms converge. +> Covered by regression test `examples/0523-packs-new-form-variadic-cross-module.sx` (pinned, exit 0), which calls the new-form stdlib `path_join` across the import boundary. + ## Symptom A pack-fn declared with the **new** variadic syntax diff --git a/issues/0050-monomorphizeFunction-pack-state-leak.md b/issues/0050-monomorphizeFunction-pack-state-leak.md index 61019018..19e7d6c5 100644 --- a/issues/0050-monomorphizeFunction-pack-state-leak.md +++ b/issues/0050-monomorphizeFunction-pack-state-leak.md @@ -1,5 +1,9 @@ # 0050 — `monomorphizeFunction` leaks outer pack-fn state into the mono body +> **RESOLVED.** Root cause: `monomorphizeFunction` saved/nulled `type_bindings`/`scope`/builder state but left the outer pack-fn maps live, so a generic callee with an `args`-named param had its `args.len` constant-folded (via `lowerFieldAccess`'s `.len` intercept) to the first mono's arity and baked into the cached IR. +> Fix: `monomorphizeFunction` in `src/ir/lower/generic.zig` now saves+nulls+defer-restores `pack_arg_nodes` / `pack_param_count` / `pack_arg_types` / `inline_return_target` (lines 51-64), mirroring the `lazyLowerFunction` isolation from issue-0048. +> Covered by regression test `examples/0524-packs-generic-fn-pack-state-leak.sx` (probes print 0/1/2/4). + ## Symptom A generic function (one with `$T: Type` type params) called from diff --git a/issues/0051-macos-bundle-assets-cwd-relative.md b/issues/0051-macos-bundle-assets-cwd-relative.md index 5129c6eb..c0487420 100644 --- a/issues/0051-macos-bundle-assets-cwd-relative.md +++ b/issues/0051-macos-bundle-assets-cwd-relative.md @@ -1,5 +1,14 @@ **FIXED.** Two parts, both needed for Finder/`open` to work: +> **RESOLVED.** A bundled macOS `.app` launched via Finder/`open` starts with +> CWD=`/`, so CWD-relative asset loads (`read_file_bytes("assets/...")`) missed +> and the app crashed loading fonts/textures. Fixed in `library/modules/platform/sdl3.sx`: +> `SdlPlatform.init` now calls `sdl_chdir_to_bundle()`, which `chdir`s to +> `SDL_GetBasePath()` only when that path lives inside a `.app` (gated on `OS == .macos`), +> mirroring uikit.sx's iOS precedent; the `sx run` dev flow and wasm are left untouched. +> This is a platform-library fix (no `src/**` compiler change); no standalone regression +> test exists since it requires a bundled GUI `.app` launched via Finder. + 1. **cwd-relative assets** — `SdlPlatform.init` now chdir's to `SDL_GetBasePath()` when running from inside a `.app` (sx commit `b31fbae`, `library/modules/platform/sdl3.sx`), mirroring uikit.sx's iOS diff --git a/issues/0052-slice-of-protocol-variadic-not-erased.md b/issues/0052-slice-of-protocol-variadic-not-erased.md index 28165a69..62f86f5a 100644 --- a/issues/0052-slice-of-protocol-variadic-not-erased.md +++ b/issues/0052-slice-of-protocol-variadic-not-erased.md @@ -1,4 +1,12 @@ **FIXED.** `packVariadicCallArgs` ([src/ir/lower.zig](../src/ir/lower.zig)) + +> **RESOLVED.** A slice-of-protocol variadic `..xs: []P` stored each raw 8-byte +> concrete arg into a 16-byte protocol-sized array slot, yielding garbage +> `{ctx, vtable}` values and a Bus error on `xs[i].method()` dispatch. +> Fixed in `packVariadicCallArgs` (`src/ir/lower/pack.zig`): it now computes +> `elem_is_protocol` and, per arg, `buildProtocolErasure`-erases each concrete +> value into a real protocol value before storing. +> Regression test: `examples/0535-packs-slice-of-protocol-variadic.sx`. now detects a protocol element type and `xx`-erases each arg into the `[N]P` array via `buildProtocolErasure`, instead of storing the raw concrete value. Regression: [examples/202-slice-of-protocol-variadic.sx](../examples/202-slice-of-protocol-variadic.sx). diff --git a/issues/0053-comptime-pack-spread-into-any-slice.md b/issues/0053-comptime-pack-spread-into-any-slice.md index e5af920b..a1c119ab 100644 --- a/issues/0053-comptime-pack-spread-into-any-slice.md +++ b/issues/0053-comptime-pack-spread-into-any-slice.md @@ -1,4 +1,11 @@ **FIXED** via the `xx ` bridge (the preferred fix below), not by changing + +> **RESOLVED.** Spreading a comptime pack into a `[]Any`/`[]P` parameter via `xx args` previously +> hit the pack-as-value diagnostic ("pack has no runtime value") before `xx` was considered, so no +> slice was ever materialized. Fixed by intercepting `xx ` with a slice target in the +> `unary_op .xx` arm of `lowerExpr` (`src/ir/lower/expr.zig`) *before* lowering the operand, calling +> `lowerPackToSlice` (`src/ir/lower/pack.zig:167`) to box/erase each element into a runtime `[N]elem` → `[]elem`. +> Covered by regression test `examples/0537-packs-pack-xx-to-slice.sx`. the `..args` spread. `xx args` with a slice target now materializes the pack into a runtime `[]Any`/`[]P` — see [examples/204-pack-xx-to-slice.sx](../examples/204-pack-xx-to-slice.sx). `lowerXX`/the unary-op arm intercepts `xx ` before the pack-as-value diff --git a/issues/0054-generic-struct-to-param-protocol-erasure.md b/issues/0054-generic-struct-to-param-protocol-erasure.md index 8c57a6d3..1d8603a8 100644 --- a/issues/0054-generic-struct-to-param-protocol-erasure.md +++ b/issues/0054-generic-struct-to-param-protocol-erasure.md @@ -1,4 +1,13 @@ **FIXED** (`1f6e27d`, `examples/212`). Two root causes: + +> **RESOLVED.** A generic-struct instance erased to a parameterized protocol +> (`xx c` : `Combined__i64_i64` → `VL(i64)`) trapped at dispatch because the +> erasure thunk's vtable slot was never bound to the monomorphized instance +> method. Fixed by `instantiateGenericStruct` binding the template name to the +> concrete instance + recording `struct_instance_bindings` (src/ir/lower/generic.zig:1746,1753), +> and `createProtocolThunk` monomorphizing the generic-struct instance method +> for those bindings (src/ir/lower/protocol.zig:createProtocolThunk, ~line 349). +> Covered by `examples/0414-protocols-generic-struct-protocol-erase.sx`. 1. `instantiateGenericStruct` now binds the template name to the concrete instance (`tb.put(tmpl.name, id)`), so an impl method `self: *Combined` resolves `self.field` to the instance, not the 0-field generic stub. (This diff --git a/issues/0055-binary-arith-no-operand-type-check.md b/issues/0055-binary-arith-no-operand-type-check.md index 6b2d8dd9..0fe24160 100644 --- a/issues/0055-binary-arith-no-operand-type-check.md +++ b/issues/0055-binary-arith-no-operand-type-check.md @@ -1,5 +1,14 @@ # 0055 — binary arithmetic accepts mismatched operand types (`i64 + string`) +> **RESOLVED.** Scalar binary ops derived the result type from the LHS and never +> checked the RHS, so `i64 + string` lowered as `add : i64` and reinterpreted the +> string's bytes (garbage); ordering/bitwise had the same hole. Fixed in +> `lowerBinaryOp` (src/ir/lower/expr.zig:2520), which now gates arithmetic / +> ordering / bitwise-shift groups through `isArithOperand` / `isOrderingOperand` / +> `isBitwiseOperand` (src/ir/lower.zig:1408-1437) — `.unresolved` passes through, +> a concretely incompatible operand emits `cannot apply '' …` and returns a +> placeholder sentinel. Covered by `examples/1106-diagnostics-binop-operand-type-check.sx`. + **FIXED** (`examples/214-binop-operand-type-check.sx`). `lowerBinaryOp` in [src/ir/lower.zig](../src/ir/lower.zig) now checks operand-type compatibility for every scalar binary-op group before emitting, via three diff --git a/issues/0056-param-impl-not-deduped-across-diamond-import.md b/issues/0056-param-impl-not-deduped-across-diamond-import.md index 818b59e0..5115aef9 100644 --- a/issues/0056-param-impl-not-deduped-across-diamond-import.md +++ b/issues/0056-param-impl-not-deduped-across-diamond-import.md @@ -1,5 +1,13 @@ # 0056 — parameterised-protocol impl not deduped across a diamond import +> **RESOLVED.** An anonymous `impl_block` has no `declName`, so the flat decl +> list's name-keyed dedup let the same cached AST node (shared by both diamond +> paths) be appended twice — `registerParamImpl` in `src/ir/lower.zig` then saw +> two same-module entries and raised "duplicate impl 'Into' for source 'i64'". +> Fixed in `ResolvedModule.mergeFlat` (and the directory-import merge loop) in +> `src/imports.zig`, which now also dedup by node identity via a `seen_nodes: +> AutoHashMap(*Node, void)`. Regression test: `examples/0709-modules-issue-0056-diamond-param-impl.sx`. + **FIXED** (`examples/issue-0056-diamond-param-impl.sx`, helpers in `examples/issue-0056/`). The flat decl list in [src/imports.zig](../src/imports.zig) now dedups by **node identity** as well diff --git a/issues/0131-protocol-call-extra-args-silently-dropped.md b/issues/0131-protocol-call-extra-args-silently-dropped.md index 05871db3..bb89c369 100644 --- a/issues/0131-protocol-call-extra-args-silently-dropped.md +++ b/issues/0131-protocol-call-extra-args-silently-dropped.md @@ -1,5 +1,12 @@ # 0131 — protocol method call with extra arguments compiles and silently drops them +> **RESOLVED.** Protocol-dispatch lowering matched the call args against the +> method's parameter list without an arity check, so extra trailing args were +> silently truncated (and missing args left the thunk reading garbage). Fixed in +> `src/ir/lower/protocol.zig:emitProtocolDispatch` (lines 531-538), which now +> exact-arity-checks and emits `'{name}' expects N argument(s), but M were given` +> at the call span. Regression pinned by `examples/1634-protocol-call-arity`. + ## Symptom Calling a protocol method with MORE arguments than the protocol