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:
@@ -43,6 +43,7 @@ pub const CoercionResolver = struct {
|
||||
widen, // same kind, dst wider
|
||||
narrow, // same kind, dst narrower
|
||||
array_to_slice, // [N]T → []T (materialize backing storage + header)
|
||||
many_to_slice_reject, // [*]T → []T (no length — needs ptr[0..len]; diagnostic)
|
||||
string_to_cstring, // literal-only implicit; other strings need to_cstring
|
||||
cstring_to_string_reject, // explicit from_cstring required (diagnostic)
|
||||
none, // nothing applies — pass the value through
|
||||
@@ -82,6 +83,15 @@ pub const CoercionResolver = struct {
|
||||
if (si == .array and di == .slice and si.array.element == di.slice.element) {
|
||||
return .array_to_slice;
|
||||
}
|
||||
// `[*]T → []T`: a many-pointer carries NO length, so it cannot form a
|
||||
// `{ptr,len}` slice header implicitly. Silently passing the bare 8-byte
|
||||
// pointer where a 16-byte fat pointer is expected corrupts the callee's
|
||||
// view (garbage `.len`, mis-aligned reads) — at comptime it segfaults
|
||||
// (issue 0141), at runtime it fails LLVM verification. Reject loudly so
|
||||
// the user supplies the length via `ptr[0..len]`.
|
||||
if (si == .many_pointer and di == .slice) {
|
||||
return .many_to_slice_reject;
|
||||
}
|
||||
}
|
||||
|
||||
// Optional → Concrete unwrap (narrowing).
|
||||
|
||||
@@ -730,6 +730,15 @@ pub fn coerceMode(self: *Lowering, val: Ref, src_ty: TypeId, dst_ty: TypeId, mod
|
||||
.narrow => return self.builder.emit(.{ .narrow = .{ .operand = val, .from = src_ty, .to = dst_ty } }, dst_ty),
|
||||
.widen => return self.builder.emit(.{ .widen = .{ .operand = val, .from = src_ty, .to = dst_ty } }, dst_ty),
|
||||
.array_to_slice => return self.builder.emit(.{ .array_to_slice = .{ .operand = val } }, dst_ty),
|
||||
// `[*]T → []T`: a many-pointer has no length, so it can't form a slice
|
||||
// header implicitly. Diagnose and tell the user to slice with a length.
|
||||
.many_to_slice_reject => {
|
||||
if (self.diagnostics) |d| {
|
||||
const cs = self.builder.current_span;
|
||||
d.addFmt(.err, ast.Span{ .start = cs.start, .end = cs.end }, "a many-pointer '[*]T' does not coerce to a slice '[]T' implicitly (it carries no length) — slice it with a length: ptr[0..len]", .{});
|
||||
}
|
||||
return val;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -504,6 +504,15 @@ pub fn runComptimeTypeFunc(self: *Lowering, func_id: FuncId, span: ast.Span) ?Ty
|
||||
}
|
||||
}
|
||||
|
||||
// If lowering this type-fn's BODY already emitted an error (e.g. a rejected
|
||||
// coercion like `[*]T → []T`, issue 0141), the function holds malformed IR —
|
||||
// a slice value that is really a bare 8-byte pointer, etc. Running the VM on it
|
||||
// would dereference garbage (a comptime Addr is a real host pointer, so a bad
|
||||
// data pointer FAULTS, defeating the VM's bail-not-crash guards which only catch
|
||||
// malformed Refs, not malformed comptime DATA). The user's real diagnostic is
|
||||
// already on the list; skip the eval and let `hasErrors()` abort the build.
|
||||
if (self.diagnostics) |d| if (d.hasErrors()) return null;
|
||||
|
||||
// The comptime VM is the SOLE evaluator (P5.7) — no legacy fallback. A
|
||||
// type-fn runs on the VM; a bail is ALWAYS a build-gating diagnostic, never a
|
||||
// fallback. The VM is hardened against malformed lowering-time IR (it BAILS,
|
||||
|
||||
Reference in New Issue
Block a user