Erasing a type to a protocol when it conforms only via a free function (not an explicit impl P for T) built a vtable of unreachable thunks -> SIGABRT on first dispatch, with no diagnostic. Per specs.md erasure is impl-driven, not structural, so the erasure was never valid. Add a conformance gate (firstUnimplementedMethod in buildProtocolValue, src/ir/lower/protocol.zig): emit a located diagnostic when a protocol method has no reachable impl, or when an impl method introduces its own type params (signature mismatch — it bails lazyLowerFunction and would reach the unreachable thunk). A std.debug.panic tripwire guards the diagnostics==null path so a non-conforming erasure can never silently ship as undef. Gate<->thunk equivalence verified bidirectional. Regressions: protocols/0419 (positive struct-field dispatch), diagnostics/1197 (no-impl) + 1198 (generic-method signature mismatch). Updated memory/0808 (it erased a non-conforming type that never dispatched). Verified by 3+1 adversarial reviews, suite 788/0. Filed adjacent bug 0178 (protocol impl method type-mismatch silent miscompile).
2.1 KiB
0178 — protocol impl method with a mismatched return/param TYPE silently miscompiles
Symptom
An impl P for T whose method has the right NAME but a mismatched return type or
parameter type is accepted (it satisfies the issue-0176 conformance gate, which
is name-based), and dispatch through the erased protocol silently produces the
WRONG result (exit 0). No diagnostic. (Arity mismatch and #builtin-body
mismatch fail loudly — exit 1 — and are not this bug; the TYPE-mismatch cases are
silent.)
Reproduction
#import "modules/std.sx";
P :: protocol { val :: (self: *Self) -> i64; }
T :: struct { n: i64 = 7; }
impl P for T { val :: (self: *T) -> bool { return true; } } // return type bool ≠ i64
main :: () {
t := T.{ n = 7 };
p : P = t;
print("{}\n", p.val()); // prints "1" (the bool), silently wrong — no diagnostic
}
A parameter-type mismatch (x: bool where the protocol declares x: i64)
similarly dispatches silently wrong.
Investigation prompt
The issue-0176 conformance gate (firstUnimplementedMethod in
src/ir/lower/protocol.zig) checks method PRESENCE (and rejects type_params > 0), but does NOT check that the impl method's SIGNATURE (parameter types,
arity, return type) matches the protocol method's declared signature. A
mismatched-type impl builds a thunk that calls the impl with the wrong ABI,
silently miscompiling. Add signature validation when registering / gating an
impl method against its protocol method: compare the impl method's params
(after the erased self) and return type against the protocol declaration, and
emit a located diagnostic on mismatch (arity, param type, or return type). The
protocol method declaration is in protocol_decl_map; the impl FnDecl is in
fn_ast_map. Decide whether this lives in the conformance gate or in
ProtocolResolver.registerImplBlock (src/ir/protocols.zig). Follow the
no-silent-fallback rule. Verify: the repro is now a clean diagnostic (exit 1);
a correctly-typed impl still works; add an examples/diagnostics/11xx-...
negative regression. (Found during adversarial review of issue 0176.)