diff --git a/internal/wwfixture/types.ww b/internal/wwfixture/types.ww index cb37c108..b642d036 100644 --- a/internal/wwfixture/types.ww +++ b/internal/wwfixture/types.ww @@ -1,13 +1,13 @@ package wwfixture; def protocolversion: i32 = 1; -def corpuscount: i32 = 1752; -def errorcount: i32 = 349; +def corpuscount: i32 = 1754; +def errorcount: i32 = 351; def compilecount: i32 = 21; def runcount: i32 = 209; def runexitcount: i32 = 1173; -def nativecount: i32 = 3504; -def corpushash: str = "d3c7b6a94918a9c9af14df029ef0cdf6d976a3f1351d41ff9db9a69b753cb016"; +def nativecount: i32 = 3508; +def corpushash: str = "67a119b41fd78b1f2900c096e5b5670e98935d2965573f67bb21ab817931dffa"; type directive = enum i32 { ERROR = 0, diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index fd05d9b6..414b519a 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -6063,6 +6063,19 @@ fn desugarcallargs(c: *checker, n: *syntax.node) void = { let a: *syntax.node = n.list; for (a != nil) { let nexta: *syntax.node = a.next; + // Arity, surplus side — params exhausted with args left + // (variadics never exhaust: TK_ELLIPSIS absorbs without + // advancing, the FFI "..." breaks out below). cstage errs + // at check.c N_CALL `too many arguments`; wwstage ran no + // count check at all, so `add(1,2,3)` built and pushed a + // stray arg silently (cs-reject/ww-accept divergence). + // fn-VALUE callees bail above decl==nil — task #51 owns + // their whole typecheck, arity included. + if (param == nil) { + cerr("error: too many arguments\n"); + c.errs += 1; + return; + }; if (param != nil) { if (param.kind == syntax.nkind.N_PARAM) { // C-style FFI variadic (bare `...`): str=="...", @@ -6134,6 +6147,16 @@ fn desugarcallargs(c: *checker, n: *syntax.node) void = { prev = a; a = nexta; }; + // Arity, missing side — a regular param left over after the + // args ran out (a leftover TK_ELLIPSIS or FFI "..." is a legal + // zero-arg variadic tail). Mirror cstage `not enough arguments`. + if (param != nil) { if (param.kind == syntax.nkind.N_PARAM) { + if (param.op != syntax.tkind.TK_ELLIPSIS + && !syntax.streq(param.str, "...")) { + cerr("error: not enough arguments\n"); + c.errs += 1; + }; + }; }; }; // checkassign — #258 at the assignment context. wwstage runs no other diff --git a/test/wcc/data/call_arity_missing/case.ww b/test/wcc/data/call_arity_missing/case.ww new file mode 100644 index 00000000..c38edddb --- /dev/null +++ b/test/wcc/data/call_arity_missing/case.ww @@ -0,0 +1,8 @@ +//ww:error "not enough arguments" +// Arity, missing side: the callee read a garbage second param slot. +package main; +fn add(a: i64, b: i64) i64 = { return a + b; }; +export fn main() i32 = { + let x: i64 = add(4); + return x: i32; +}; diff --git a/test/wcc/data/call_arity_surplus/case.ww b/test/wcc/data/call_arity_surplus/case.ww new file mode 100644 index 00000000..60936a77 --- /dev/null +++ b/test/wcc/data/call_arity_surplus/case.ww @@ -0,0 +1,9 @@ +//ww:error "too many arguments" +// Arity, surplus side: wwstage desugarcallargs ran no count check, so +// a stray arg built and pushed silently (cs-reject/ww-accept). +package main; +fn add(a: i64, b: i64) i64 = { return a + b; }; +export fn main() i32 = { + let x: i64 = add(1, 2, 3); + return x: i32; +};