parse: enforce strict-package — reject package-less files (#24a)

Flip the soft-default to a hard "missing package clause" error symmetrically in
both stages (cmd/wcc/parse.c + lib/ww/syntax/parse.ww): the first real decl of a
primary section with empty pathmod/resetmod and no seen clause is now rejected.
Closes the documented soft-default divergence (the 63-wrapper carve-out).

The gate flip can't be split from the migration it breaks, so this is one atomic
commit: ~80 test/wcc wrappers gain `package main;` via a shared wwtestpkg.h
helper, 6 data fixtures plus 17 asm-grep assertions update for the bare->main.<leaf>
root-helper mangle shift, and rt/ declares `package rt;` with @symbol pinning the
bare rt_ensure/rt_malloc linker names.

Root mangling narrows: the executable entry `main` stays bare (existing
carve-out), but root helper symbols become main.X. The #84 cluster is rewritten
to assert main.run distinct from aa.run/test.run; its cgen fix and bare machinery
are retained — still load-bearing for package-less module-reset deps. New
table-driven test 782_strict_package.c (6 rows, both stages).

Retiring //ww:module-reset is deferred to #24b: it is load-bearing (clears the
.wwi pathmod so the body's package clause asserts), not a vestige; fusing its
removal here would be a silent mismatch.

All byte-id gates green; full make test reports "all 335 tests passed".
This commit is contained in:
2026-06-29 03:55:26 +09:00
parent d34c90d932
commit 7b9488706b
95 changed files with 579 additions and 223 deletions

View File

@@ -27,6 +27,7 @@
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "wwtestpkg.h"
static int
runwait(const char *cmd)
@@ -52,44 +53,44 @@ static const struct row rows[] = {
/* Scalar i8 = -1 → low byte 0xff, then 7 bytes of sign-extension. */
{ "i8_neg", "let x: i8 = -1i8;\n"
"fn main() i32 = { return 0; };\n",
"x",
"main.x",
"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff" },
/* Scalar i16 = -2 → 0xfe 0xff then 6 byte sign extension. */
{ "i16_neg", "let x: i16 = -2i16;\n"
"fn main() i32 = { return 0; };\n",
"x",
"main.x",
"\\xfe\\xff\\xff\\xff\\xff\\xff\\xff\\xff" },
/* Scalar i32 = -100 → 0x9C 0xFF 0xFF 0xFF then 4 byte sign extension. */
{ "i32_neg", "let x: i32 = -100i32;\n"
"fn main() i32 = { return 0; };\n",
"x",
"main.x",
"\\x9c\\xff\\xff\\xff\\xff\\xff\\xff\\xff" },
/* Scalar i64 = -1000 → 0xfffffffffffffc18 little-endian. */
{ "i64_neg", "let x: i64 = -1000i64;\n"
"fn main() i32 = { return 0; };\n",
"x",
"main.x",
"\\x18\\xfc\\xff\\xff\\xff\\xff\\xff\\xff" },
/* Array [4]i8 = [1, -2, 3, -4] → 0x01 0xfe 0x03 0xfc. */
{ "i8_arr", "let a: [4]i8 = [1i8, -2i8, 3i8, -4i8];\n"
"fn main() i32 = { return 0; };\n",
"a",
"main.a",
"\\x01\\xfe\\x03\\xfc" },
/* Array [4]i16 = [1, -2, 3, -4] → LE16 each. */
{ "i16_arr", "let a: [4]i16 = [1i16, -2i16, 3i16, -4i16];\n"
"fn main() i32 = { return 0; };\n",
"a",
"main.a",
"\\x01\\x00\\xfe\\xff\\x03\\x00\\xfc\\xff" },
/* Array [3]i32 = [10, -20, 30] → LE32 each. */
{ "i32_arr", "let a: [3]i32 = [10i32, -20i32, 30i32];\n"
"fn main() i32 = { return 0; };\n",
"a",
"main.a",
"\\x0a\\x00\\x00\\x00\\xec\\xff\\xff\\xff\\x1e\\x00\\x00\\x00" },
/* Array [2]i64 = [100, -200] → LE64 each. Note 100 == 'd' and
* 56 == '8' are printable ASCII so emit_data_byte writes them
* raw rather than as `\xNN`. */
{ "i64_arr", "let a: [2]i64 = [100i64, -200i64];\n"
"fn main() i32 = { return 0; };\n",
"a",
"main.a",
"d\\x00\\x00\\x00\\x00\\x00\\x00\\x00"
"8\\xff\\xff\\xff\\xff\\xff\\xff\\xff" },
};
@@ -128,7 +129,7 @@ emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap)
FILE *f = fopen(src, "wb");
if (!f) return -1;
fputs(r->src, f);
wwtest_fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src);