check: compare variadic fn params at the declared element type

installparams normalizes a decl's `T...` param lhs to []T in place
(cstage instead sets the resolved tp->type and never mutates AST), so
typeeqast compared a decl-synthesized fn type's []i64 against a fn
TYPE expr's surface i64 and confidently rejected
`let f: fn(args: i64...) void = sum;` — cstage accepts and runs. The
normalization wrapper now carries an op marker and typeeqast peels
exactly it, so both sides compare at the declared element type without
admitting genuinely different element depths. Fixture fnptrlet_variadic;
corpus pin 1486/2972.
This commit is contained in:
2026-08-08 02:47:08 +09:00
parent 98a2e37ac0
commit 430c7e0546
4 changed files with 47 additions and 7 deletions

View File

@@ -1083,7 +1083,18 @@ fn typeeqast(c: *checker, a: *syntax.node, b: *syntax.node) bool = {
let va: bool = pa.op == syntax.tkind.TK_ELLIPSIS;
let vb: bool = pb.op == syntax.tkind.TK_ELLIPSIS;
if (va != vb) { return false; };
if (!typeeqast(c, pa.lhs, pb.lhs)) { return false; };
// installparams normalizes a DECL's `T...` param lhs to
// a marked []T in place; a fn TYPE expr stays surface.
// Peel exactly the marked wrapper so both sides compare
// at the declared element type (cstage's type_eq sees
// type_slice on BOTH sides, check.c:908-917).
let la: *syntax.node = pa.lhs;
let lb: *syntax.node = pb.lhs;
if (la != nil && la.kind == syntax.nkind.N_TSLICE
&& la.op == syntax.tkind.TK_ELLIPSIS) { la = la.lhs; };
if (lb != nil && lb.kind == syntax.nkind.N_TSLICE
&& lb.op == syntax.tkind.TK_ELLIPSIS) { lb = lb.lhs; };
if (!typeeqast(c, la, lb)) { return false; };
};
pa = pa.next;
pb = pb.next;
@@ -6817,6 +6828,14 @@ fn installparams(c: *checker, params: *syntax.node) void = {
if (p.lhs != nil && p.lhs.kind != syntax.nkind.N_TSLICE) {
let sl: *syntax.node = syntax.newnode(syntax.nkind.N_TSLICE, "", 0, 0);
sl.lhs = p.lhs;
// op marks the wrapper as THIS normalization, not
// surface syntax, so typeeqast can peel exactly it
// when comparing against an unnormalized fn TYPE
// expr (`let f: fn(args: i64...) void = sum` — the
// decl side reads []i64 here, the let side i64).
// Param-lhs position never carries a tagged spread
// marker, so the op reads stay disjoint.
sl.op = syntax.tkind.TK_ELLIPSIS;
p.lhs = sl;
};
};