test(ir): lock protocol/impl lookup before A4.2 extraction (A4.2 scaffolding step 1)

Test-first scaffolding ahead of extracting src/ir/protocols.zig — no code change
to the refactor targets (registerProtocolDecl / registerImplBlock /
registerParamImpl / hasImplPlain / tryUserConversion / tryPackImplMatch /
createProtocolThunk / buildProtocolValue).

Adds 4 .ir snapshots (only 0400 existed for 04xx), each captured surgically via
`sx ir | normalize_ir`, path-free, idempotent, and print-free at IR-gen time
(the 0524 contamination lesson):
- 0413-protocols-parameterized-protocol-value  parameterized protocol
                                               (registerParamImpl + tryUserConversion)
- 0414-protocols-generic-struct-protocol-erase generic-struct erasure
                                               (createProtocolThunk + buildProtocolValue)
- 0416-protocols-auto-type-erasure             auto erasure (buildProtocolValue + thunk)
- 0528-packs-protocol-pack-methods             pack-variadic impl (tryPackImplMatch)

With existing 0400 (impl-for-builtin) they pin erasure (auto/generic/builtin) +
parameterized + pack-variadic + dispatch; the 0410/0411/0412 runtime anchors
already pin cross-module visibility + duplicate-impl rejections.

Adds 1 unit test via the public surface (no new exposure, mirroring A4.1
sub-step 1): registerProtocolDecl -> getProtocolInfo builds the dispatch method
table (method names, param_types with self excluded, concrete vs Self return
with ret_is_self + *void encoding). The impl-lookup / conversion plan-object
tests (hasImplPlain, tryUserConversion, tryPackImplMatch — private today) land
with the registry in sub-step 2.

zig build, zig build test, tests/run_examples.sh (357/0) all green.
This commit is contained in:
agra
2026-06-02 21:44:01 +03:00
parent 7ab5d7bee9
commit df386a422e
5 changed files with 17395 additions and 0 deletions

View File

@@ -828,3 +828,54 @@ test "E1.4c noreturn typing: divergence shapes + if-else unification + block pro
defer alloc.destroy(both_div);
try std.testing.expectEqual(TypeId.noreturn, lowering.inferExprType(both_div));
}
// ── A4.2 test-first scaffolding: protocol-decl registration ──────────
// Lock `registerProtocolDecl`'s method-table output (consumed by protocol
// dispatch + impl planning) before the protocol/impl lookup moves to
// `src/ir/protocols.zig`. Public surface only (registerProtocolDecl +
// getProtocolInfo are pub) — the impl-lookup / conversion plan tests land
// with the registry in sub-step 2 (as A4.1's internal tests landed with
// GenericResolver). Arena: a non-parameterized protocol dupes its method
// infos via the module allocator and never frees them.
test "protocols: registerProtocolDecl builds the dispatch method table" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
var module = ir_mod.Module.init(alloc);
defer module.deinit();
var lowering = Lowering.init(&module);
// Shape :: protocol { area :: () -> f64; scaled :: (factor: f64) -> Self; }
const methods = [_]ast.ProtocolMethodDecl{
.{ .name = "area", .params = &.{}, .param_names = &.{}, .return_type = typeKeyword(alloc, "f64"), .default_body = null },
.{
.name = "scaled",
.params = &[_]*Node{typeKeyword(alloc, "f64")},
.param_names = &[_][]const u8{"factor"},
.return_type = typeKeyword(alloc, "Self"),
.default_body = null,
},
};
const pd = ast.ProtocolDecl{ .name = "Shape", .methods = &methods };
lowering.registerProtocolDecl(&pd);
// getProtocolInfo resolves the registered protocol struct by type.
const shape_ty = module.types.findByName(module.types.internString("Shape")).?;
const info = lowering.getProtocolInfo(shape_ty).?;
try std.testing.expectEqual(@as(usize, 2), info.methods.len);
// area :: () -> f64 — no params (self excluded), concrete f64 return.
try std.testing.expectEqualStrings("area", info.methods[0].name);
try std.testing.expectEqual(@as(usize, 0), info.methods[0].param_types.len);
try std.testing.expectEqual(TypeId.f64, info.methods[0].ret_type);
try std.testing.expect(!info.methods[0].ret_is_self);
// scaled :: (factor: f64) -> Self — one f64 param; `Self` return is
// flagged and encoded as `*void` (the dispatcher auto-unboxes it).
try std.testing.expectEqualStrings("scaled", info.methods[1].name);
try std.testing.expectEqual(@as(usize, 1), info.methods[1].param_types.len);
try std.testing.expectEqual(TypeId.f64, info.methods[1].param_types[0]);
try std.testing.expect(info.methods[1].ret_is_self);
try std.testing.expectEqual(module.types.ptrTo(.void), info.methods[1].ret_type);
}