P5.7 Step E: fix issue 0141 (reject silent [*]T -> []T coercion); land regression

The 0141 repro relied on a silent-wrong coercion: passing List.items (a
[*]T many-pointer, no length) to a []T parameter passed the bare 8-byte
pointer into a 16-byte {ptr,len} slot — garbage .len, at comptime a segfault
in the VM slice decoder (decodeMemberSlice), at runtime an LLVM verify failure.

Fix (root cause): classify [*]T -> []T as many_to_slice_reject in
conversions.zig and emit a build-gating diagnostic in coerce.zig telling the
user to slice with a length (ptr[0..len]). Guard runComptimeTypeFunc to skip
VM eval once diagnostics.hasErrors() — a type-fn body that failed coercion
holds malformed comptime data (a real host Addr) that would fault the VM's
Ref-level guards.

Land the corrected feature as examples/0640 (List-grown comptime enum via
vs.items[0..vs.len] -> green=7) and the rejection as
examples/1183-diagnostics-many-pointer-to-slice-rejected. Mark issue 0141
RESOLVED.

708/0 corpus + 476/476 unit.
This commit is contained in:
agra
2026-06-19 20:40:21 +03:00
parent 7b8be86834
commit 61f5700a36
13 changed files with 147 additions and 34 deletions

View File

@@ -1,5 +1,52 @@
# 0141 — `List(T).append` at comptime (in a type-construction `::`) bails
> **Status: RESOLVED (2026-06-19).**
>
> **Root cause (final, after the legacy interp was deleted and the comptime VM
> became the sole evaluator):** the original repro passes `vs.items` — a bare
> many-pointer `[*]EnumVariant` — where a slice `[]EnumVariant` is expected. A
> `[*]T` carries NO length, so there was no valid implicit coercion to `[]T`; the
> classifier fell through to `.none` and passed the bare 8-byte data pointer
> UNCHANGED where a 16-byte `{ptr,len}` fat pointer was expected. The callee then
> read the slice header off the wrong bytes. At RUNTIME this failed LLVM
> verification (8-byte ptr into a `{ptr,len}` param slot); at COMPTIME the VM read
> `.ptr`/`.len` from adjacent bytes and dereferenced a garbage data pointer (a
> comptime `Addr` is a REAL host pointer), faulting at address `0x646572` ("red")
> inside `comptime_vm.decodeMemberSlice`. This was a silent-wrong-coercion (a
> CLAUDE.md REJECTED PATTERN), not a List/allocator/slot_ptr problem — the
> List growth, the comptime allocator, and `define`/`decodeMemberSlice` are all
> correct (the prior multi-layer analyses below predate the VM rewrite and are
> SUPERSEDED).
>
> **Fix:**
> 1. `src/ir/conversions.zig` — `CoercionResolver.classify` now classifies
> `[*]T → []T` as the new `.many_to_slice_reject` plan (added to `CoercionPlan`).
> 2. `src/ir/lower/coerce.zig` — `coerceMode`'s new `.many_to_slice_reject` arm
> emits a build-gating diagnostic: *"a many-pointer '[*]T' does not coerce to a
> slice '[]T' implicitly (it carries no length) — slice it with a length:
> ptr[0..len]"*.
> 3. `src/ir/lower/comptime.zig` — `runComptimeTypeFunc` now skips the VM eval when
> `diagnostics.hasErrors()` is already set. A type-fn whose body failed coercion
> holds malformed IR; running the VM on it would deref garbage (the VM's
> bail-not-crash guards catch malformed *Refs*, not malformed comptime *data*).
> The user's real diagnostic is already on the list, so the build aborts cleanly
> instead of segfaulting.
>
> **Correct spelling** for the List-grown form is `vs.items[0..vs.len]` (a
> subslice that supplies the length), exactly like the array form's `dirs[0..2]`
> in `examples/0621`.
>
> **Regression tests:**
> - `examples/0640-comptime-list-grown-variant-define.sx` — the List-grown enum
> construction with the correct `vs.items[0..vs.len]` spelling → prints
> `green=7`, exit 0 (the feature this issue tracked, now working on the VM).
> - `examples/1183-diagnostics-many-pointer-to-slice-rejected.sx` — the bare
> `[*]T → []T` mis-coercion now produces the clean diagnostic (exit 1), no crash.
>
> ---
>
> *Original (now-stale) writeup follows — kept for history.*
>
> **Status: OPEN — deferred enhancement, NOT a blocker.** Building a comptime
> variant/field list with an array-literal local already works
> (`examples/0620`/`0624`); only the `List`-grown form fails. Filed to record the

View File

@@ -1,34 +0,0 @@
// Repro for issue 0141 — a `List(T)` grown at comptime inside a type-construction
// `::` const bails. `make_enum` assembles its variant list in a `List`, appends,
// then mints from `vs.items`. The append fails at comptime ("struct_get: base has
// no fields") even though the identical code works at runtime AND via `#run`.
//
// Expected: `Color` constructs from the List-built variant list and `.green(7)`
// matches (prints "green=7"), exit 0 — the same as the array-literal form
// (examples/0620), which already works.
#import "modules/std.sx";
#import "modules/std/meta.sx";
make_enum :: (name: string, variants: []EnumVariant) -> Type {
return define(declare(name), .enum(.{ variants = variants }));
}
build_color :: () -> Type {
vs : List(EnumVariant) = .{};
vs.append(EnumVariant.{ name = "red", payload = void });
vs.append(EnumVariant.{ name = "green", payload = i64 });
vs.append(EnumVariant.{ name = "blue", payload = void });
return make_enum("Color", vs.items);
}
Color :: build_color();
main :: () -> i32 {
c : Color = .green(7);
if c == {
case .red: { print("red\n"); }
case .green: (v) { print("green={}\n", v); }
case .blue: { print("blue\n"); }
}
return 0;
}