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:
@@ -32,9 +32,9 @@ categories out of the ordinary developer target.
|
|||||||
| Fixed point and self-host | `test-bootstrap` |
|
| Fixed point and self-host | `test-bootstrap` |
|
||||||
| Host linker/platform behavior | `test-platform` |
|
| Host linker/platform behavior | `test-platform` |
|
||||||
|
|
||||||
The live declarative compiler corpus has 1,485 fixtures and 2,970 C/WW cells:
|
The live declarative compiler corpus has 1,486 fixtures and 2,972 C/WW cells:
|
||||||
338 expected rejections (314 shared and 24 stage-specific), 17 compile-only
|
338 expected rejections (314 shared and 24 stage-specific), 17 compile-only
|
||||||
successes, 191 exit-zero programs, and 939 explicit-exit programs.
|
successes, 191 exit-zero programs, and 940 explicit-exit programs.
|
||||||
|
|
||||||
147 native C carriers remain. They are partitioned exactly once as five
|
147 native C carriers remain. They are partitioned exactly once as five
|
||||||
in-process units, 24 byte/artifact gates, six bootstrap gates, one platform
|
in-process units, 24 byte/artifact gates, six bootstrap gates, one platform
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package wwfixture;
|
package wwfixture;
|
||||||
|
|
||||||
def protocolversion: i32 = 1;
|
def protocolversion: i32 = 1;
|
||||||
def corpuscount: i32 = 1485;
|
def corpuscount: i32 = 1486;
|
||||||
def errorcount: i32 = 338;
|
def errorcount: i32 = 338;
|
||||||
def compilecount: i32 = 17;
|
def compilecount: i32 = 17;
|
||||||
def runcount: i32 = 191;
|
def runcount: i32 = 191;
|
||||||
def runexitcount: i32 = 939;
|
def runexitcount: i32 = 940;
|
||||||
def nativecount: i32 = 2970;
|
def nativecount: i32 = 2972;
|
||||||
def corpushash: str = "69453e64f903a75b8a3f27c79e336ae766b3fb7ecfaac85d13f5d1f0c0dc4f50";
|
def corpushash: str = "ab4b1d1ffc078b23de0a70911740511e873cd8d6f76009c4719d704bb36e6133";
|
||||||
|
|
||||||
type directive = enum i32 {
|
type directive = enum i32 {
|
||||||
ERROR = 0,
|
ERROR = 0,
|
||||||
|
|||||||
@@ -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 va: bool = pa.op == syntax.tkind.TK_ELLIPSIS;
|
||||||
let vb: bool = pb.op == syntax.tkind.TK_ELLIPSIS;
|
let vb: bool = pb.op == syntax.tkind.TK_ELLIPSIS;
|
||||||
if (va != vb) { return false; };
|
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;
|
pa = pa.next;
|
||||||
pb = pb.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) {
|
if (p.lhs != nil && p.lhs.kind != syntax.nkind.N_TSLICE) {
|
||||||
let sl: *syntax.node = syntax.newnode(syntax.nkind.N_TSLICE, "", 0, 0);
|
let sl: *syntax.node = syntax.newnode(syntax.nkind.N_TSLICE, "", 0, 0);
|
||||||
sl.lhs = p.lhs;
|
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;
|
p.lhs = sl;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
21
test/wcc/data/fnptrlet_variadic/case.ww
Normal file
21
test/wcc/data/fnptrlet_variadic/case.ww
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
//ww:run-exit 42
|
||||||
|
package main;
|
||||||
|
|
||||||
|
// A variadic fn assigned to a fn-typed let and called through it. The
|
||||||
|
// wwstage over-rejected the let ("not assignable"): installparams
|
||||||
|
// normalizes the DECL's `T...` param lhs to []T in place, while the
|
||||||
|
// let's fn TYPE expr stays surface, so typeeqast compared []i64 vs
|
||||||
|
// i64. cstage compares resolved types with type_slice on both sides
|
||||||
|
// (check.c:908-917) and accepts.
|
||||||
|
|
||||||
|
fn tally(args: i64...) i64 = {
|
||||||
|
let s: i64 = 0i64;
|
||||||
|
let i: i32 = 0;
|
||||||
|
for (i < args.len) { s += args[i]; i += 1; };
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() i32 = {
|
||||||
|
let f: fn(args: i64...) i64 = tally;
|
||||||
|
return (f(1i64, 2i64, 3i64) * 7i64): i32;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user