lang: slice-of-protocol variadic ..xs: []P erases each arg to the protocol

packVariadicCallArgs stored the raw concrete arg into a [N x P] array when the
element type was a protocol, so an 8-byte struct landed in a 16-byte {ctx,
vtable} slot -> garbage vtable -> Bus error on dispatch. Now, when the slice
element type is a protocol, each arg is xx-erased to the protocol value via
buildProtocolErasure (same impl-driven machinery as the xx cast). This makes
..xs: []P the runtime, protocol-erased counterpart to the comptime
heterogeneous pack ..xs: P (which stays comptime-only): xs[runtime_i].method()
now works in an ordinary loop.

specs.md: full variadic/pack form-comparison table (concrete-vs-erased,
comptime-vs-runtime). Regression: examples/202. Issue 0052 (FIXED). 237 green.
This commit is contained in:
agra
2026-05-30 01:50:29 +03:00
parent 82bdcd634a
commit ab572359ae
6 changed files with 149 additions and 10 deletions

View File

@@ -8341,6 +8341,14 @@ pub const Lowering = struct {
// Determine if we need to box as Any (for ..Any params) or use raw type
const is_any = (elem_ty == .any);
// `..xs: []P` (slice of a protocol): each concrete arg must be erased to
// a protocol value {ctx, vtable}, not stored raw (which would be a
// size/type mismatch — a heap of garbage vtables → crash on dispatch).
const elem_is_protocol = blk: {
if (elem_ty.isBuiltin()) break :blk false;
const ei = self.module.types.get(elem_ty);
break :blk ei == .@"struct" and ei.@"struct".is_protocol;
};
// Allocate stack array [N x ElemType]
const array_elem = if (is_any) TypeId.any else elem_ty;
@@ -8388,6 +8396,16 @@ pub const Lowering = struct {
if (source_ty != .any) {
val = self.builder.boxAny(val, source_ty);
}
} else if (elem_is_protocol) {
// Erase each concrete arg to the protocol value via the same
// impl-driven `xx` machinery, so the runtime `[]P` holds real
// {ctx, vtable} values and `xs[i].method()` dispatches.
const arg_node = c.args[fixed_count + i];
var source_ty = self.inferExprType(arg_node);
if (source_ty == .unresolved) source_ty = self.builder.getRefType(val);
if (source_ty != elem_ty) {
val = self.buildProtocolErasure(val, arg_node, source_ty, elem_ty);
}
}
const idx_ref = self.builder.constInt(@intCast(i), .s64);
const elem_ptr = self.builder.emit(.{ .index_gep = .{ .lhs = array_slot, .rhs = idx_ref } }, self.module.types.ptrTo(array_elem));