Files
ww/test/wcc/719_signed_data_emit.c
Hojun-Cho 7b9488706b 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".
2026-06-29 03:55:26 +09:00

224 lines
7.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 719_signed_data_emit — corpus-coverage-blind sentinel for #19.
*
* Confirms each cstage / wwstage `.s` for a top-level `let` of signed
* integer type contains a `DATAW <sym>(SB),"..."` row, *and* that the
* inline literal bytes encode the value's two's complement at the
* declared element width. Pre-fix:
* - cstage emit_lets dropped the row entirely on `let x: i8 = -1i8;`
* (link failed, no DATAW); the array arm dropped the whole row.
* - wwstage emitletdataw silently emitted zero bytes where the
* negative literal should have produced 0xFF... (link succeeded;
* runtime read 0). The array arm hit the same gap.
*
* Both test/lang/signed_data_emit_test.ww (the migrated runtime rows)
* and the broader bootstrap byte-id (995_self_rebuild) would catch a
* future regression, but this row
* pins the *asm shape* itself — a future cgen refactor that emits
* the slot via a different directive (e.g. via DATA + DATAR rather
* than DATAW) would silently divergent even with green semantics.
*
* Plus a cstage-vs-wwstage byte-id diff per row, mirroring 707's
* pattern.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include "wwtestpkg.h"
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return -1;
}
struct row {
const char *label;
const char *src;
const char *sym;
const char *want_bytes; /* exact byte-payload inside the DATAW */
};
/* The .s emits each byte either as a printable ASCII glyph (b is 0x20-
* 0x7e and not '"'/'\\') or as `\xNN`. emit_data_byte's rules: 0x22 + 0x5c
* → `\"` / `\\`; printable → raw; everything else → `\xNN`. For the
* sign-extended pads we'll see `\xff` × N as a contiguous run. */
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",
"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",
"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",
"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",
"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",
"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",
"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",
"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",
"main.a",
"d\\x00\\x00\\x00\\x00\\x00\\x00\\x00"
"8\\xff\\xff\\xff\\xff\\xff\\xff\\xff" },
};
static int
slurp(const char *path, char *buf, size_t cap)
{
FILE *f = fopen(path, "rb");
if (!f) return -1;
size_t n = fread(buf, 1, cap - 1, f);
fclose(f);
buf[n] = '\0';
return (int)n;
}
/* Check that the .s contains a DATAW row for `sym` whose payload
* matches `want_bytes`. Returns 0 on match. */
static int
check_dataw(const char *spath, const struct row *r)
{
char buf[1 << 16];
if (slurp(spath, buf, sizeof buf) < 0) return -1;
char needle[256];
snprintf(needle, sizeof needle, "DATAW %s(SB),\"%s\"",
r->sym, r->want_bytes);
return strstr(buf, needle) ? 0 : -1;
}
static int
emit_s(const char *w6c, const struct row *r, int i, char *out_s, size_t cap)
{
char src[64], cmd[1024];
snprintf(src, sizeof src, "/tmp/sde_asm_%d_%d.ww", getpid(), i);
snprintf(out_s, cap, "/tmp/sde_asm_%d_%d_%s.s",
getpid(), i, w6c[strlen(w6c) - 1] == 'w' ? "ww" : "c");
FILE *f = fopen(src, "wb");
if (!f) return -1;
wwtest_fputs(r->src, f);
fclose(f);
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src);
int rc = runwait(cmd);
unlink(src);
return rc;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[512];
if (bin[0] != '/') {
char cwd[256];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char w6c[640], w6c_ww[640];
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
int have_ww = (access(w6c_ww, X_OK) == 0);
int n = (int)(sizeof rows / sizeof rows[0]);
int total = 0, fail = 0;
for (int i = 0; i < n; i++) {
char cs_path[128], ws_path[128];
/* cstage row: DATAW emit + byte payload. */
if (emit_s(w6c, &rows[i], i, cs_path, sizeof cs_path) != 0) {
fprintf(stderr,
"signed_data_emit[cstage][%s]: w6c failed\n",
rows[i].label);
fail++; total++; continue;
}
total++;
if (check_dataw(cs_path, &rows[i]) != 0) {
fprintf(stderr,
"signed_data_emit[cstage][%s]: DATAW %s(SB),\"%s\" missing\n",
rows[i].label, rows[i].sym, rows[i].want_bytes);
fail++;
}
if (!have_ww) { unlink(cs_path); continue; }
/* wwstage row: same DATAW emit. */
if (emit_s(w6c_ww, &rows[i], i, ws_path, sizeof ws_path) != 0) {
fprintf(stderr,
"signed_data_emit[wwstage][%s]: w6c_ww failed\n",
rows[i].label);
fail++; total++;
unlink(cs_path);
continue;
}
total++;
if (check_dataw(ws_path, &rows[i]) != 0) {
fprintf(stderr,
"signed_data_emit[wwstage][%s]: DATAW %s(SB),\"%s\" missing\n",
rows[i].label, rows[i].sym, rows[i].want_bytes);
fail++;
}
/* Byte-id diff between stages. The bootstrap byte-id covers
* cross-stage drift globally; per-row diff here surfaces a
* focused regression in the emit_lets / emitletdataw arms. */
total++;
char cmd[512];
snprintf(cmd, sizeof cmd, "cmp -s %s %s", cs_path, ws_path);
if (runwait(cmd) != 0) {
fprintf(stderr,
"signed_data_emit[%s]: cstage vs wwstage asm differs\n",
rows[i].label);
fail++;
}
unlink(cs_path); unlink(ws_path);
}
if (fail) {
fprintf(stderr,
"signed_data_emit: %d/%d fixtures failed\n", fail, total);
return 1;
}
printf("signed_data_emit: %d/%d ok\n", total, total);
return 0;
}