check: gate C-style ... to bodiless decls, both stages (#11)

A bodied fn with a bare C-style `...` was silently accepted by cstage
and SEGFAULTED wwstage (resolvefnbody walked a typeless `...` param).
Gate it: bare C-`...` is allowed only on bodiless decls (extern /
@symbol prototypes), the real FFI path; Hare-style `T...` is unaffected.

ww restricts C-`...` to bodiless decls pending vastart/vaarg/vaend
builtins (#16); harec permits bodied C-variadic fns (check.c:3656) -- a
documented divergence, reopened when #16 lands.

Test 852 runs both stages; its reject rows require the gate's diagnostic
(not merely a nonzero exit), so a crash can't pass them vacuously.
This commit is contained in:
2026-06-21 12:14:31 +09:00
parent c814856550
commit 1f1efb273a
4 changed files with 263 additions and 0 deletions

View File

@@ -6306,6 +6306,20 @@ fn checktryprop(c: *checker, n: *syntax.node) void = {
};
};
// hascvariadic — true iff the param list ends in a bare C-style `...`
// (the param whose str=="...", set by the parser; Hare-style `T...`
// carries a name + op==TK_ELLIPSIS instead). Mirrors cstage
// build_fn_type's `p->str && strcmp(p->str,"...")==0` test
// (check.c:2646).
fn hascvariadic(params: *syntax.node) bool = {
let p: *syntax.node = params;
for (p != nil) {
if (syntax.streq(p.str, "...")) { return true; };
p = p.next;
};
return false;
};
// install_param — when entering a fn body, define its params in a
// fresh local scope.
//
@@ -6781,6 +6795,17 @@ export fn checkfile(c: *checker, file: *syntax.node) void = {
let k: syntax.nkind = d.kind;
switch (k) {
case syntax.nkind.N_FNDECL:
// ww restricts C-style ... to bodiless decls pending
// vastart/vaarg/vaend builtins (#16); harec permits bodied
// C-variadic fns (check.c:3656). The bare C-style param is
// the one with str=="..." (Hare-style `T...` carries a name
// + op==TK_ELLIPSIS instead), mirroring cstage build_fn_type
// check.c:2646.
if (d.body != nil && hascvariadic(d.list)) {
cerr(d.file);
cerr(": error: C-style variadic '...' requires a bodiless declaration\n");
c.errs += 1;
};
if (d.lhs != nil) { resolvewalk(c, d.lhs); }; // return type
resolvefnbody(c, d);
case syntax.nkind.N_DEF: