test: migrate Fam4 static-init/DATA-emit value tests to @test (#30)
Continues the test-arch tower past Fam8-13. 11 module-level static-init / DATA-emit value drivers move from test/wcc/*_run.c into @test row- tables under test/lang/; every classification empirically re-probed at HEAD (refuting two stale worklist tags). - value rows -> test/lang/*_test.ww (11 files) - reject rows -> runww //ww:error carriers (3, dual-stage non-vacuous; 947 const-divzero confirmed a both-stage compile-reject, not run-exit) - 840_zeroinit, 944_array_zeroinit, 989_arrlit_tail_zero kept as byte-id .c pins (zero-over-dirtied-frame / DATAW-length is byte-id-blind to a runtime @test; #263), mutation-gated - repoint two stale comment refs to deleted test names (719, 989_structlocal_frame) Migrated static-init @test ride the cs==ww T2 byte-id gate, preserving DATA-emit byte-id. 2D global-struct array-field read (#137/#150) confirmed cs==ww + correct at HEAD. Coverage parity verified row-by-row; two-round reviewed. Floor ratchet follows.
This commit is contained in:
41
test/lang/arr_u16_store_test.ww
Normal file
41
test/lang/arr_u16_store_test.ww
Normal file
@@ -0,0 +1,41 @@
|
||||
// arr_u16_store_test — [N]u16/[N]i16 array-literal init stores MOVW per slot.
|
||||
// Migrated from test/wcc/914_arr_u16_store_run.c. Pre-fix a [4]u16 init emitted
|
||||
// overlapping MOVQ stores (accident-corrected for some values); the fix dispatches
|
||||
// a proper MOVW per 2-byte slot (op chosen by esz, not signedness). cstage
|
||||
// build+run was T1; cs==ww .s byte-id rides T2 (test-lang-byteid). The full_init
|
||||
// row asserts each element to catch any value drift; the [4]u8 control pins the
|
||||
// MOVB path is unchanged; i16_signed pins the dispatch is not signedness-gated.
|
||||
|
||||
package arr_u16_store_test;
|
||||
|
||||
@test fn u16_full_init() void = {
|
||||
let a: [4]u16 = [0xABCDu16, 0xBEEFu16, 0xC0DEu16, 0xDEADu16];
|
||||
assert(a[0] == 0xABCDu16);
|
||||
assert(a[1] == 0xBEEFu16);
|
||||
assert(a[2] == 0xC0DEu16);
|
||||
assert(a[3] == 0xDEADu16);
|
||||
};
|
||||
|
||||
@test fn u16_small_values() void = {
|
||||
let a: [4]u16 = [10u16, 20u16, 30u16, 40u16];
|
||||
let s: i32 = 0;
|
||||
s += a[0]: i32; s += a[1]: i32;
|
||||
s += a[2]: i32; s += a[3]: i32;
|
||||
assert(s == 100);
|
||||
};
|
||||
|
||||
@test fn u8_control() void = {
|
||||
let a: [4]u8 = [10u8, 20u8, 30u8, 40u8];
|
||||
let s: i32 = 0;
|
||||
s += a[0]: i32; s += a[1]: i32;
|
||||
s += a[2]: i32; s += a[3]: i32;
|
||||
assert(s == 100);
|
||||
};
|
||||
|
||||
@test fn i16_signed() void = {
|
||||
let a: [4]i16 = [10i16, 20i16, 30i16, -5i16];
|
||||
let s: i32 = 0;
|
||||
s += a[0]: i32; s += a[1]: i32;
|
||||
s += a[2]: i32; s += a[3]: i32;
|
||||
assert(s == 55);
|
||||
};
|
||||
99
test/lang/array_static_init_test.ww
Normal file
99
test/lang/array_static_init_test.ww
Normal file
@@ -0,0 +1,99 @@
|
||||
// array_static_init_test — module-level 1D/2D/3D array static-init, element
|
||||
// read. Migrated from test/wcc/919_array_static_init_run.c (#129 Phase A.3,
|
||||
// #156). emit_array_lit_bytes recurses on a TY_ARRAY element (esz=etype->size,
|
||||
// rule 13); cgindex leaves the sub-array ADDRESS for an array element so the
|
||||
// outer index dereferences the right cell (#135 sister). cstage build+run was
|
||||
// T1; cs==ww .s byte-id rides T2 (test-lang-byteid). The nested-array `...`
|
||||
// repeat reject is the runww carrier test/wcc/data/arr_nested_ellipsis_reject.
|
||||
//
|
||||
// Float-element rows assert bit-exact incl negative elements (drew: exercise the
|
||||
// sign-XOR byte-loop per element). The struct-with-array-field rows read the
|
||||
// array-field elements directly (D1.buf[k], D2.m[i][j]): the old #137/#150 cs≠ww
|
||||
// global-struct-field-base bug is CLOSED at HEAD, so the read is now cs==ww IDENT
|
||||
// — the original deferred this to byte-id only because the bug was then open.
|
||||
|
||||
package array_static_init_test;
|
||||
|
||||
let A_U8: [4]u8 = [1u8, 2u8, 3u8, 4u8];
|
||||
let A_U32: [4]u32 = [1u32, 2u32, 3u32, 4u32];
|
||||
let A_U64: [2]u64 = [1u64, 2u64];
|
||||
let A_INEG: [4]i32 = [-1, -2, -3, -4];
|
||||
let A_F64: [4]f64 = [1.5, -2.5, 3.5, -4.5];
|
||||
let A_F32: [4]f32 = [1.5f32, -2.5f32, 3.5f32, -4.5f32];
|
||||
def DA_U32: [4]u32 = [11u32, 22u32, 33u32, 44u32];
|
||||
def DA_F64: [4]f64 = [1.5, 2.5, 3.5, 4.5];
|
||||
let A_UZ: [4]u8 = [0u8, 0u8, 0u8, 0u8];
|
||||
let A_U1: [1]u8 = [7u8];
|
||||
let A2D: [3][2]u64 = [[1u64,2u64],[3u64,4u64],[5u64,6u64]];
|
||||
def DA2D: [3][2]u64 = [[1u64,2u64],[3u64,4u64],[5u64,6u64]];
|
||||
let A2V: [3][2]u64 = [[10u64,11u64],[20u64,21u64],[30u64,31u64]];
|
||||
let A28: [2][1]u32 = [[7u32],[9u32]];
|
||||
let A2W: [2][2]u64 = [[1u64,2u64],[3u64,4u64]];
|
||||
let A3D: [2][2][2]u8 = [[[1u8,2u8],[3u8,4u8]],[[5u8,6u8],[7u8,8u8]]];
|
||||
|
||||
type dt1 = struct { tag: i32, buf: [4]u8 };
|
||||
let D1: dt1 = dt1{tag=42, buf=[1u8, 2u8, 3u8, 4u8]};
|
||||
type dt2 = struct { tag: i32, m: [2][2]u64 };
|
||||
let D2: dt2 = dt2{tag=42, m=[[1u64,2u64],[3u64,4u64]]};
|
||||
|
||||
fn bits64(v: f64) u64 = {
|
||||
let x: f64 = v;
|
||||
let p: *u64 = (&x): *u64;
|
||||
return *p;
|
||||
};
|
||||
|
||||
fn bits32(v: f32) u32 = {
|
||||
let x: f32 = v;
|
||||
let p: *u32 = (&x): *u32;
|
||||
return *p;
|
||||
};
|
||||
|
||||
fn at(i: i32, j: i32) u64 = { return A2V[i][j]; };
|
||||
|
||||
@test fn let_u8_arr() void = { assert(A_U8[0]: i32 == 1); };
|
||||
@test fn let_u32_arr() void = { assert(A_U32[0]: i32 == 1); };
|
||||
@test fn let_u64_arr() void = { assert(A_U64[0]: i32 == 1); };
|
||||
@test fn let_i32_neg_arr() void = { assert(A_INEG[0] == -1); };
|
||||
|
||||
@test fn let_f64_arr() void = {
|
||||
assert(bits64(A_F64[0]) == 0x3FF8000000000000u64);
|
||||
assert(bits64(A_F64[1]) == 0xC004000000000000u64);
|
||||
assert(bits64(A_F64[3]) == 0xC012000000000000u64);
|
||||
};
|
||||
|
||||
@test fn let_f32_arr() void = {
|
||||
assert(bits32(A_F32[0]) == 0x3FC00000u32);
|
||||
assert(bits32(A_F32[1]) == 0xC0200000u32);
|
||||
assert(bits32(A_F32[3]) == 0xC0900000u32);
|
||||
};
|
||||
|
||||
@test fn def_u32_arr() void = { assert(DA_U32[0]: i32 == 11); };
|
||||
|
||||
@test fn def_f64_arr() void = {
|
||||
assert(bits64(DA_F64[0]) == 0x3FF8000000000000u64);
|
||||
assert(bits64(DA_F64[3]) == 0x4012000000000000u64);
|
||||
};
|
||||
|
||||
@test fn let_struct_with_arr_field() void = {
|
||||
assert(D1.tag == 42);
|
||||
assert(D1.buf[0]: i32 == 1);
|
||||
assert(D1.buf[3]: i32 == 4);
|
||||
};
|
||||
@test fn let_u8_zero_arr() void = { assert(A_UZ[0]: i32 == 0); };
|
||||
@test fn let_u8_single() void = { assert(A_U1[0]: i32 == 7); };
|
||||
@test fn let_2d_u64() void = { assert(A2D[1][0]: i32 == 3); };
|
||||
@test fn def_2d_u64() void = { assert(DA2D[2][1]: i32 == 6); };
|
||||
@test fn let_2d_varidx() void = { assert((at(1, 1) + at(2, 0)): i32 == 51); };
|
||||
@test fn let_2d_u32_8byte() void = { assert(A28[1][0]: i32 == 9); };
|
||||
|
||||
@test fn let_2d_write() void = {
|
||||
A2W[1][0] = 99u64;
|
||||
assert((A2W[0][0] + A2W[0][1] + A2W[1][0] + A2W[1][1]): i32 == 106);
|
||||
};
|
||||
|
||||
@test fn let_struct_2d_field() void = {
|
||||
assert(D2.tag == 42);
|
||||
assert(D2.m[1][0]: i32 == 3);
|
||||
assert(D2.m[1][1]: i32 == 4);
|
||||
};
|
||||
@test fn let_3d_u8() void = { assert(A3D[1][1][0]: i32 == 7); };
|
||||
62
test/lang/const_slice_aggregate_test.ww
Normal file
62
test/lang/const_slice_aggregate_test.ww
Normal file
@@ -0,0 +1,62 @@
|
||||
// const_slice_aggregate_test — module-level const slice of (str,*fn) tuple rows
|
||||
// + scalar &fn globals, DATA emit with element relocations. Migrated from
|
||||
// test/wcc/946_const_slice_aggregate_run.c (#117/#119). cstage build+run was T1;
|
||||
// cs==ww .s byte-id rides T2 (test-lang-byteid). The loud non-tuple-aggregate
|
||||
// reject is the runww carrier test/wcc/data/slice_of_str_reject.
|
||||
//
|
||||
// drew: CALL THROUGH the *fn reloc — a scalar-only / len-only assert is
|
||||
// reloc-blind. Each row calls each distinct fn back so a wrong/swapped reloc is
|
||||
// caught; the str-element relocs + per-row backing stride ride the T2 byte-id.
|
||||
// Whole-element word0 is the only correctly-loaded backing word (#121 read
|
||||
// constraint), so the fn-first / scalar-first orderings put the read target at
|
||||
// word0.
|
||||
|
||||
package const_slice_aggregate_test;
|
||||
|
||||
fn fa(c: rune) bool = { return c == 'a'; };
|
||||
fn fz(c: rune) bool = { return c == 'z'; };
|
||||
|
||||
const TBL_FN: [](*fn(c: rune) bool, str) = [(&fa, "aa"), (&fz, "zzz")];
|
||||
const TBL_SF: [](str, *fn(c: rune) bool) = [("aa", &fa), ("zzz", &fz)];
|
||||
const TBL_3: [](str, *fn(c: rune) bool) =
|
||||
[("a", &fa), ("bb", &fa), ("ccc", &fa)];
|
||||
const TSCAL: [](i64, str) = [(10i64, "a"), (20i64, "bb")];
|
||||
let PF: *fn(c: rune) bool = &fa;
|
||||
let PG: *fn(c: rune) bool = &fz;
|
||||
|
||||
@test fn fnfirst_reloc() void = {
|
||||
let e0 = TBL_FN[0];
|
||||
let e1 = TBL_FN[1];
|
||||
let f0 = e0.0;
|
||||
let f1 = e1.0;
|
||||
assert((*f0)('a'));
|
||||
assert(!(*f0)('z'));
|
||||
assert((*f1)('z'));
|
||||
assert(!(*f1)('a'));
|
||||
assert(len(TBL_FN) == 2);
|
||||
};
|
||||
|
||||
@test fn consumer_str_fn() void = {
|
||||
assert(len(TBL_SF) == 2);
|
||||
};
|
||||
|
||||
@test fn consumer_3row() void = {
|
||||
assert(len(TBL_3) == 3);
|
||||
};
|
||||
|
||||
@test fn scalar_tuple_slice() void = {
|
||||
let e0 = TSCAL[0];
|
||||
let e1 = TSCAL[1];
|
||||
let v0 = e0.0;
|
||||
let v1 = e1.0;
|
||||
assert(v0 == 10);
|
||||
assert(v1 == 20);
|
||||
assert(len(TSCAL) == 2);
|
||||
};
|
||||
|
||||
@test fn scalar_fnptr() void = {
|
||||
assert((*PF)('a'));
|
||||
assert(!(*PF)('z'));
|
||||
assert((*PG)('z'));
|
||||
assert(!(*PG)('a'));
|
||||
};
|
||||
62
test/lang/def_float_lit_test.ww
Normal file
62
test/lang/def_float_lit_test.ww
Normal file
@@ -0,0 +1,62 @@
|
||||
// def_float_lit_test — module-level `def`/`let` f64/f32 literal (incl negation)
|
||||
// emits DATA, read back from a fn. Migrated from test/wcc/917_def_float_lit_run.c
|
||||
// (#129 Phase A.1). Pre-fix `def K: f64 = lit;` fell through emit_defs's int-only
|
||||
// fold gate so no DATAW landed (link: `undefined reference to main.K`); emit_lets's
|
||||
// float arm never peeled N_UN(MINUS/PLUS, N_FLOATLIT) so `let g: f64 = -1.5;`
|
||||
// silently zero-emitted. Fix = shared emit_floatlit_data helper (negation via
|
||||
// IEEE-754 sign-bit XOR). cstage build+run was T1; cs==ww .s byte-id rides T2
|
||||
// (test-lang-byteid).
|
||||
//
|
||||
// drew: assert BIT-EXACT incl sign — the bits()-reinterpret reads the full IEEE
|
||||
// pattern, so a sign-bit or mantissa drift fails (a truncating `: i32` read would
|
||||
// alias 1.5 and 1.9). The negative rows pin the sign-XOR byte path.
|
||||
|
||||
package def_float_lit_test;
|
||||
|
||||
def KDPOS: f64 = 1.5;
|
||||
def KDNEG: f64 = -1.5;
|
||||
def KFPOS: f32 = 1.5f32;
|
||||
def KFNEG: f32 = -1.5f32;
|
||||
let GDNEG: f64 = -1.5;
|
||||
let GDPOS: f64 = 1.5;
|
||||
let GFPOS: f32 = 1.5f32;
|
||||
|
||||
fn bits64(v: f64) u64 = {
|
||||
let x: f64 = v;
|
||||
let p: *u64 = (&x): *u64;
|
||||
return *p;
|
||||
};
|
||||
|
||||
fn bits32(v: f32) u32 = {
|
||||
let x: f32 = v;
|
||||
let p: *u32 = (&x): *u32;
|
||||
return *p;
|
||||
};
|
||||
|
||||
@test fn def_f64_pos() void = {
|
||||
assert(bits64(KDPOS) == 0x3FF8000000000000u64);
|
||||
};
|
||||
|
||||
@test fn def_f64_neg() void = {
|
||||
assert(bits64(KDNEG) == 0xBFF8000000000000u64);
|
||||
};
|
||||
|
||||
@test fn def_f32_pos() void = {
|
||||
assert(bits32(KFPOS) == 0x3FC00000u32);
|
||||
};
|
||||
|
||||
@test fn def_f32_neg() void = {
|
||||
assert(bits32(KFNEG) == 0xBFC00000u32);
|
||||
};
|
||||
|
||||
@test fn let_f64_neg() void = {
|
||||
assert(bits64(GDNEG) == 0xBFF8000000000000u64);
|
||||
};
|
||||
|
||||
@test fn let_f64_pos() void = {
|
||||
assert(bits64(GDPOS) == 0x3FF8000000000000u64);
|
||||
};
|
||||
|
||||
@test fn let_f32_pos() void = {
|
||||
assert(bits32(GFPOS) == 0x3FC00000u32);
|
||||
};
|
||||
33
test/lang/inferred_scalar_global_test.ww
Normal file
33
test/lang/inferred_scalar_global_test.ww
Normal file
@@ -0,0 +1,33 @@
|
||||
// inferred_scalar_global_test — module-level inferred/const-expr scalar global
|
||||
// emit + read. Migrated from test/wcc/947_inferred_scalar_global_run.c
|
||||
// (#66 b-i, #134 neg, #133 const-expr). An inferred / unary / const-expr int
|
||||
// global once emitted no DATAW on wwstage (MOVSXD on stale AX) and link-failed
|
||||
// on cstage; the let pass-2 arm now const-folds the rhs and stamps an N_INTLIT
|
||||
// so cgen's literal DATA emitter fires in BOTH stages. cstage build+run was T1;
|
||||
// cs==ww .s byte-id rides T2 (test-lang-byteid). The div-by-zero const-expr
|
||||
// reject is the runww carrier test/wcc/data/const_divzero_reject.
|
||||
//
|
||||
// The typed controls (typed_ctrl/neg_typed_ctrl/const_typed) lock the
|
||||
// construction proof: the inferred decl's emitted .s equals the typed decl's
|
||||
// after defaulting.
|
||||
|
||||
package inferred_scalar_global_test;
|
||||
|
||||
let SI = 42;
|
||||
let STC: i64 = 42;
|
||||
let SNI = -42;
|
||||
let SNT: int = -42;
|
||||
let SCI = 7 * 6;
|
||||
let SCT: i64 = 7 * 6;
|
||||
def KD: int = 6;
|
||||
let SDR = KD * 7;
|
||||
let SUB = -(2 * 3);
|
||||
|
||||
@test fn inferred() void = { assert(SI: i64 == 42); };
|
||||
@test fn typed_ctrl() void = { assert(STC == 42); };
|
||||
@test fn neg_inferred() void = { assert(SNI: i64 == -42); };
|
||||
@test fn neg_typed_ctrl() void = { assert(SNT == -42); };
|
||||
@test fn const_inferred() void = { assert(SCI: i64 == 42); };
|
||||
@test fn const_typed() void = { assert(SCT == 42); };
|
||||
@test fn const_defref() void = { assert(SDR: i64 == 42); };
|
||||
@test fn const_unary_binop() void = { assert(SUB: i64 == -6); };
|
||||
38
test/lang/nestfield_test.ww
Normal file
38
test/lang/nestfield_test.ww
Normal file
@@ -0,0 +1,38 @@
|
||||
// nestfield_test — nested sub-8 composite struct field at natural offsets.
|
||||
// Migrated from test/wcc/989_nestfield_run.c (#44/#55). For outer{a:u8, p:inner}
|
||||
// the natural offset of p is 1, but a pre-fix slot-padded layout put it at 8, so
|
||||
// construction (write) and field-access (read) disagreed and o.p.x / o.p.y read
|
||||
// wrong. cstage build+run was T1; the original cat-A invariant (cs==ww exit)
|
||||
// now rides the stronger T2 .s byte-id (test-lang-byteid).
|
||||
//
|
||||
// nested3's z proves post-composite accumulation stays natural (z follows the
|
||||
// 1-byte-padded inner, not the 8-byte-padded one). flat_ctl is the no-sub-8-field
|
||||
// control where natural and slot-padded layouts coincide.
|
||||
|
||||
package nestfield_test;
|
||||
|
||||
type inr = struct { x: u8, y: u8 };
|
||||
type ot2 = struct { a: u8, p: inr };
|
||||
type ot3 = struct { a: u8, p: inr, z: i64 };
|
||||
type flt = struct { a: u8, b: i64 };
|
||||
|
||||
@test fn nested2() void = {
|
||||
let o: ot2 = ot2 { a = 5, p = inr { x = 6, y = 7 } };
|
||||
assert(o.a: int == 5);
|
||||
assert(o.p.x: int == 6);
|
||||
assert(o.p.y: int == 7);
|
||||
};
|
||||
|
||||
@test fn nested3() void = {
|
||||
let o: ot3 = ot3 { a = 1, p = inr { x = 2, y = 3 }, z = 0x44444444i64 };
|
||||
assert(o.a: int == 1);
|
||||
assert(o.p.x: int == 2);
|
||||
assert(o.p.y: int == 3);
|
||||
assert(o.z == 0x44444444i64);
|
||||
};
|
||||
|
||||
@test fn flat_ctl() void = {
|
||||
let o: flt = flt { a = 9, b = 0x33333333i64 };
|
||||
assert(o.a: int == 9);
|
||||
assert(o.b == 0x33333333i64);
|
||||
};
|
||||
66
test/lang/signed_data_emit_test.ww
Normal file
66
test/lang/signed_data_emit_test.ww
Normal file
@@ -0,0 +1,66 @@
|
||||
// signed_data_emit_test — module-level signed i8..i64 scalar + 1D-array DATA
|
||||
// emit, self-asserting the sign survives exactly. Migrated from
|
||||
// test/wcc/923_signed_data_emit_run.c. Pre-fix wwstage silently zeroed negative
|
||||
// array entries; cstage emitted nothing (link failed). cstage build+run was T1;
|
||||
// cs==ww .s byte-id rides T2 (test-lang-byteid).
|
||||
//
|
||||
// drew: assert SIGN survives exactly — every row compares against the signed
|
||||
// typed literal, so a sign-extension or zero-fill drift fails. i64_tilde pins
|
||||
// the N_UN(TK_TILDE) fold (~0u64 == -1i64). The arrays mirror the utf8 DFA shape.
|
||||
|
||||
package signed_data_emit_test;
|
||||
|
||||
let X_I8N: i8 = -1i8;
|
||||
let X_I8P: i8 = 42i8;
|
||||
let X_I16N: i16 = -2i16;
|
||||
let X_I16P: i16 = 1000i16;
|
||||
let X_I32N: i32 = -100i32;
|
||||
let X_I32P: i32 = 100000i32;
|
||||
let X_I64N: i64 = -1000i64;
|
||||
let X_I64P: i64 = 1000000i64;
|
||||
let X_TILDE: i64 = (~0u64): i64;
|
||||
|
||||
let DFA: [8]i8 = [0i8, -1i8, 1i8, 2i8, 0i8, 0i8, -1i8, -1i8];
|
||||
let A16: [4]i16 = [1i16, -2i16, 3i16, -4i16];
|
||||
let A32: [4]i32 = [10i32, -20i32, 30i32, -40i32];
|
||||
let A64: [4]i64 = [100i64, -200i64, 300i64, -400i64];
|
||||
|
||||
@test fn i8_neg_scalar() void = { assert(X_I8N == -1i8); };
|
||||
@test fn i8_pos_scalar() void = { assert(X_I8P == 42i8); };
|
||||
@test fn i16_neg_scalar() void = { assert(X_I16N == -2i16); };
|
||||
@test fn i16_pos_scalar() void = { assert(X_I16P == 1000i16); };
|
||||
@test fn i32_neg_scalar() void = { assert(X_I32N == -100i32); };
|
||||
@test fn i32_pos_scalar() void = { assert(X_I32P == 100000i32); };
|
||||
@test fn i64_neg_scalar() void = { assert(X_I64N == -1000i64); };
|
||||
@test fn i64_pos_scalar() void = { assert(X_I64P == 1000000i64); };
|
||||
@test fn i64_tilde_scalar() void = { assert(X_TILDE == -1i64); };
|
||||
|
||||
@test fn i8_arr_dfa() void = {
|
||||
assert(DFA[0] == 0i8);
|
||||
assert(DFA[1] == -1i8);
|
||||
assert(DFA[2] == 1i8);
|
||||
assert(DFA[3] == 2i8);
|
||||
assert(DFA[6] == -1i8);
|
||||
assert(DFA[7] == -1i8);
|
||||
};
|
||||
|
||||
@test fn i16_arr_mixed() void = {
|
||||
assert(A16[0] == 1i16);
|
||||
assert(A16[1] == -2i16);
|
||||
assert(A16[2] == 3i16);
|
||||
assert(A16[3] == -4i16);
|
||||
};
|
||||
|
||||
@test fn i32_arr_mixed() void = {
|
||||
assert(A32[0] == 10i32);
|
||||
assert(A32[1] == -20i32);
|
||||
assert(A32[2] == 30i32);
|
||||
assert(A32[3] == -40i32);
|
||||
};
|
||||
|
||||
@test fn i64_arr_mixed() void = {
|
||||
assert(A64[0] == 100i64);
|
||||
assert(A64[1] == -200i64);
|
||||
assert(A64[2] == 300i64);
|
||||
assert(A64[3] == -400i64);
|
||||
};
|
||||
44
test/lang/strarray_static_test.ww
Normal file
44
test/lang/strarray_static_test.ww
Normal file
@@ -0,0 +1,44 @@
|
||||
// strarray_static_test — module-level [N]str static-init: per-element .len
|
||||
// header sums + per-element .ptr RELOC dereferenced to chars. Migrated from
|
||||
// test/wcc/919_strarray_static_run.c. cstage build+run was T1; cs==ww .s byte-id
|
||||
// rides T2 (test-lang-byteid).
|
||||
//
|
||||
// drew: assert THROUGH the per-element .ptr deref (not just .len) — a reloc-blind
|
||||
// .len-only check would miss a wrong/swapped element relocation. The empty-slot
|
||||
// and `...` repeat-suffix rows pin that an empty element doesn't shift the
|
||||
// following element's reloc and that the repeat-fill row's DATAR resolves to the
|
||||
// last explicit element.
|
||||
|
||||
package strarray_static_test;
|
||||
|
||||
let TLEN: [3]str = ["ab", "cde", "f"];
|
||||
let TPTR: [3]str = ["A", "B", "C"];
|
||||
let TVAR: [3]str = ["xx", "yyy", "z"];
|
||||
let TEMP: [2]str = ["", "Z"];
|
||||
let TREP: [3]str = ["X", "Y"...];
|
||||
|
||||
@test fn strtab_len() void = {
|
||||
assert((TLEN[0].len + TLEN[1].len + TLEN[2].len): i32 == 6);
|
||||
};
|
||||
|
||||
@test fn strtab_ptr_elem2() void = {
|
||||
let p: *u8 = TPTR[2].ptr;
|
||||
assert((*p): i32 == 67);
|
||||
};
|
||||
|
||||
@test fn strtab_ptr_varindex() void = {
|
||||
let i: i32 = 1;
|
||||
let p: *u8 = TVAR[i].ptr;
|
||||
assert((*p): i32 == 121);
|
||||
};
|
||||
|
||||
@test fn strtab_empty_then_ptr() void = {
|
||||
assert(TEMP[0].len == 0);
|
||||
let p: *u8 = TEMP[1].ptr;
|
||||
assert((*p): i32 == 90);
|
||||
};
|
||||
|
||||
@test fn strtab_repeat_suffix() void = {
|
||||
let p: *u8 = TREP[2].ptr;
|
||||
assert((*p): i32 == 89);
|
||||
};
|
||||
66
test/lang/struct_composite_init_test.ww
Normal file
66
test/lang/struct_composite_init_test.ww
Normal file
@@ -0,0 +1,66 @@
|
||||
// struct_composite_init_test — module-level `let`/`def` composite-struct
|
||||
// static-init, field read. Migrated from test/wcc/918_struct_composite_init_run.c
|
||||
// (#129 Phase A.2). Pre-fix emit_lets/emit_defs skipped struct-typed decls
|
||||
// (link: `undefined reference to main.NAME`) and a struct def read MOVSXD'd
|
||||
// stack byte 0. Fix = emit_struct_data/emit_struct_lit_bytes walking the field
|
||||
// list with per-field-offset zero-fill (rule 13). cstage build+run was T1;
|
||||
// cs==ww .s byte-id rides T2 (test-lang-byteid).
|
||||
//
|
||||
// The float-field rows assert bit-exact (drew) via bits64. let_empty/norhs read
|
||||
// 0 from the statically zero-filled DATA (a global, not a dirtied stack — no
|
||||
// benign-zero trap; an under-emit would shorten the symbol, not pad zero). The
|
||||
// 8B-struct row pins the !is_struct gate over the sz==8 scalar short-circuit.
|
||||
|
||||
package struct_composite_init_test;
|
||||
|
||||
type cfg_t = struct { a: i32, b: u64 };
|
||||
type ft = struct { a: f64, b: u32 };
|
||||
type s8 = struct { a: i32, b: i32 };
|
||||
|
||||
let CFG_L: cfg_t = cfg_t{a=1, b=2u64};
|
||||
def CFG_D: cfg_t = cfg_t{a=1, b=2u64};
|
||||
let F_L: ft = ft{a=1.5, b=99u32};
|
||||
def F_D: ft = ft{a=1.5, b=99u32};
|
||||
let Z: cfg_t = cfg_t{};
|
||||
let X8: s8 = s8{a=7, b=42};
|
||||
let CFG_NR: cfg_t;
|
||||
|
||||
fn bits64(v: f64) u64 = {
|
||||
let x: f64 = v;
|
||||
let p: *u64 = (&x): *u64;
|
||||
return *p;
|
||||
};
|
||||
|
||||
@test fn let_int_struct() void = {
|
||||
assert(CFG_L.a == 1);
|
||||
assert(CFG_L.b == 2u64);
|
||||
};
|
||||
|
||||
@test fn def_int_struct() void = {
|
||||
assert(CFG_D.a == 1);
|
||||
assert(CFG_D.b == 2u64);
|
||||
};
|
||||
|
||||
@test fn let_float_field() void = {
|
||||
assert(bits64(F_L.a) == 0x3FF8000000000000u64);
|
||||
assert(F_L.b == 99u32);
|
||||
};
|
||||
|
||||
@test fn def_float_field() void = {
|
||||
assert(bits64(F_D.a) == 0x3FF8000000000000u64);
|
||||
assert(F_D.b == 99u32);
|
||||
};
|
||||
|
||||
@test fn let_empty_struct() void = {
|
||||
assert(Z.a == 0);
|
||||
assert(Z.b == 0u64);
|
||||
};
|
||||
|
||||
@test fn let_int_struct_8b() void = {
|
||||
assert(X8.a == 7);
|
||||
assert(X8.b == 42);
|
||||
};
|
||||
|
||||
@test fn let_norhs_struct() void = {
|
||||
assert(CFG_NR.a == 0);
|
||||
};
|
||||
34
test/lang/structlit_arrfield_test.ww
Normal file
34
test/lang/structlit_arrfield_test.ww
Normal file
@@ -0,0 +1,34 @@
|
||||
// structlit_arrfield_test — struct array-field init + read (global let/def and
|
||||
// local lit). Migrated from test/wcc/949_structlit_arrfield_run.c (#249). BUG B:
|
||||
// reading a [4]u8 field of a module-global struct SEGFAULTed on wwstage; BUG A:
|
||||
// a local struct-lit array field fell to the scalar tail and dropped every
|
||||
// element but the first. Fix = cg_structlit_fill array arm + def_isstructdef
|
||||
// LOAD widening (ADDQ $field_off). cstage build+run was T1; cs==ww .s byte-id
|
||||
// rides T2 (test-lang-byteid). The def row has a [3]u8 pad ahead of encmap, so
|
||||
// the read exercises a non-zero field offset (the base64 std_encoding shape).
|
||||
|
||||
package structlit_arrfield_test;
|
||||
|
||||
type e1 = struct { encmap: [4]u8 };
|
||||
let GE: e1 = e1 { encmap = [65u8, 66u8, 67u8, 68u8] };
|
||||
|
||||
type e2 = struct { pad: [3]u8, encmap: [4]u8 };
|
||||
def GD: e2 = e2 { pad = [9u8, 9u8, 9u8], encmap = [65u8, 66u8, 67u8, 68u8] };
|
||||
|
||||
@test fn global_let_read() void = {
|
||||
assert(GE.encmap[0]: i32 == 65);
|
||||
};
|
||||
|
||||
@test fn global_def_read_off() void = {
|
||||
assert(GD.encmap[2]: i32 == 67);
|
||||
};
|
||||
|
||||
@test fn local_lit_read0() void = {
|
||||
let g: e1 = e1 { encmap = [65u8, 66u8, 67u8, 68u8] };
|
||||
assert(g.encmap[0]: i32 == 65);
|
||||
};
|
||||
|
||||
@test fn local_lit_read3() void = {
|
||||
let g: e1 = e1 { encmap = [65u8, 66u8, 67u8, 68u8] };
|
||||
assert(g.encmap[3]: i32 == 68);
|
||||
};
|
||||
70
test/lang/valstruct_subsize_test.ww
Normal file
70
test/lang/valstruct_subsize_test.ww
Normal file
@@ -0,0 +1,70 @@
|
||||
// valstruct_subsize_test — sub-8 nested value-struct zero-init extent, local +
|
||||
// global, distinct byte asserts. Migrated from
|
||||
// test/wcc/949_valstruct_subsize_run.c. A nested {[N]u8} value-struct of ABI
|
||||
// size 1/2/4 once emitted a stray MOVQ $0 (local) / 8 zero DATA bytes (global)
|
||||
// on wwstage that cstage didn't. cstage build+run was T1; cs==ww .s byte-id
|
||||
// rides T2 (test-lang-byteid).
|
||||
//
|
||||
// The D1/D2 rows WRITE then READ a byte so the value is deterministic (the
|
||||
// robust falsifiable dimension). The ctl_*_16 rows are the negative control: a
|
||||
// >8 multi-word value-struct still zero-inits — a wrongly-suppressed >8 zero arm
|
||||
// would read stack garbage (and diverge from the still-zeroing cstage on the T2
|
||||
// byte-id, which is the real guarantee for the unwritten read).
|
||||
|
||||
package valstruct_subsize_test;
|
||||
|
||||
type in4 = struct { m: [4]u8 };
|
||||
type ov4 = struct { i: in4 };
|
||||
type in2 = struct { m: [2]u8 };
|
||||
type ov2 = struct { i: in2 };
|
||||
type in1 = struct { m: [1]u8 };
|
||||
type ov1 = struct { i: in1 };
|
||||
type in16 = struct { m: [16]u8 };
|
||||
type ov16 = struct { i: in16 };
|
||||
|
||||
let G4: ov4;
|
||||
let G2: ov2;
|
||||
let G1: ov1;
|
||||
let G16: ov16;
|
||||
|
||||
@test fn d1_local_4() void = {
|
||||
let o: ov4;
|
||||
o.i.m[0] = 66u8;
|
||||
assert(o.i.m[0]: i32 == 66);
|
||||
};
|
||||
|
||||
@test fn d1_local_2() void = {
|
||||
let o: ov2;
|
||||
o.i.m[1] = 55u8;
|
||||
assert(o.i.m[1]: i32 == 55);
|
||||
};
|
||||
|
||||
@test fn d1_local_1() void = {
|
||||
let o: ov1;
|
||||
o.i.m[0] = 44u8;
|
||||
assert(o.i.m[0]: i32 == 44);
|
||||
};
|
||||
|
||||
@test fn d2_global_4() void = {
|
||||
G4.i.m[0] = 66u8;
|
||||
assert(G4.i.m[0]: i32 == 66);
|
||||
};
|
||||
|
||||
@test fn d2_global_2() void = {
|
||||
G2.i.m[1] = 55u8;
|
||||
assert(G2.i.m[1]: i32 == 55);
|
||||
};
|
||||
|
||||
@test fn d2_global_1() void = {
|
||||
G1.i.m[0] = 44u8;
|
||||
assert(G1.i.m[0]: i32 == 44);
|
||||
};
|
||||
|
||||
@test fn ctl_local_16() void = {
|
||||
let o: ov16;
|
||||
assert(o.i.m[7]: i32 == 0);
|
||||
};
|
||||
|
||||
@test fn ctl_global_16() void = {
|
||||
assert(G16.i.m[7]: i32 == 0);
|
||||
};
|
||||
@@ -11,8 +11,9 @@
|
||||
* negative literal should have produced 0xFF... (link succeeded;
|
||||
* runtime read 0). The array arm hit the same gap.
|
||||
*
|
||||
* Both 923_signed_data_emit_run and the broader bootstrap byte-id
|
||||
* (995_self_rebuild) would catch a future regression, but this row
|
||||
* 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.
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
/*
|
||||
* 914_arr_u16_store_run — runtime + byte-id net for #128a: array-
|
||||
* literal init into [N]u16 (or any [N]T where esz==2) must store with
|
||||
* MOVW, not MOVQ. Pre-fix cstage's cgen.c:7180-7222 array-init dispatch
|
||||
* routed esz==2 to MOVQ fall-through (the comment at 7194-7198
|
||||
* documented + deferred this until A_MOVW landed in w6a); the MOVQ
|
||||
* wrote 8 bytes into a 2-byte slot, overlapping the next 6 bytes of
|
||||
* stack. Adjacent fully-init writes accident-corrected via overlap
|
||||
* (each MOVQ rewrote the prior MOVQ's trailing 6B), but a PARTIAL
|
||||
* init left high garbage in slots that should have been default-zero.
|
||||
*
|
||||
* Fix is the now-unblocked dispatch: `else if (esz == 2) op = A_MOVW;`
|
||||
* The wwstage emitter was already correct (uses MOVW); the cstage gap
|
||||
* was the deferred TODO. Both stages now emit byte-identical MOVW for
|
||||
* [N]u16 (and any aliased-narrow-element [N]T whose tinfo.size == 2).
|
||||
*
|
||||
* Rows cover (the bug is caught via the rule-10 cs==ww byte-id gate;
|
||||
* ww doesn't accept truly-partial init literals, and adjacent MOVQ
|
||||
* writes accident-correct the runtime values, so byte-id is the lever):
|
||||
* - u16_full_init: fully-init [4]u16, regression guard (pre-fix
|
||||
* accident-correct at runtime via MOVQ overlap; .s shifts
|
||||
* MOVQ→MOVW, cs==ww BYTE-IDENTICAL now)
|
||||
* - u16_small_values: small u16 values, summed (regression guard)
|
||||
* - u8_control: [4]u8 baseline (already MOVB pre-fix, no shift)
|
||||
* - i16_signed: signed-narrow [N]i16, same dispatch — verifies the
|
||||
* fix isn't gated on unsignedness
|
||||
*
|
||||
* TY_NAMED alias of u16 (`type myw = u16; let a: [4]myw = …`) is a
|
||||
* sibling miscompile filed separately: wwstage's array-init element-
|
||||
* size dispatch reads `primsize(elemn.str)` which returns 0 for an
|
||||
* alias name, leaving esz at 8 (wrong slot, wrong stride). Out of
|
||||
* scope for #128a (cstage MOVW landing).
|
||||
*
|
||||
* Each row carries (a) cstage `ww build` + run asserting the exit
|
||||
* code and (b) w6c vs w6c_ww `.s` cmp (rule-10 byte-id). Post-#128a
|
||||
* the partial-init row's cstage emission gains MOVW; both stages
|
||||
* unchanged everywhere else.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.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; int want_exit; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* Fully-init [4]u16: pre-fix MOVQ overlap accident-corrected;
|
||||
* post-fix proper MOVW per slot. Sum of last bytes = 0xBE = 190;
|
||||
* mod 256 = 190. We assert per-element to catch any value drift. */
|
||||
{ "u16_full_init",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [4]u16 = [0xABCDu16, 0xBEEFu16, 0xC0DEu16, 0xDEADu16];\n"
|
||||
" if (a[0] != 0xABCDu16) { return 1; };\n"
|
||||
" if (a[1] != 0xBEEFu16) { return 2; };\n"
|
||||
" if (a[2] != 0xC0DEu16) { return 3; };\n"
|
||||
" if (a[3] != 0xDEADu16) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
/* Same shape with smaller values — pin that the dispatch fires
|
||||
* on values that don't need the upper 16 bits. */
|
||||
{ "u16_small_values",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [4]u16 = [10u16, 20u16, 30u16, 40u16];\n"
|
||||
" let s: i32 = 0;\n"
|
||||
" s += a[0]: i32; s += a[1]: i32;\n"
|
||||
" s += a[2]: i32; s += a[3]: i32;\n"
|
||||
" return s;\n"
|
||||
"};\n", 100 },
|
||||
/* [4]u8 control: already correct (MOVB pre-fix). Regression
|
||||
* guard — asm should be unchanged. */
|
||||
{ "u8_control",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [4]u8 = [10u8, 20u8, 30u8, 40u8];\n"
|
||||
" let s: i32 = 0;\n"
|
||||
" s += a[0]: i32; s += a[1]: i32;\n"
|
||||
" s += a[2]: i32; s += a[3]: i32;\n"
|
||||
" return s;\n"
|
||||
"};\n", 100 },
|
||||
/* [4]i16: signed-narrow, same MOVW dispatch (op chosen by esz,
|
||||
* not signedness). Verifies the fix isn't gated. */
|
||||
{ "i16_signed",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [4]i16 = [10i16, 20i16, 30i16, -5i16];\n"
|
||||
" let s: i32 = 0;\n"
|
||||
" s += a[0]: i32; s += a[1]: i32;\n"
|
||||
" s += a[2]: i32; s += a[3]: i32;\n"
|
||||
" return s;\n"
|
||||
"};\n", 55 /* 10+20+30+(-5) = 55 */ },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa);
|
||||
int cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char w6c[1100], w6c_ww[1100];
|
||||
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
||||
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
||||
if (access(w6c_ww, X_OK) != 0) {
|
||||
fprintf(stderr, "arru16store: w6c_ww missing — cannot run "
|
||||
"the cs==ww byte-id gate (the whole point of this test)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int n = 0, fail = 0;
|
||||
for (int i = 0; rows[i].src; i++, n++) {
|
||||
char tmpdir[64];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwau16_%d_d_%d",
|
||||
getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
|
||||
char src[128], outbin[128], rmcmd[160];
|
||||
snprintf(src, sizeof src, "%s/wwau16_%d_%d.ww",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/wwau16_%d_%d",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { runwait(rmcmd); fail++; continue; }
|
||||
fputs(rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
|
||||
bin, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage build failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
if (got != rows[i].want_exit) {
|
||||
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
|
||||
rows[i].label, got, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
|
||||
char cs_s[128], ws_s[128];
|
||||
snprintf(cs_s, sizeof cs_s, "%s/wwau16_%d_%d_cs.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "%s/wwau16_%d_%d_ww.s",
|
||||
tmpdir, getpid(), i);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c, cs_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww failed\n",
|
||||
rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: cstage/wwstage .s DIFFER (rule-10 "
|
||||
"byte-id violation)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "%d/%d arr-u16-store tests failed\n", fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("arru16store: %d/%d ok (cstage run + cs==ww byte-id)\n",
|
||||
n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,231 +0,0 @@
|
||||
/*
|
||||
* 917_def_float_lit_run — runtime + byte-id net for #129 Phase A.1:
|
||||
* module-level `def NAME: f64 = literal;` (and `def: f32 = …`)
|
||||
* silently fell through emit_defs's int-only `fold_int_literal` gate,
|
||||
* so no DATAW row landed in the .data section — `w6l` failed link
|
||||
* with `undefined reference to 'main.NAME'`. Symmetric latent bug in
|
||||
* emit_lets's float arm: it only handled bare N_FLOATLIT, never
|
||||
* peeled N_UN(MINUS/PLUS, N_FLOATLIT), so `let g: f64 = -1.5;`
|
||||
* silently zero-emitted into the same undef-ref shape.
|
||||
*
|
||||
* Phase A.1 fix (rule-12 sea-of-stars consolidation):
|
||||
* - cstage: extract `emit_floatlit_data(out, c, dir, name, T, rhs)`
|
||||
* helper, called by emit_lets's float arm (now collapsed) AND
|
||||
* emit_defs (new float arm). N_CAST + N_UN(MINUS/PLUS,
|
||||
* N_FLOATLIT) peeled inside the helper.
|
||||
* - wwstage: parallel `emitfloatlitdata` helper. Negation via
|
||||
* IEEE-754 sign-bit XOR (avoids dragging the math bitcast helpers
|
||||
* into cgen).
|
||||
* - LOAD side: cstage's N_IDENT float-let arm and wwstage's cgident
|
||||
* def-branch both gated on let_islet/deflookup-but-not-float; the
|
||||
* gate widens to also catch float defs (they emit through the
|
||||
* same mod_mangle / emitsymname symbol).
|
||||
*
|
||||
* Bootstrap NEUTRAL: zero current `def: f{32,64} = lit` consumers in
|
||||
* lib/ or selfhost/. The N_UN-peel collateral fix for lets has no
|
||||
* shipped consumers either (audit: no `let g: f64 = -lit;` anywhere).
|
||||
*
|
||||
* Rows cover both fix sides (DATA emit + LOAD) and the N_UN collateral:
|
||||
* - f64_def_pos: `def K: f64 = 1.5;` — was undef-ref pre-fix, exit
|
||||
* 1 (1.5 → i32 truncates to 1).
|
||||
* - f64_def_neg: `def K: f64 = -1.5;` — was undef-ref pre-fix; exit
|
||||
* 255 (sign-bit flip via XOR; -1.5 → i32 truncates to -1 → 255
|
||||
* in unsigned exit byte).
|
||||
* - f32_def_pos: `def K: f32 = 1.5f32; …` — same shape, f32 width.
|
||||
* - f64_let_neg: `let g: f64 = -1.5;` — pre-existing latent bug
|
||||
* (N_UN peel never added to emit_lets); now fixed by the same
|
||||
* helper.
|
||||
* - f64_let_pos: `let g: f64 = 1.5;` — regression guard (existing
|
||||
* bare-N_FLOATLIT path).
|
||||
* - f32_let_pos: `let g: f32 = 1.5f32;` — regression guard, f32 path.
|
||||
*
|
||||
* Each row carries (a) cstage `ww build` + run asserting exit code
|
||||
* and (b) w6c vs w6c_ww `.s` cmp (rule-10 byte-id).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.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; int want_exit; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "f64_def_pos",
|
||||
"package main;\n"
|
||||
"def K: f64 = 1.5;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: f64 = K;\n"
|
||||
" return (x: i32);\n"
|
||||
"};\n", 1 },
|
||||
{ "f64_def_neg",
|
||||
"package main;\n"
|
||||
"def K: f64 = -1.5;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: f64 = K;\n"
|
||||
" return (x: i32);\n"
|
||||
"};\n", 255 /* -1 as unsigned exit byte */ },
|
||||
{ "f32_def_pos",
|
||||
"package main;\n"
|
||||
"def K: f32 = 1.5f32;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: f32 = K;\n"
|
||||
" return (x: i32);\n"
|
||||
"};\n", 1 },
|
||||
/* Matrix-closure: f32 + neg combined directly. Exercises the
|
||||
* f32-narrow path PLUS the top-byte-XOR negation in one row.
|
||||
* The other rows cover each leg individually (f64_def_neg for
|
||||
* negation, f32_def_pos for f32 narrow); this row pins they
|
||||
* compose correctly. */
|
||||
{ "f32_def_neg",
|
||||
"package main;\n"
|
||||
"def K: f32 = -1.5f32;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: f32 = K;\n"
|
||||
" return (x: i32);\n"
|
||||
"};\n", 255 /* -1 as exit byte */ },
|
||||
/* Pre-existing latent: emit_lets's float arm never peeled
|
||||
* N_UN(MINUS,N_FLOATLIT). The class-closure consolidation
|
||||
* fix in emit_floatlit_data heals this too. */
|
||||
{ "f64_let_neg",
|
||||
"package main;\n"
|
||||
"let g: f64 = -1.5;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return (g: i32);\n"
|
||||
"};\n", 255 },
|
||||
{ "f64_let_pos",
|
||||
"package main;\n"
|
||||
"let g: f64 = 1.5;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return (g: i32);\n"
|
||||
"};\n", 1 },
|
||||
{ "f32_let_pos",
|
||||
"package main;\n"
|
||||
"let g: f32 = 1.5f32;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return (g: i32);\n"
|
||||
"};\n", 1 },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa);
|
||||
int cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char w6c[1100], w6c_ww[1100];
|
||||
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
||||
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
||||
if (access(w6c_ww, X_OK) != 0) {
|
||||
fprintf(stderr, "deflitf: w6c_ww missing — cannot run "
|
||||
"the cs==ww byte-id gate (the whole point of this test)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int n = 0, fail = 0;
|
||||
for (int i = 0; rows[i].src; i++, n++) {
|
||||
char tmpdir[64];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwdflf_%d_d_%d",
|
||||
getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
|
||||
char rmcmd[128];
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
char src[128], outbin[128], cs_s[128], ws_s[128];
|
||||
snprintf(src, sizeof src, "%s/wwdflf_%d_%d.ww",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/wwdflf_%d_%d",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(cs_s, sizeof cs_s, "%s/wwdflf_%d_%d_cs.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "%s/wwdflf_%d_%d_ww.s",
|
||||
tmpdir, getpid(), i);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { fail++; runwait(rmcmd); continue; }
|
||||
fputs(rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
|
||||
bin, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage build failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
if (got != rows[i].want_exit) {
|
||||
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
|
||||
rows[i].label, got, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c, cs_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww failed\n",
|
||||
rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: cstage/wwstage .s DIFFER (rule-10 "
|
||||
"byte-id violation)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "%d/%d def-float-lit tests failed\n", fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("deflitf: %d/%d ok (cstage run + cs==ww byte-id)\n",
|
||||
n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* 918_struct_composite_init_run — runtime + byte-id net for #129
|
||||
* Phase A.2: module-level `let`/`def` with composite-struct
|
||||
* initializer silently emitted undefined ref OR wrong bytes pre-fix.
|
||||
*
|
||||
* Pre-A.2 failure modes:
|
||||
* - cstage emit_lets / wwstage emitletdataw skipped struct-typed lets
|
||||
* entirely (`if (is_struct) continue;` / parallel) — link surfaced
|
||||
* `undefined reference to 'main.NAME'`.
|
||||
* - cstage emit_defs / wwstage emitdefconstants had no struct arm —
|
||||
* same undef ref for def, plus a SEPARATE LOAD-side cgen bug
|
||||
* emitting `MOVSXD (BP), AX` (reading stack frame byte 0) when the
|
||||
* LOAD did get past the link.
|
||||
*
|
||||
* Phase A.2 fix (per A.1 SSoT-helper precedent):
|
||||
* - cstage: emit_struct_data + emit_struct_lit_bytes helpers walk
|
||||
* Tfield list in declaration order, zero-fill padding via per-
|
||||
* field offsets (rule 13), dispatch per field type. Float-field
|
||||
* bytes inlined (mirroring A.1's emit_floatlit_data shape but
|
||||
* localised so the byte loop covers padding too). Nested struct
|
||||
* recurses. Out-of-scope field kinds (array / str / slice / ptr)
|
||||
* fatal loud per rule-7.
|
||||
* - cstage: emit_lets + emit_defs gain struct arms routing through
|
||||
* the helper.
|
||||
* - cstage: LOAD-side widening at `if (u && u->kind == TY_STRUCT
|
||||
* && lhs->kind == N_IDENT)` arm — the `let_islet`-gated LEAQ
|
||||
* name(SB) shape now also fires for struct defs via the new
|
||||
* `def_isstructdef` registry (mirror of letvars).
|
||||
* - wwstage: parallel emitstructdata + emitstructlitbytes helpers,
|
||||
* defent.dtnode field, defvarstructinfo (cgdot LOAD widening).
|
||||
*
|
||||
* Bootstrap NEUTRAL: zero `let/def: T = T{...}` consumers in lib/ or
|
||||
* selfhost/. γ-cleanup is the first consumer (lib/math:floatinfo).
|
||||
*
|
||||
* Rows cover all A.2-scope shapes:
|
||||
* - (a) `let CFG: cfg_t = cfg_t{a=1, b=2u64};` — plain mixed-int let
|
||||
* - (b) `def CFG: cfg_t = cfg_t{a=1, b=2u64};` — plain mixed-int def
|
||||
* (exercises LOAD-widening; both stages)
|
||||
* - (c) `let F: ft = ft{a=1.5, b=99u32};` — let with float field
|
||||
* - (d) `def F: ft = ft{a=1.5, b=99u32};` — def with float field
|
||||
* (storage + LOAD + float-narrow path together)
|
||||
* - (e) `let Z: zt = zt{};` — empty struct-lit zero-fill
|
||||
* - (f) `let CFG: cfg_t;` — regression: no-rhs (pre-existing path
|
||||
* unchanged)
|
||||
*
|
||||
* Each row carries (a) cstage `ww build` + run asserting exit code
|
||||
* and (b) w6c vs w6c_ww `.s` cmp (rule-10 byte-id).
|
||||
*
|
||||
* Nested-struct shape (#3 in design report) is OMITTED here — the
|
||||
* helper implements the recursion but #145 (parser/checker inner-
|
||||
* literal field-name leak) blocks end-to-end correctness; nested
|
||||
* row deferred until #145 lands.
|
||||
*
|
||||
* Array-in-struct shape parked to Phase A.3 boundary.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.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; int want_exit; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "let_int_struct",
|
||||
"package main;\n"
|
||||
"type cfg_t = struct { a: i32, b: u64 };\n"
|
||||
"let CFG: cfg_t = cfg_t{a=1, b=2u64};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return CFG.a;\n"
|
||||
"};\n", 1 },
|
||||
{ "def_int_struct",
|
||||
"package main;\n"
|
||||
"type cfg_t = struct { a: i32, b: u64 };\n"
|
||||
"def CFG: cfg_t = cfg_t{a=1, b=2u64};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return CFG.a;\n"
|
||||
"};\n", 1 },
|
||||
{ "let_float_field",
|
||||
"package main;\n"
|
||||
"type ft = struct { a: f64, b: u32 };\n"
|
||||
"let F: ft = ft{a=1.5, b=99u32};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return (F.a: i32);\n"
|
||||
"};\n", 1 },
|
||||
{ "def_float_field",
|
||||
"package main;\n"
|
||||
"type ft = struct { a: f64, b: u32 };\n"
|
||||
"def F: ft = ft{a=1.5, b=99u32};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return (F.a: i32);\n"
|
||||
"};\n", 1 },
|
||||
{ "let_empty_struct",
|
||||
"package main;\n"
|
||||
"type zt = struct { a: i32, b: u64 };\n"
|
||||
"let Z: zt = zt{};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return Z.a;\n"
|
||||
"};\n", 0 },
|
||||
/* 8B struct hits the cstage emit_lets scalar-8B short-circuit
|
||||
* (sz==8 fold_int_literal arm) — without the `!let_isstruct`
|
||||
* gate the struct lit fold-fails and the let drops entirely,
|
||||
* emitting no DATA. Both stages now route via the struct arm. */
|
||||
{ "let_int_struct_8b",
|
||||
"package main;\n"
|
||||
"type s8 = struct { a: i32, b: i32 };\n"
|
||||
"let X: s8 = s8{a=7, b=42};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return X.a;\n"
|
||||
"};\n", 7 },
|
||||
/* Regression: no-rhs path unchanged (emit_data_row_zero / wwstage
|
||||
* parallel). */
|
||||
{ "let_norhs_struct",
|
||||
"package main;\n"
|
||||
"type cfg_t = struct { a: i32, b: u64 };\n"
|
||||
"let CFG: cfg_t;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return CFG.a;\n"
|
||||
"};\n", 0 },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa);
|
||||
int cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char w6c[1100], w6c_ww[1100];
|
||||
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
||||
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
||||
if (access(w6c_ww, X_OK) != 0) {
|
||||
fprintf(stderr, "strcomp: w6c_ww missing — cannot run "
|
||||
"the cs==ww byte-id gate (the whole point of this test)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int n = 0, fail = 0;
|
||||
for (int i = 0; rows[i].src; i++, n++) {
|
||||
char tmpdir[64];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwstrc_%d_d_%d",
|
||||
getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
|
||||
char src[128], outbin[128], cs_s[128], ws_s[128], rmcmd[160];
|
||||
snprintf(src, sizeof src, "%s/wwstrc_%d_%d.ww",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/wwstrc_%d_%d",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(cs_s, sizeof cs_s, "%s/wwstrc_%d_%d_cs.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "%s/wwstrc_%d_%d_ww.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { fail++; runwait(rmcmd); continue; }
|
||||
fputs(rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
|
||||
bin, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage build failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
if (got != rows[i].want_exit) {
|
||||
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
|
||||
rows[i].label, got, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c, cs_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww failed\n",
|
||||
rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: cstage/wwstage .s DIFFER (rule-10 "
|
||||
"byte-id violation)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "%d/%d struct-composite tests failed\n", fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("strcomp: %d/%d ok (cstage run + cs==ww byte-id)\n",
|
||||
n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,354 +0,0 @@
|
||||
/*
|
||||
* 919_array_static_init_run — runtime + byte-id net for #129 Phase
|
||||
* A.3: module-level let/def with array initializer.
|
||||
*
|
||||
* Pre-A.3 state:
|
||||
* - Int-element arrays (u8/i8/u16/u32/u64/i32 etc) already worked
|
||||
* in both stages via fold_int_literal.
|
||||
* - Float-element arrays ([N]f64, [N]f32) → undef-ref at link
|
||||
* (emit_lets array arm fold_int_literal fails on FLOATLIT).
|
||||
* - Array DEFs (def A: [N]T = [...]) → emit_defs no array arm
|
||||
* (storage missing) AND cgindex broken (reads LEAQ (BP), BX —
|
||||
* stack frame, not data section).
|
||||
* - Array-in-struct field (`def D: dt = dt{tag=42, buf=[...]}`) →
|
||||
* #129 A.2 rule-7 fatal "array field rhs not foldable" — the
|
||||
* shape parked in A.2 awaiting A.3.
|
||||
*
|
||||
* Phase A.3 fix (mirror A.1/A.2 SSoT-helper precedent):
|
||||
* - cstage: emit_array_data + emit_array_lit_bytes helpers with
|
||||
* element-kind dispatch (int via fold_int_literal preserving
|
||||
* bootstrap byte-id, float via inline bitcast + sign-XOR byte-
|
||||
* loop mirror of A.1, struct via emit_struct_lit_bytes recursion
|
||||
* mirror of A.2). Two-pass validate-then-emit avoids partial-byte
|
||||
* corruption on rhs-fold-failure.
|
||||
* - cstage: emit_lets array arm routes through helper; emit_defs
|
||||
* gains array arm.
|
||||
* - cstage: DefArray registry + def_isarraydef populated in
|
||||
* let_collect; cgindex N_INDEX direct-ident `isglobal` gate
|
||||
* widened to (let_islet || def_isarraydef).
|
||||
* - cstage: emit_struct_lit_bytes (A.2 helper) gains TY_ARRAY field
|
||||
* arm calling emit_array_lit_bytes recursively (closes A.2 parked
|
||||
* shape 15).
|
||||
* - wwstage: parallel emitarraydata + emitarraylitbytes; emitstruct
|
||||
* litbytes TY_ARRAY arm; defvartnode helper; cgindex falls through
|
||||
* to defvartnode after letvartnode nil.
|
||||
*
|
||||
* Bootstrap RISK: live consumers in lib/os, lib/bufio, lib/strings,
|
||||
* lib/encoding/utf8 (dfa + masks), lib/strconv/stof_data
|
||||
* (left_shift_table). All use typed-int-literal elements; the int-elem
|
||||
* helper path is byte-for-byte preserved → bootstrap NEUTRAL.
|
||||
*
|
||||
* Rows (size strata 1B/2B/4B/8B × count strata 1/2/4 × int/float/struct/
|
||||
* empty/no-rhs/def-variant, avoiding the 16B-evade pattern from A.2):
|
||||
*
|
||||
* - (a) `let A: [4]u8 = [1u8, 2u8, 3u8, 4u8]` — 1B regression
|
||||
* - (b) `let A: [4]u32 = [1u32, 2u32, 3u32, 4u32]` — 4B regression
|
||||
* - (c) `let A: [2]u64 = [1u64, 2u64]` — 8B regression
|
||||
* - (d) `let A: [4]i32 = [-1, -2, -3, -4]` — N_UN peel regression
|
||||
* - (e) `let A: [4]f64 = [1.5, -2.5, 3.5, -4.5]` — NEW float-elem
|
||||
* - (f) `let A: [4]f32 = [1.5f32, -2.5f32, 3.5f32, -4.5f32]` — NEW
|
||||
* f32 narrow + sign-XOR per element
|
||||
* - (g) `def A: [4]u32 = [11u32, 22u32, 33u32, 44u32]` — NEW
|
||||
* def-storage + LOAD-widening
|
||||
* - (h) `def A: [4]f64 = [1.5, 2.5, 3.5, 4.5]` — NEW def-variant of
|
||||
* float
|
||||
* - (i) `def D: dt = dt{tag=42, buf=[1u8,2u8,3u8,4u8]}` — NEW
|
||||
* closes A.2 shape 15 (struct-with-array-field)
|
||||
* - (j) `let A: [4]u8 = [0u8, 0u8, 0u8, 0u8]` — explicit-zero
|
||||
* regression
|
||||
* - (k) `let A: [1]u8 = [0u8]` — single-elem (matches lib/os/
|
||||
* emptypath pattern)
|
||||
*
|
||||
* Each row: cstage `ww build` + run asserting exit code + w6c vs
|
||||
* w6c_ww `.s` cmp (rule-10 byte-id).
|
||||
*
|
||||
* #156 (PREREQ-1) extends this with 2D `[N][M]T` static-init (a) +
|
||||
* double-index read (b) — the A.3 shape-14 capstone, consumer-driven by
|
||||
* fold-4's powers_of_ten[596][2]u64. emit_array_lit_bytes gains a
|
||||
* TY_ARRAY-element arm (recurse; esz=etype->size); cgindex leaves the
|
||||
* sub-array ADDRESS for an array element (sister of #135). Rows
|
||||
* let_2d_x, def_2d_u64 and let_struct_2d_field + the nested-`...`
|
||||
* loud-reject (rule-7) below. The D.m[i][j] global-struct-field-array
|
||||
* READ stays a
|
||||
* pre-existing gap (#160, 1D+2D, cs≠ww) out of scope here.
|
||||
*
|
||||
* Deferred:
|
||||
* - Pointer-element arrays `[N]*T = [&G, &H]` — needs DATAR per
|
||||
* element (own task/fold).
|
||||
* - `...` repeat with a nested-array element — loud-reject (#156
|
||||
* rule-7); no consumer (powers_of_ten is fully enumerated).
|
||||
* - Bare-int `[N]u8 = [1, 2, 3, 4]` — #130 (checker issue).
|
||||
* - Partial init `[4]u8 = [1u8]` — checker rejects (parser/checker
|
||||
* decision).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.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; int want_exit; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "let_u8_arr",
|
||||
"package main;\n"
|
||||
"let A: [4]u8 = [1u8, 2u8, 3u8, 4u8];\n"
|
||||
"export fn main() i32 = { return A[0]: i32; };\n", 1 },
|
||||
{ "let_u32_arr",
|
||||
"package main;\n"
|
||||
"let A: [4]u32 = [1u32, 2u32, 3u32, 4u32];\n"
|
||||
"export fn main() i32 = { return A[0]: i32; };\n", 1 },
|
||||
{ "let_u64_arr",
|
||||
"package main;\n"
|
||||
"let A: [2]u64 = [1u64, 2u64];\n"
|
||||
"export fn main() i32 = { return A[0]: i32; };\n", 1 },
|
||||
{ "let_i32_neg_arr",
|
||||
"package main;\n"
|
||||
"let A: [4]i32 = [-1, -2, -3, -4];\n"
|
||||
"export fn main() i32 = { return A[0]; };\n", 255 /* -1 */ },
|
||||
/* Float-element array — NEW in A.3. Includes both signs to exercise
|
||||
* the sign-XOR byte-loop per element. */
|
||||
{ "let_f64_arr",
|
||||
"package main;\n"
|
||||
"let A: [4]f64 = [1.5, -2.5, 3.5, -4.5];\n"
|
||||
"export fn main() i32 = { return (A[0]: i32); };\n", 1 },
|
||||
{ "let_f32_arr",
|
||||
"package main;\n"
|
||||
"let A: [4]f32 = [1.5f32, -2.5f32, 3.5f32, -4.5f32];\n"
|
||||
"export fn main() i32 = { return (A[0]: i32); };\n", 1 },
|
||||
/* def-variant exercises the LOAD-widening (def_isarraydef) at
|
||||
* cgindex/cgident. Storage emit also new. */
|
||||
{ "def_u32_arr",
|
||||
"package main;\n"
|
||||
"def A: [4]u32 = [11u32, 22u32, 33u32, 44u32];\n"
|
||||
"export fn main() i32 = { return A[0]: i32; };\n", 11 },
|
||||
{ "def_f64_arr",
|
||||
"package main;\n"
|
||||
"def A: [4]f64 = [1.5, 2.5, 3.5, 4.5];\n"
|
||||
"export fn main() i32 = { return (A[0]: i32); };\n", 1 },
|
||||
/* The A.2-parked shape-15 — closes via emit_struct_lit_bytes
|
||||
* TY_ARRAY field arm. */
|
||||
{ "let_struct_with_arr_field",
|
||||
"package main;\n"
|
||||
"type dt = struct { tag: i32, buf: [4]u8 };\n"
|
||||
"let D: dt = dt{tag=42, buf=[1u8, 2u8, 3u8, 4u8]};\n"
|
||||
"export fn main() i32 = { return D.tag; };\n", 42 },
|
||||
{ "let_u8_zero_arr",
|
||||
"package main;\n"
|
||||
"let A: [4]u8 = [0u8, 0u8, 0u8, 0u8];\n"
|
||||
"export fn main() i32 = { return A[0]: i32; };\n", 0 },
|
||||
{ "let_u8_single",
|
||||
"package main;\n"
|
||||
"let A: [1]u8 = [7u8];\n"
|
||||
"export fn main() i32 = { return A[0]: i32; };\n", 7 },
|
||||
/* #156 (PREREQ-1): 2D [N][M]T static-init (a) + double-index read
|
||||
* (b) — the A.3 shape-14 capstone, consumer-driven by fold-4's
|
||||
* powers_of_ten[596][2]u64. emit_array_lit_bytes recurses on the
|
||||
* TY_ARRAY element (esz=etype->size, rule-13); cgindex leaves the
|
||||
* sub-array ADDRESS (not a value) for an array element so the outer
|
||||
* index dereferences the right cell (sister of #135). */
|
||||
{ "let_2d_u64",
|
||||
"package main;\n"
|
||||
"let A: [3][2]u64 = [[1u64,2u64],[3u64,4u64],[5u64,6u64]];\n"
|
||||
"export fn main() i32 = { return A[1][0]: i32; };\n", 3 },
|
||||
{ "def_2d_u64",
|
||||
"package main;\n"
|
||||
"def A: [3][2]u64 = [[1u64,2u64],[3u64,4u64],[5u64,6u64]];\n"
|
||||
"export fn main() i32 = { return A[2][1]: i32; };\n", 6 },
|
||||
/* variable-index read — the exact fold-4 access pattern
|
||||
* (powers_of_ten[i][0]/[i][1]). 21 + 30 = 51. */
|
||||
{ "let_2d_varidx",
|
||||
"package main;\n"
|
||||
"let A: [3][2]u64 = [[10u64,11u64],[20u64,21u64],[30u64,31u64]];\n"
|
||||
"fn at(i: i32, j: i32) u64 = { return A[i][j]; };\n"
|
||||
"export fn main() i32 = { return (at(1, 1) + at(2, 0)): i32; };\n",
|
||||
51 },
|
||||
/* 8-byte 2D: must NOT hit the sz==8 scalar short-circuit (the
|
||||
* isarr8/N_TARRAY guard, #128 lesson) — routes to the array arm. */
|
||||
{ "let_2d_u32_8byte",
|
||||
"package main;\n"
|
||||
"let A: [2][1]u32 = [[7u32],[9u32]];\n"
|
||||
"export fn main() i32 = { return A[1][0]: i32; };\n", 9 },
|
||||
/* 2D write to one cell, sum all four — verifies the lvalue address
|
||||
* targets the exact cell with no neighbour clobber. 1+2+99+4=106. */
|
||||
{ "let_2d_write",
|
||||
"package main;\n"
|
||||
"let A: [2][2]u64 = [[1u64,2u64],[3u64,4u64]];\n"
|
||||
"export fn main() i32 = { A[1][0] = 99u64; return "
|
||||
"(A[0][0]+A[0][1]+A[1][0]+A[1][1]): i32; };\n", 106 },
|
||||
/* struct field that is itself a 2D array — static-init emit +
|
||||
* layout (read D.tag). The D.m[i][j] field-array READ exercises a
|
||||
* pre-existing global-struct-field-base bug (#137/#150 family,
|
||||
* 1D+2D, cs≠ww) out of PREREQ-1 scope — the array bytes are
|
||||
* covered by the cs==ww byte-id gate below. */
|
||||
{ "let_struct_2d_field",
|
||||
"package main;\n"
|
||||
"type dt = struct { tag: i32, m: [2][2]u64 };\n"
|
||||
"let D: dt = dt{tag=42, m=[[1u64,2u64],[3u64,4u64]]};\n"
|
||||
"export fn main() i32 = { return D.tag; };\n", 42 },
|
||||
/* 3D — locks recursion-depth>2 in both emit (nested TY_ARRAY arm
|
||||
* recurses twice) and read (double-then-single index, two
|
||||
* address-leaves). [[[1,2],[3,4]],[[5,6],[7,8]]]; A[1][1][0] = 7. */
|
||||
{ "let_3d_u8",
|
||||
"package main;\n"
|
||||
"let A: [2][2][2]u8 = "
|
||||
"[[[1u8,2u8],[3u8,4u8]],[[5u8,6u8],[7u8,8u8]]];\n"
|
||||
"export fn main() i32 = { return A[1][1][0]: i32; };\n", 7 },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa);
|
||||
int cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char w6c[1100], w6c_ww[1100];
|
||||
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
||||
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
||||
if (access(w6c_ww, X_OK) != 0) {
|
||||
fprintf(stderr, "arrinit: w6c_ww missing — cannot run "
|
||||
"the cs==ww byte-id gate (the whole point of this test)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int n = 0, fail = 0;
|
||||
for (int i = 0; rows[i].src; i++, n++) {
|
||||
char tmpdir[64];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwari_%d_d_%d",
|
||||
getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
char rmcmd[160];
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
char src[128];
|
||||
snprintf(src, sizeof src, "%s/wwari_%d_%d.ww",
|
||||
tmpdir, getpid(), i);
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { fail++; runwait(rmcmd); continue; }
|
||||
fputs(rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
char outbin[128];
|
||||
snprintf(outbin, sizeof outbin, "%s/wwari_%d_%d",
|
||||
tmpdir, getpid(), i);
|
||||
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
|
||||
bin, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage build failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
if (got != rows[i].want_exit) {
|
||||
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
|
||||
rows[i].label, got, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
|
||||
char cs_s[128], ws_s[128];
|
||||
snprintf(cs_s, sizeof cs_s, "%s/wwari_%d_%d_cs.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "%s/wwari_%d_%d_ww.s",
|
||||
tmpdir, getpid(), i);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c, cs_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww failed\n",
|
||||
rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: cstage/wwstage .s DIFFER (rule-10 "
|
||||
"byte-id violation)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
}
|
||||
|
||||
/* #156 rule-7: a `...` repeat marker with a nested-array element is
|
||||
* a loud reject in BOTH stages (no consumer needs it; powers_of_ten
|
||||
* is fully enumerated). The compile must FAIL, not silently emit
|
||||
* wrong bytes. Separate from the rows table (which asserts build
|
||||
* success). */
|
||||
{
|
||||
char src[64];
|
||||
snprintf(src, sizeof src, "/tmp/wwari_%d_rej.ww", getpid());
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f != NULL) {
|
||||
fputs("package main;\n"
|
||||
"let A: [4][2]u64 = [[1u64, 2u64]...];\n"
|
||||
"export fn main() i32 = { return 0; };\n", f);
|
||||
fclose(f);
|
||||
}
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd, "%s -o /dev/null %s 2>/dev/null",
|
||||
w6c, src);
|
||||
int rc_cs = runwait(cmd);
|
||||
snprintf(cmd, sizeof cmd, "%s -o /dev/null %s 2>/dev/null",
|
||||
w6c_ww, src);
|
||||
int rc_ww = runwait(cmd);
|
||||
n++;
|
||||
if (rc_cs == 0 || rc_ww == 0) {
|
||||
fprintf(stderr, "row[nested_ellipsis_reject]: expected "
|
||||
"BOTH stages to reject (cs=%d ww=%d), want nonzero "
|
||||
"(#156 rule-7)\n", rc_cs, rc_ww);
|
||||
fail++;
|
||||
}
|
||||
unlink(src);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "%d/%d array-static-init tests failed\n", fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("arrinit: %d/%d ok (cstage run + cs==ww byte-id)\n",
|
||||
n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* 919_strarray_static_run — BUG #18. Runtime + cs==ww byte-id net for a
|
||||
* module-level `let xs: [N]str = ["a","b",…];` static table.
|
||||
*
|
||||
* THE BUG (BOTH stages dropped it — shared-logic gap, not rule-10):
|
||||
* A module-level [N]str static init emitted NO .data at all. The
|
||||
* per-element str header carries a ptr→rodata relocation (not just
|
||||
* bytes), so it can't ride emit_array_lit_bytes (byte-only): the str-
|
||||
* element case fell through to fold_int_literal, returned 0, and
|
||||
* emit_lets zero-init'd / skipped — leaving `main.<tab>` undefined.
|
||||
* `w6l: undefined reference` at link. Function-LOCAL [N]str worked
|
||||
* (runtime element stores); only the module-level STATIC DATA form was
|
||||
* broken.
|
||||
*
|
||||
* THE FIX (#18): emit_strarray_data / emitstrarraydata apply the scalar-
|
||||
* str-global pattern (DATAW header with a zero ptr placeholder + inline
|
||||
* LE len, then a per-element `DATAR sym+idx*esz(SB),_S_n(SB)`) at each
|
||||
* element offset. let_pre_intern / letpreintern pre-intern each element
|
||||
* strlit so the _S_ rodata rows precede the DATAR references. Scoped to
|
||||
* the DATAW (`let`) directive — A_DATAR requires a DATAW holder.
|
||||
*
|
||||
* EACH ROW CARRIES BOTH DIMENSIONS (801 model):
|
||||
* (a) cstage `ww build` + run, asserting the exit — proves the table's
|
||||
* .len bytes are correct AND each element's .ptr relocation
|
||||
* resolves to the right rodata label (the rows deref a NON-zero
|
||||
* element's first byte, so a dropped/wrong reloc gives a wrong
|
||||
* char — built-in negative control).
|
||||
* (b) w6c vs w6c_ww `.s` cmp — FAILS if the stages diverge.
|
||||
*
|
||||
* GATE POLARITY: must stay GREEN. A wrong exit means the static table
|
||||
* relocations regressed; a byte-id FAIL means the stages diverged.
|
||||
*
|
||||
* NB: the rows read elements via the `.ptr`/`.len` pseudo-fields, NOT
|
||||
* the `len()` builtin — `len(xs[i])` on an indexed str element is a
|
||||
* separate pre-existing read-side miscompile (returns the ptr; filed
|
||||
* alongside #18), orthogonal to the emission fix under test here.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.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; int want_exit; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* .len bytes: 2+3+1 == 6. A dropped table would fail to link. */
|
||||
{ "strtab_len",
|
||||
"package main;\n"
|
||||
"let t: [3]str = [\"ab\",\"cde\",\"f\"];\n"
|
||||
"export fn main() i32 = { return (t[0].len + t[1].len + t[2].len): i32; };\n",
|
||||
6 },
|
||||
/* element-2 .ptr reloc: *(t[2].ptr) == 'C' (67), NOT 'A' — proves
|
||||
* the +2*esz relocation resolves to "C", not element 0. */
|
||||
{ "strtab_ptr_elem2",
|
||||
"package main;\n"
|
||||
"let t: [3]str = [\"A\",\"B\",\"C\"];\n"
|
||||
"export fn main() i32 = { let p: *u8 = t[2].ptr; return (*p): i32; };\n",
|
||||
67 },
|
||||
/* runtime-indexed .ptr: i=1, *(t[i].ptr) == 'y' (121) — the
|
||||
* LEAQ tab(SB)+i*esz addressing reaches the right element's reloc. */
|
||||
{ "strtab_ptr_varindex",
|
||||
"package main;\n"
|
||||
"let t: [3]str = [\"xx\",\"yyy\",\"z\"];\n"
|
||||
"export fn main() i32 = { let i: i32 = 1; let p: *u8 = t[i].ptr; "
|
||||
"return (*p): i32; };\n",
|
||||
121 },
|
||||
/* empty-element (no reloc, len 0) followed by a real element: t[0]
|
||||
* len==0, *(t[1].ptr) == 'Z' (90) — the empty slot must not shift
|
||||
* the following element's reloc offset. */
|
||||
{ "strtab_empty_then_ptr",
|
||||
"package main;\n"
|
||||
"let t: [2]str = [\"\",\"Z\"];\n"
|
||||
"export fn main() i32 = { if (t[0].len != 0) { return 88; }; "
|
||||
"let p: *u8 = t[1].ptr; return (*p): i32; };\n",
|
||||
90 },
|
||||
/* repeat suffix `[X, Y...]` — the trailing `...` fills the rest of
|
||||
* [3]str with the last explicit element ("Y"). t[2] is a repeat-
|
||||
* filled slot: *(t[2].ptr) == 'Y' (89), NOT 'X' — proves the +48
|
||||
* fill row's DATAR resolves to last_ev ("Y"), exercising the
|
||||
* repeat branch (its own len-fill + DATAR loop) the other rows skip. */
|
||||
{ "strtab_repeat_suffix",
|
||||
"package main;\n"
|
||||
"let t: [3]str = [\"X\",\"Y\"...];\n"
|
||||
"export fn main() i32 = { let p: *u8 = t[2].ptr; return (*p): i32; };\n",
|
||||
89 },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa);
|
||||
int cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char w6c[1100], w6c_ww[1100];
|
||||
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
||||
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
||||
if (access(w6c_ww, X_OK) != 0) {
|
||||
fprintf(stderr, "strarray_static: w6c_ww missing — cannot run "
|
||||
"the cs==ww byte-id gate\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int n = 0, fail = 0;
|
||||
for (int i = 0; rows[i].src; i++, n++) {
|
||||
char tmpdir[64];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwsas_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
char rmcmd[160];
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
char src[128], outbin[128];
|
||||
snprintf(src, sizeof src, "%s/wwsas_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/wwsas_%d_%d", tmpdir, getpid(), i);
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { runwait(rmcmd); fail++; continue; }
|
||||
fputs(rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
/* (a) cstage build + run. */
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s", bin, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage build failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
if (got != rows[i].want_exit) {
|
||||
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
|
||||
rows[i].label, got, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
|
||||
/* (b) cs==ww byte-id gate. */
|
||||
char cs_s[128], ws_s[128];
|
||||
snprintf(cs_s, sizeof cs_s, "%s/wwsas_%d_%d_cs.s", tmpdir, getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "%s/wwsas_%d_%d_ww.s", tmpdir, getpid(), i);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, cs_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww failed\n", rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage/wwstage .s DIFFER "
|
||||
"(rule-10 byte-id violation)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "%d/%d strarray static-init tests failed\n",
|
||||
fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("strarray_static: %d/%d ok (cstage run + cs==ww byte-id)\n",
|
||||
n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
/*
|
||||
* 923_signed_data_emit_run — top-level `let` bindings of signed
|
||||
* integer types must emit a module-scope DATA slot whose initialiser
|
||||
* is the literal's two's-complement bytes (#19).
|
||||
*
|
||||
* Pre-fix both stages skipped the scalar DATAW arm when the rhs was
|
||||
* N_UN(TK_MINUS, INTLIT). cstage's emit_lets fell through to the
|
||||
* default `else continue;` so `let x: i8 = -1i8;` produced no DATAW
|
||||
* row and the link failed with "undefined reference to x". Wwstage's
|
||||
* emitletdataw silently left the slot at zero — link succeeded but
|
||||
* the read returned 0 instead of -1, a corpus-coverage-blind bug.
|
||||
*
|
||||
* The array arm carried the same defect on both stages. cstage's
|
||||
* walker bailed on the first non-foldable element (then dropped the
|
||||
* whole DATAW row for the array via the `is_array continue` fall-
|
||||
* through); wwstage emitted zeros at every unfoldable index, again
|
||||
* silently. The utf8 DFA shape (`let dfa: [8]i8 = [0i8, -1i8, ...]`)
|
||||
* is the canonical real-source trigger.
|
||||
*
|
||||
* Sister to #24, which fixed the same N_UN-fold gap on the `def`-
|
||||
* emit side (emit_defs / emitdefconstants) via the shared
|
||||
* fold_int_literal / foldintliteral helper. Task #19 routes the
|
||||
* `let`-emit paths (scalar + array) through the same helper.
|
||||
*
|
||||
* Rows pin the full signed-int matrix (i8/i16/i32/i64) for both
|
||||
* scalar and 1D array shapes, plus positive controls so a future
|
||||
* regression of the gate that affects only the negative arm still
|
||||
* leaves the positive rows green. The negative-zero (TK_MINUS over
|
||||
* 0) and TK_TILDE rows pin the other two N_UN ops the helper covers.
|
||||
*
|
||||
* Stage matrix: cstage always; wwstage gated on `ww_ww` existing.
|
||||
* Runtime checks all read through cast-strip + literal compare so a
|
||||
* cgen-side narrow-load regression surfaces here rather than hiding
|
||||
* behind the DATA slot's wider-than-elem padding.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.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; int want; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "i8_neg_scalar",
|
||||
"let x: i8 = -1i8;\n"
|
||||
"fn main() i32 = { if (x != -1i8) { return 1; }; return 0; };\n",
|
||||
0 },
|
||||
{ "i8_pos_scalar",
|
||||
"let x: i8 = 42i8;\n"
|
||||
"fn main() i32 = { if (x != 42i8) { return 1; }; return 0; };\n",
|
||||
0 },
|
||||
{ "i16_neg_scalar",
|
||||
"let x: i16 = -2i16;\n"
|
||||
"fn main() i32 = { if (x != -2i16) { return 1; }; return 0; };\n",
|
||||
0 },
|
||||
{ "i16_pos_scalar",
|
||||
"let x: i16 = 1000i16;\n"
|
||||
"fn main() i32 = { if (x != 1000i16) { return 1; }; return 0; };\n",
|
||||
0 },
|
||||
{ "i32_neg_scalar",
|
||||
"let x: i32 = -100i32;\n"
|
||||
"fn main() i32 = { if (x != -100i32) { return 1; }; return 0; };\n",
|
||||
0 },
|
||||
{ "i32_pos_scalar",
|
||||
"let x: i32 = 100000i32;\n"
|
||||
"fn main() i32 = { if (x != 100000i32) { return 1; }; return 0; };\n",
|
||||
0 },
|
||||
{ "i64_neg_scalar",
|
||||
"let x: i64 = -1000i64;\n"
|
||||
"fn main() i32 = { if (x != -1000i64) { return 1; }; return 0; };\n",
|
||||
0 },
|
||||
{ "i64_pos_scalar",
|
||||
"let x: i64 = 1000000i64;\n"
|
||||
"fn main() i32 = { if (x != 1000000i64) { return 1; }; return 0; };\n",
|
||||
0 },
|
||||
/* TK_TILDE over a literal — the other N_UN op fold_int_literal
|
||||
* covers. ~0u64 == 0xFFFFFFFFFFFFFFFF == -1i64. */
|
||||
{ "i64_tilde_scalar",
|
||||
"let x: i64 = (~0u64): i64;\n"
|
||||
"fn main() i32 = { if (x != -1i64) { return 1; }; return 0; };\n",
|
||||
0 },
|
||||
/* Array of i8: utf8 DFA shape. The negative entries silently
|
||||
* zeroed pre-fix on wwstage; cstage emitted nothing at all and
|
||||
* the link failed before this row could even build. */
|
||||
{ "i8_arr_dfa",
|
||||
"let dfa: [8]i8 = [0i8, -1i8, 1i8, 2i8, 0i8, 0i8, -1i8, -1i8];\n"
|
||||
"fn main() i32 = {\n"
|
||||
" if (dfa[0] != 0i8) { return 1; };\n"
|
||||
" if (dfa[1] != -1i8) { return 2; };\n"
|
||||
" if (dfa[2] != 1i8) { return 3; };\n"
|
||||
" if (dfa[3] != 2i8) { return 4; };\n"
|
||||
" if (dfa[6] != -1i8) { return 5; };\n"
|
||||
" if (dfa[7] != -1i8) { return 6; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
{ "i16_arr_mixed",
|
||||
"let a: [4]i16 = [1i16, -2i16, 3i16, -4i16];\n"
|
||||
"fn main() i32 = {\n"
|
||||
" if (a[0] != 1i16) { return 1; };\n"
|
||||
" if (a[1] != -2i16) { return 2; };\n"
|
||||
" if (a[2] != 3i16) { return 3; };\n"
|
||||
" if (a[3] != -4i16) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
{ "i32_arr_mixed",
|
||||
"let a: [4]i32 = [10i32, -20i32, 30i32, -40i32];\n"
|
||||
"fn main() i32 = {\n"
|
||||
" if (a[0] != 10i32) { return 1; };\n"
|
||||
" if (a[1] != -20i32) { return 2; };\n"
|
||||
" if (a[2] != 30i32) { return 3; };\n"
|
||||
" if (a[3] != -40i32) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
{ "i64_arr_mixed",
|
||||
"let a: [4]i64 = [100i64, -200i64, 300i64, -400i64];\n"
|
||||
"fn main() i32 = {\n"
|
||||
" if (a[0] != 100i64) { return 1; };\n"
|
||||
" if (a[1] != -200i64) { return 2; };\n"
|
||||
" if (a[2] != 300i64) { return 3; };\n"
|
||||
" if (a[3] != -400i64) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
};
|
||||
|
||||
static int
|
||||
run_driver(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/sde_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/sde_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/sde_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -1; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s", driver, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
r->label, driver);
|
||||
runwait(rmcmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
return got;
|
||||
}
|
||||
|
||||
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 cdrv[640];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
char wdrv[640];
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
struct { const char *name; const char *path; int gated_on_existence; }
|
||||
drivers[] = {
|
||||
{ "cstage", cdrv, 0 },
|
||||
{ "wwstage", wdrv, 1 },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
for (int d = 0; drivers[d].name; d++) {
|
||||
if (drivers[d].gated_on_existence
|
||||
&& access(drivers[d].path, X_OK) != 0) {
|
||||
fprintf(stderr, "signed_data_emit: skip %s (no %s)\n",
|
||||
drivers[d].name, drivers[d].path);
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
int got = run_driver(drivers[d].path, &rows[i], i);
|
||||
total++;
|
||||
if (got != rows[i].want) {
|
||||
fprintf(stderr,
|
||||
"signed_data_emit[%s][%s]: exit=%d want=%d\n",
|
||||
drivers[d].name, rows[i].label,
|
||||
got, rows[i].want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -1,303 +0,0 @@
|
||||
/*
|
||||
* 946_const_slice_aggregate_run — #117/#119: const slice of (str,*fn)
|
||||
* tuple rows + scalar &fn globals emit DATA (was both-stage LOUD at
|
||||
* emit_slice_data / silent-no-DATA for the scalar &fn).
|
||||
*
|
||||
* #117: a `const [](str,*fn)` (fold-6's charclass_map shape) emitted NO
|
||||
* DATA — emit_slice_data rode the scalar emit_array_lit_bytes backing and
|
||||
* loud-stopped on the str/tuple/fn elements. Now the backing is k tuple
|
||||
* rows (emit_tuple_row_*): each str element a 24B header + ptr→char DATAR,
|
||||
* each &fn element an 8B slot + the BRAND-NEW &fn→DATAR reloc, at the 8B
|
||||
* tuple-slot offsets (str@row+0/24B, *fn@row+24/8B = 32B/row, ken-confirmed
|
||||
* layout). #119: a scalar `let p: *fn = &f` global wires the same reloc
|
||||
* helper at the emit_lets 8B-scalar site (pre-#119: no DATA → undefined
|
||||
* ref / garbage deref).
|
||||
*
|
||||
* POLARITY: align-BOTH — both stages were LOUD at base, no runtime
|
||||
* reference. byte-id is BLIND (#263): the runtime READ-BACK is the net.
|
||||
* The fn-reloc (the genuinely new machinery) is pinned at runtime with
|
||||
* DISTINCT fns per row, so a wrong reloc is caught.
|
||||
*
|
||||
* READ-PATH NOTE (honest boundary): the indexed/ptr tuple-element FIELD
|
||||
* reads off a const slice (`tbl[i].0`, `(&tbl[i]).0`) and the whole-element
|
||||
* register-cursor read are pre-existing-broken (the #37/#58 index-cursor
|
||||
* truncation + "unsupported field-read shape" — separate mechanisms, filed
|
||||
* siblings; fold-6's compile arm also needs them). The one runtime
|
||||
* observation available of the BACKING is the whole-element bind's word0
|
||||
* (loaded correctly even by the truncating cursor): so the fn-reloc rows
|
||||
* put the *fn FIRST (word0) to read it back, and the consumer-ordering
|
||||
* (str,*fn) rows pin `len(tbl)` (header) + cs==ww byte-id on the DATA. The
|
||||
* str-element header+reloc reuses the proven emit_tuple_data machinery
|
||||
* (941 t3_global_elem runtime-pinned) — verified here by byte-id.
|
||||
*
|
||||
* NNN<950, self-contained (/tmp, no imports) — rule-14's selfhost-sibling
|
||||
* race does not apply (941/944/945 precedent). Every K_RUN row also pins
|
||||
* cstage/wwstage asm byte-id.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa), cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define K_RUN 0 /* build+run both drivers, exit==want, + cs==ww byte-id */
|
||||
#define K_BUILDERR 1 /* build must FAIL with experr on BOTH drivers (rule 7) */
|
||||
|
||||
struct row { const char *label; const char *src; int want;
|
||||
int kind; const char *experr; };
|
||||
|
||||
static int
|
||||
errlog_has(const char *path, const char *needle)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return 0;
|
||||
char buf[8192];
|
||||
size_t got = fread(buf, 1, sizeof buf - 1, f);
|
||||
fclose(f);
|
||||
buf[got] = '\0';
|
||||
return strstr(buf, needle) != NULL;
|
||||
}
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* #117 fn-reloc readback: *fn FIRST so the whole-element bind's
|
||||
* word0 (the only backing word the truncating index-cursor loads
|
||||
* correctly) IS the fn ptr. DISTINCT fns per row — a wrong reloc
|
||||
* (or a swapped per-row offset) is caught by calling each back. */
|
||||
{ "fnfirst_reloc",
|
||||
"package main;\n"
|
||||
"fn fa(c: rune) bool = { return c == 'a'; };\n"
|
||||
"fn fz(c: rune) bool = { return c == 'z'; };\n"
|
||||
"const tbl: [](*fn(c: rune) bool, str) = [(&fa, \"aa\"), (&fz, \"zzz\")];\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let e0 = tbl[0];\n"
|
||||
" let e1 = tbl[1];\n"
|
||||
" let f0 = e0.0;\n"
|
||||
" let f1 = e1.0;\n"
|
||||
" if (!(*f0)('a')) { return 1; };\n"
|
||||
" if ((*f0)('z')) { return 2; };\n"
|
||||
" if (!(*f1)('z')) { return 3; };\n"
|
||||
" if ((*f1)('a')) { return 4; };\n"
|
||||
" if (len(tbl) != 2) { return 5; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* fold-6's exact consumer ordering: (str,*fn). The str + fn element
|
||||
* relocs at the consumer-ordering slot offsets are pinned by cs==ww
|
||||
* byte-id on the DATA; len(tbl) confirms the slice header resolves. */
|
||||
{ "consumer_str_fn",
|
||||
"package main;\n"
|
||||
"fn fa(c: rune) bool = { return c == 'a'; };\n"
|
||||
"fn fz(c: rune) bool = { return c == 'z'; };\n"
|
||||
"const tbl: [](str, *fn(c: rune) bool) = [(\"aa\", &fa), (\"zzz\", &fz)];\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (len(tbl) != 2) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* multi-row str backing: 3 rows of distinct str lengths — pins the
|
||||
* per-row 32B stride of the str-header backing (byte-id) + header. */
|
||||
{ "consumer_3row",
|
||||
"package main;\n"
|
||||
"fn fa(c: rune) bool = { return c == 'a'; };\n"
|
||||
"const tbl: [](str, *fn(c: rune) bool) = "
|
||||
"[(\"a\", &fa), (\"bb\", &fa), (\"ccc\", &fa)];\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (len(tbl) != 3) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* #117 broad-arm pin (rob): the TY_TUPLE arm also backs an ACCEPTED
|
||||
* non-(str,*fn) scalar-tuple slice — (i64,str) with a TYPED i64 (a bare
|
||||
* untyped int is ww-checker-rejected, #120). Scalar FIRST so the i64
|
||||
* reads back via the word0-correct whole-element cursor (#121 read-path:
|
||||
* .1/word3 is truncated, same constraint as the fn-first row). DISTINCT
|
||||
* values catch a wrong per-row stride/offset; len(t) pins the header; the
|
||||
* str element rides the 32B backing (its DATAR + len pinned by cs==ww
|
||||
* byte-id). Base f8be2ae has no TY_TUPLE arm → louds (broad arm is new). */
|
||||
{ "scalar_tuple_slice",
|
||||
"package main;\n"
|
||||
"const t: [](i64, str) = [(10i64, \"a\"), (20i64, \"bb\")];\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let e0 = t[0];\n"
|
||||
" let e1 = t[1];\n"
|
||||
" let v0 = e0.0;\n"
|
||||
" let v1 = e1.0;\n"
|
||||
" if (v0 != 10) { return 1; };\n"
|
||||
" if (v1 != 20) { return 2; };\n"
|
||||
" if (len(t) != 2) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* #119: scalar &fn module-globals — DISTINCT fns, each called back
|
||||
* through the reloc (pre-#119: no DATA → undefined ref / garbage). */
|
||||
{ "scalar_fnptr",
|
||||
"package main;\n"
|
||||
"fn fa(c: rune) bool = { return c == 'a'; };\n"
|
||||
"fn fz(c: rune) bool = { return c == 'z'; };\n"
|
||||
"let pf: *fn(c: rune) bool = &fa;\n"
|
||||
"let pg: *fn(c: rune) bool = &fz;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (!(*pf)('a')) { return 1; };\n"
|
||||
" if ((*pf)('z')) { return 2; };\n"
|
||||
" if (!(*pg)('z')) { return 3; };\n"
|
||||
" if ((*pg)('a')) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* honest boundary (rule 7): a non-tuple aggregate element (here a
|
||||
* bare []str) stays LOUD, symmetric both stages — #117 is NARROW to
|
||||
* the (str,*fn) tuple form, not the full slice-of-aggregate family. */
|
||||
{ "loud_slice_of_str",
|
||||
"package main;\n"
|
||||
"const xs: []str = [\"a\", \"b\"];\n"
|
||||
"export fn main() i32 = { return len(xs): i32; };\n", 0,
|
||||
K_BUILDERR, "slice-of-{str,slice,tagged}" },
|
||||
};
|
||||
|
||||
static int
|
||||
run_driver(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char tmpdir[96], src[128], outbin[128], errf[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/csa_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/csa_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/csa_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(errf, sizeof errf, "%s/err", tmpdir);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -1; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s >/dev/null 2>%s",
|
||||
driver, outbin, src, errf);
|
||||
int brc = runwait(cmd);
|
||||
if (r->kind == K_BUILDERR) {
|
||||
int ok = (brc != 0)
|
||||
&& (r->experr == NULL || errlog_has(errf, r->experr));
|
||||
if (!ok)
|
||||
fprintf(stderr, "row[%s]: %s expected loud builderr "
|
||||
"\"%s\" (brc=%d)\n", r->label, driver,
|
||||
r->experr ? r->experr : "", brc);
|
||||
runwait(rmcmd);
|
||||
return ok ? 0 : 1;
|
||||
}
|
||||
if (brc != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
r->label, driver);
|
||||
runwait(rmcmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
if (got != r->want) {
|
||||
fprintf(stderr, "row[%s]: %s exit %d, want %d\n",
|
||||
r->label, driver, got, r->want);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
asm_byte_identical(const char *bin, const struct row *r, int i)
|
||||
{
|
||||
char src[96], cs[96], ws[96], cmd[1024];
|
||||
snprintf(src, sizeof src, "/tmp/csa_asm_%d_%d.ww", getpid(), i);
|
||||
snprintf(cs, sizeof cs, "/tmp/csa_asm_%d_%d_c.s", getpid(), i);
|
||||
snprintf(ws, sizeof ws, "/tmp/csa_asm_%d_%d_w.s", getpid(), i);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
|
||||
unlink(src);
|
||||
return -1;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
|
||||
bin, ws, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
|
||||
unlink(src); unlink(cs);
|
||||
return -1;
|
||||
}
|
||||
int rc = slurp_eq(cs, ws);
|
||||
if (rc != 0)
|
||||
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
|
||||
r->label);
|
||||
unlink(src); unlink(cs); unlink(ws);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[2080];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[2120], wdrv[2120];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (run_driver(cdrv, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
if (access(wdrv, X_OK) == 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (run_driver(wdrv, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (rows[i].kind == K_BUILDERR)
|
||||
continue;
|
||||
total++;
|
||||
if (asm_byte_identical(bin, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "const_slice_aggregate: %d/%d checks failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("const_slice_aggregate: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,276 +0,0 @@
|
||||
/*
|
||||
* 947_inferred_scalar_global_run — runtime + byte-id net for #66(b-i): an
|
||||
* INFERRED int-literal module-global `let s = 42;` (no type annotation) was
|
||||
* wwstage SILENT-WRONG. The checker stamps such a global with an
|
||||
* N_TNAME("untyped_int") annotation; letemitsize / emitletdataw / the cgident
|
||||
* global-read arm key on that annotation's name, which letscalarprim doesn't
|
||||
* recognise — so the global was dropped from collectlets (no DATAW emitted)
|
||||
* AND the read fell to the silent module-leaf (no `MOVQ main.s(SB)`), leaving
|
||||
* `MOVSXD` to sign-extend a STALE AX. cstage instead type_default's the untyped
|
||||
* int to the 8B machine word before emit, so it produced the correct
|
||||
* `DATAW main.s` + `MOVQ main.s(SB), AX` (exit 42). cs≠ww, ww silent-wrong.
|
||||
*
|
||||
* Fix (wwstage only, cgen.ww defaultinferredlets, one choke-point before
|
||||
* collectlets): rewrite the untyped_int annotation to the concrete machine
|
||||
* word `int` — NOT i32 (that's the #108 truncation trap, opposite polarity) —
|
||||
* so all three consumers resolve it as an 8B int global. The emitted DATAW +
|
||||
* MOVQ then match cstage byte-for-byte (align ww UP). Guarded to N_INTLIT rhs
|
||||
* ONLY: a const-EXPR inferred global (`let s = 7*6`) is #66(b-ii), a SEPARATE
|
||||
* loud both-stage no-DATA gap, and must stay on its loud route.
|
||||
*
|
||||
* Each row carries (a) a cstage `ww build` + run asserting the exit code (R1
|
||||
* pre-fix: wwstage exit 136 / asm differs), and (b) a w6c vs w6c_ww `.s` cmp
|
||||
* (rule-10 byte-id). R2 is the typed-annotation control (already cs==ww/42),
|
||||
* locking no regression on the annotated path.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.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; int want_exit; int want_err; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* R1 — the #66(b-i) repro: inferred int-literal module-global, read
|
||||
* back from main. Pre-fix wwstage emitted neither the DATAW nor the
|
||||
* MOVQ → MOVSXD on stale AX → exit 136. Post-fix cs==ww, exit 42. */
|
||||
{ "inferred",
|
||||
"package main;\n"
|
||||
"let s = 42;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 42, 0 },
|
||||
/* R2 — typed-annotation control: `let s: i64 = 42;` already worked
|
||||
* (cs==ww, DATA emitted). Regression guard for the annotated path. */
|
||||
{ "typed_ctrl",
|
||||
"package main;\n"
|
||||
"let s: i64 = 42;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 42, 0 },
|
||||
/* R3 — #134 neg leg: inferred unary-over-int-literal module-global
|
||||
* `let s = -42;` (rhs N_UN(TK_MINUS) over N_INTLIT). Pre-fix wwstage
|
||||
* emitted neither DATAW nor MOVQ (un-defaulted untyped_int annotation)
|
||||
* → exit 168 (garbage). cstage folds the unary, emits DATAW + load
|
||||
* (exit 214 = -42 low-8). Post-fix defaultinferredlets peels the unary
|
||||
* and defaults to `int`, so cs==ww and both run -42 (exit 214). */
|
||||
{ "neg_inferred",
|
||||
"package main;\n"
|
||||
"let s = -42;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 214, 0 },
|
||||
/* R4 — #134 neg typed control: `let s: int = -42;` (TYPED). Already
|
||||
* cs==ww today (concrete annotation → letscalarprim fires). Locks the
|
||||
* construction proof: R3's emitted .s is byte-identical to this typed
|
||||
* control (the inferred decl IS the typed decl after defaulting). */
|
||||
{ "neg_typed_ctrl",
|
||||
"package main;\n"
|
||||
"let s: int = -42;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 214, 0 },
|
||||
/* #133 — const-EXPR scalar module-global. The let pass-2 arm now
|
||||
* const-folds an N_BIN / unary-over-N_BIN / def-ref rhs and stamps it
|
||||
* to an N_INTLIT (mirroring the N_DEF arm), so cgen's literal-only
|
||||
* DATA emitter lays the row in BOTH stages. Pre-fix: cstage LINK-FAILed
|
||||
* (`undefined main.s` — read emitted, no DATA word) and wwstage was
|
||||
* SILENT-WRONG (no DATA, no load, MOVSXD on stale AX → exit 152). */
|
||||
|
||||
/* C1 — inferred const-expr `let s = 7*6;` → 42 (N_BIN rhs). */
|
||||
{ "const_inferred",
|
||||
"package main;\n"
|
||||
"let s = 7 * 6;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 42, 0 },
|
||||
/* C2 — typed const-expr (b-ii) `let s: i64 = 7*6;` → 42. Pre-fix this
|
||||
* LINK-FAILed BOTH stages (annotation-independent no-DATA gap); the
|
||||
* same stamp supplies the foldable value. */
|
||||
{ "const_typed",
|
||||
"package main;\n"
|
||||
"let s: i64 = 7 * 6;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 42, 0 },
|
||||
/* C3 — def-ref through let `def K: int = 6; let s = K*7;` → 42. Proves
|
||||
* eval_def_const's N_IDENT def-resolution reaches through the new let
|
||||
* hook. */
|
||||
{ "const_defref",
|
||||
"package main;\n"
|
||||
"def K: int = 6;\n"
|
||||
"let s = K * 7;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 42, 0 },
|
||||
/* C4 — unary-over-binop `let s = -(2*3);` → -6 (low byte 250). Proves
|
||||
* the N_UN ∘ N_BIN composition folds through eval_def_const. */
|
||||
{ "const_unary_binop",
|
||||
"package main;\n"
|
||||
"let s = -(2 * 3);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 250, 0 },
|
||||
/* C5 — loud guard: a div-by-zero const-expr module-global init stays a
|
||||
* compile ERROR in BOTH stages, NOT silently zeroed. Asserts the
|
||||
* eval_def_const error path is preserved through the let hook. */
|
||||
{ "const_divzero",
|
||||
"package main;\n"
|
||||
"let s = 7 / 0;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return s: i32;\n"
|
||||
"};\n", 0, 1 },
|
||||
{ NULL, NULL, 0, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa);
|
||||
int cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char w6c[1100], w6c_ww[1100];
|
||||
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
||||
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
||||
if (access(w6c_ww, X_OK) != 0) {
|
||||
fprintf(stderr, "infglobal: w6c_ww missing — cannot run the "
|
||||
"cs==ww byte-id gate (the whole point of this test)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int n = 0, fail = 0;
|
||||
for (int i = 0; rows[i].src; i++, n++) {
|
||||
char tmpdir[64];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwisg_%d_d_%d",
|
||||
getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
|
||||
char src[128], rmcmd[160];
|
||||
snprintf(src, sizeof src, "%s/wwisg_%d_%d.ww",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { runwait(rmcmd); fail++; continue; }
|
||||
fputs(rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
char cs_s[128], ws_s[128];
|
||||
snprintf(cs_s, sizeof cs_s, "%s/wwisg_%d_%d_cs.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "%s/wwisg_%d_%d_ww.s",
|
||||
tmpdir, getpid(), i);
|
||||
|
||||
/* Loud-guard rows (#133 R5): both stages must REJECT the
|
||||
* source at compile (e.g. a div-by-zero const-expr init). No
|
||||
* value to run / no byte-id — only the error-path preservation
|
||||
* matters. Assert raw w6c AND w6c_ww both emit non-zero. */
|
||||
if (rows[i].want_err) {
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c, cs_s, src);
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr, "row[%s]: w6c accepted a source "
|
||||
"that must be rejected (loud guard)\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww accepted a "
|
||||
"source that must be rejected (loud "
|
||||
"guard)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* (a) cstage build + run. */
|
||||
char outbin[128];
|
||||
snprintf(outbin, sizeof outbin, "%s/wwisg_%d_%d",
|
||||
tmpdir, getpid(), i);
|
||||
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
|
||||
bin, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage build failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
if (got != rows[i].want_exit) {
|
||||
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
|
||||
rows[i].label, got, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
|
||||
/* (b) cs==ww byte-id gate. */
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c, cs_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww failed\n",
|
||||
rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: cstage/wwstage .s DIFFER (rule-10 "
|
||||
"byte-id violation)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "%d/%d inferred-scalar-global tests failed\n",
|
||||
fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("infglobal: %d/%d ok (cstage run + cs==ww byte-id)\n", n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
/*
|
||||
* 949_structlit_arrfield_run — runtime + byte-id net for #249: struct
|
||||
* array-field init/read silent miscompiles. Two distinct roots, both
|
||||
* gate-blind (cs==ww broken identically pre-fix):
|
||||
*
|
||||
* BUG B — reading an array field of a module-GLOBAL struct value
|
||||
* (`G.arr[i]`). The N_INDEX fallback's cg_dotbase_addr / dotbaseaddr
|
||||
* helper (the #135 sibling) had no module-global-struct base arm:
|
||||
* cstage emitted `LEAQ (BP)` (read the stack → 0), wwstage fell to
|
||||
* cgexpr(N_DOT) which loaded the field VALUE as a pointer → SEGFAULT.
|
||||
* The .data was already correct (emit_struct_lit_bytes #129 A.3); only
|
||||
* the READ base address was wrong. Fix: a global value-struct base
|
||||
* emits `LEAQ name(SB) (+ ADDQ field_off)`, mirroring the scalar
|
||||
* global-field read (cgen.c:7532). const globals are def_isstructdef.
|
||||
*
|
||||
* BUG A — initializing an array field from a struct literal
|
||||
* (`e{ arr = [..] }`) as a local. cg_structlit_fill / cgstructlitfill
|
||||
* had no TY_ARRAY field arm; the array field fell to the generic
|
||||
* scalar tail (cgexpr the N_ARRLIT → AX, store one sized word) which
|
||||
* silently DROPPED every element. Fix: a TY_ARRAY field arm element-
|
||||
* wise stores the N_ARRLIT at base+field_off+i*esz, reusing the proven
|
||||
* N_LET array-init shape. The GLOBAL literal-init path is unaffected
|
||||
* (it goes through emit_struct_lit_bytes, already correct).
|
||||
*
|
||||
* cstage `ww build` + run for exit code; w6c vs w6c_ww `.s` cmp for the
|
||||
* rule-10 byte-id gate. u8 element rows only (a `[N]u8` read cast to i32
|
||||
* is byte-id; wider widths hit the i32-return MOVL/MOVSXD cs/ww
|
||||
* divergence — see 949_dotbase_arr_run's deferral note).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.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; int want_exit; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* BUG B: read [4]u8 field of a module-global `let` struct at idx 0
|
||||
* (field_off 0). Pre-fix cstage→0, wwstage→SEGFAULT. encmap[0]='A'
|
||||
* (65). */
|
||||
{ "global_let_read",
|
||||
"package main;\n"
|
||||
"type e = struct { encmap: [4]u8 };\n"
|
||||
"let g: e = e { encmap = [65u8, 66u8, 67u8, 68u8] };\n"
|
||||
"export fn main() i32 = { return g.encmap[0]: i32; };\n", 65 },
|
||||
/* BUG B: read [4]u8 field of a module-global `def` (const) struct
|
||||
* at idx 2, with a non-zero field offset (a [3]u8 pad ahead of it) —
|
||||
* exercises def_isstructdef + the ADDQ $field_off arm. The base64
|
||||
* `const std_encoding` shape. pad ahead, encmap[2]='C' (67). */
|
||||
{ "global_def_read_off",
|
||||
"package main;\n"
|
||||
"type e = struct { pad: [3]u8, encmap: [4]u8 };\n"
|
||||
"def G: e = e { pad = [9u8, 9u8, 9u8],"
|
||||
" encmap = [65u8, 66u8, 67u8, 68u8] };\n"
|
||||
"export fn main() i32 = { return G.encmap[2]: i32; };\n", 67 },
|
||||
/* BUG A: local struct-literal init of a [4]u8 field, read idx 0.
|
||||
* Pre-fix the array field fell to the generic scalar tail (cgexpr
|
||||
* the N_ARRLIT → AX, store one word) and silently dropped every
|
||||
* element → 0. encmap[0]='A' (65). */
|
||||
{ "local_lit_read0",
|
||||
"package main;\n"
|
||||
"type e = struct { encmap: [4]u8 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let g: e = e { encmap = [65u8, 66u8, 67u8, 68u8] };\n"
|
||||
" return g.encmap[0]: i32;\n"
|
||||
"};\n", 65 },
|
||||
/* BUG A: same init, read idx 3 — asserts the LAST element landed
|
||||
* (the pre-fix single-word store would never reach it). 'D' (68). */
|
||||
{ "local_lit_read3",
|
||||
"package main;\n"
|
||||
"type e = struct { encmap: [4]u8 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let g: e = e { encmap = [65u8, 66u8, 67u8, 68u8] };\n"
|
||||
" return g.encmap[3]: i32;\n"
|
||||
"};\n", 68 },
|
||||
/* NB: a trailing `...` repeat in a struct-LITERAL array field
|
||||
* (`encmap = [7u8...]`) is rejected by the CHECKER ("[1]u8 not
|
||||
* assignable to [4]u8") — the field type-check doesn't apply the
|
||||
* repeat-length inference that bare `let a: [N]T = [v...]` gets.
|
||||
* The cg_structlit_fill array arm mirrors the N_LET `...` handling
|
||||
* for symmetry, but that path is checker-unreachable today (separate
|
||||
* checker gap, not #249). No row exercises it. */
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa);
|
||||
int cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char w6c[1100], w6c_ww[1100];
|
||||
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
||||
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
||||
if (access(w6c_ww, X_OK) != 0) {
|
||||
fprintf(stderr, "structlit_arrfield: w6c_ww missing — cannot run "
|
||||
"the cs==ww byte-id gate (the whole point of this test)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int n = 0, fail = 0;
|
||||
for (int i = 0; rows[i].src; i++, n++) {
|
||||
char tmpdir[64];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwsaf_%d_d_%d",
|
||||
getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
|
||||
char src[128], outbin[128], cs_s[160], ws_s[160], rmcmd[160];
|
||||
snprintf(src, sizeof src, "%s/wwsaf_%d_%d.ww",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/wwsaf_%d_%d",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(cs_s, sizeof cs_s, "%s/wwsaf_%d_%d_cs.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "%s/wwsaf_%d_%d_ww.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { fail++; runwait(rmcmd); continue; }
|
||||
fputs(rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
|
||||
bin, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage build failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
if (got != rows[i].want_exit) {
|
||||
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
|
||||
rows[i].label, got, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c, cs_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww failed\n",
|
||||
rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: cstage/wwstage .s DIFFER (rule-10 "
|
||||
"byte-id violation)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "%d/%d structlit-arrfield tests failed\n",
|
||||
fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("structlit_arrfield: %d/%d ok (cstage run + cs==ww byte-id)\n",
|
||||
n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
/*
|
||||
* 949_valstruct_subsize_run — runtime + byte-id net for #254: a sub-8
|
||||
* (non-8-multiple) nested value-struct must size its zero-init extent
|
||||
* from the type table's natural ABI size (cstage lu->size), NOT the
|
||||
* slot-padded register-struct width.
|
||||
*
|
||||
* Root: wwstage conflated SLOT-size (round-to-8, for frame layout) with
|
||||
* ABI-size (true). A nested value-struct field was sized via fieldsize()
|
||||
* (cgenutil.ww TY_STRUCT -> ti.slotsize = 8), poisoning structabisize +
|
||||
* registerstruct si.totsize to 8 for a struct whose true ABI size is 4.
|
||||
* Two emission sites then over-sized:
|
||||
* D1 (local): cglet zsz = structabisize = 8 hit the `zsz == 8` zero
|
||||
* arm (cgenstmt.ww #213) -> a stray `MOVQ $0, off(BP)` that cstage
|
||||
* (ABI 4 is sub-8 -> left uninit per the shared no-rhs policy)
|
||||
* never emits.
|
||||
* D2 (global): emitletdataw struct arm wrote si.totsize = 8 zero bytes
|
||||
* of DATAW; cstage cg_let_emit_size returns u->size = 4.
|
||||
* Both were SILENT cs!=ww divergences (gate-blind: a standalone wwstage
|
||||
* is self-consistent; only the cs==ww .s cmp catches it).
|
||||
*
|
||||
* Fix (rule-13 SSoT): both sites source the extent from tinfo.size
|
||||
* (peeling TY_NAMED), the same value cstage reads. fieldsize /
|
||||
* registerstruct / frame slot-padding stay UNTOUCHED — moving the fix
|
||||
* into the size helpers would shift nested-struct field offsets and
|
||||
* re-diverge other byte-id.
|
||||
*
|
||||
* Rows cover the whole sub-8 class (ABI size 1/2/4) in both the local
|
||||
* (D1) and global (D2) emission contexts, plus a >8 NEGATIVE control
|
||||
* proving the fix didn't disable legitimate multi-word zero-init. Each
|
||||
* row: cstage `ww build` + run for the exit code (correctness) AND
|
||||
* w6c vs w6c_ww `.s` cmp for rule-10 byte-id (the silent-divergence net).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.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; int want_exit; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* D1 local, ABI size 4 (inner {[4]u8}). Pre-fix wwstage emitted a
|
||||
* stray MOVQ $0 cstage didn't -> .s differ. Write+read a byte so
|
||||
* the exit is deterministic (sub-8 is left uninit by BOTH stages,
|
||||
* matching cstage's no-rhs policy). */
|
||||
{ "d1_local_4",
|
||||
"package main;\n"
|
||||
"type inner = struct { m: [4]u8 };\n"
|
||||
"type outv = struct { i: inner };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let o: outv;\n"
|
||||
" o.i.m[0] = 66u8;\n"
|
||||
" return o.i.m[0]: i32;\n"
|
||||
"};\n", 66 },
|
||||
/* D1 local, ABI size 2 ([2]u8). */
|
||||
{ "d1_local_2",
|
||||
"package main;\n"
|
||||
"type inner = struct { m: [2]u8 };\n"
|
||||
"type outv = struct { i: inner };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let o: outv;\n"
|
||||
" o.i.m[1] = 55u8;\n"
|
||||
" return o.i.m[1]: i32;\n"
|
||||
"};\n", 55 },
|
||||
/* D1 local, ABI size 1 ([1]u8) — the tightest sub-8 case. */
|
||||
{ "d1_local_1",
|
||||
"package main;\n"
|
||||
"type inner = struct { m: [1]u8 };\n"
|
||||
"type outv = struct { i: inner };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let o: outv;\n"
|
||||
" o.i.m[0] = 44u8;\n"
|
||||
" return o.i.m[0]: i32;\n"
|
||||
"};\n", 44 },
|
||||
/* D2 global, ABI size 4. Pre-fix wwstage emitted DATAW of 8 zero
|
||||
* bytes vs cstage's 4 -> .s differ. */
|
||||
{ "d2_global_4",
|
||||
"package main;\n"
|
||||
"type inner = struct { m: [4]u8 };\n"
|
||||
"type outv = struct { i: inner };\n"
|
||||
"let g: outv;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" g.i.m[0] = 66u8;\n"
|
||||
" return g.i.m[0]: i32;\n"
|
||||
"};\n", 66 },
|
||||
/* D2 global, ABI size 2. */
|
||||
{ "d2_global_2",
|
||||
"package main;\n"
|
||||
"type inner = struct { m: [2]u8 };\n"
|
||||
"type outv = struct { i: inner };\n"
|
||||
"let g: outv;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" g.i.m[1] = 55u8;\n"
|
||||
" return g.i.m[1]: i32;\n"
|
||||
"};\n", 55 },
|
||||
/* D2 global, ABI size 1. */
|
||||
{ "d2_global_1",
|
||||
"package main;\n"
|
||||
"type inner = struct { m: [1]u8 };\n"
|
||||
"type outv = struct { i: inner };\n"
|
||||
"let g: outv;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" g.i.m[0] = 44u8;\n"
|
||||
" return g.i.m[0]: i32;\n"
|
||||
"};\n", 44 },
|
||||
/* NEGATIVE control — a >8 (multi-word) value-struct still zero-
|
||||
* inits. Read an UNWRITTEN byte: a working multi-word zero-init
|
||||
* fill makes it 0. If the fix had wrongly suppressed the >8 zero
|
||||
* arm, this would read stack garbage (and byte-id would diff
|
||||
* against the still-zeroing cstage). Local + global both proven. */
|
||||
{ "ctl_local_16",
|
||||
"package main;\n"
|
||||
"type inner = struct { m: [16]u8 };\n"
|
||||
"type outv = struct { i: inner };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let o: outv;\n"
|
||||
" return o.i.m[7]: i32;\n"
|
||||
"};\n", 0 },
|
||||
{ "ctl_global_16",
|
||||
"package main;\n"
|
||||
"type inner = struct { m: [16]u8 };\n"
|
||||
"type outv = struct { i: inner };\n"
|
||||
"let g: outv;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return g.i.m[7]: i32;\n"
|
||||
"};\n", 0 },
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa);
|
||||
int cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char w6c[1100], w6c_ww[1100];
|
||||
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
||||
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
||||
if (access(w6c_ww, X_OK) != 0) {
|
||||
fprintf(stderr, "valstruct_subsize: w6c_ww missing — cannot run "
|
||||
"the cs==ww byte-id gate (the whole point of this test)\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int n = 0, fail = 0;
|
||||
for (int i = 0; rows[i].src; i++, n++) {
|
||||
char tmpdir[64];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwvss_%d_d_%d",
|
||||
getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
|
||||
char rmcmd[160];
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
char src[128], outbin[128], cs_s[128], ws_s[128];
|
||||
snprintf(src, sizeof src, "%s/wwvss_%d_%d.ww",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/wwvss_%d_%d",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(cs_s, sizeof cs_s, "%s/wwvss_%d_%d_cs.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(ws_s, sizeof ws_s, "%s/wwvss_%d_%d_ww.s",
|
||||
tmpdir, getpid(), i);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (f == NULL) { fail++; runwait(rmcmd); continue; }
|
||||
fputs(rows[i].src, f);
|
||||
fclose(f);
|
||||
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd, "%s/ww build -o %s %s",
|
||||
bin, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: cstage build failed\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
if (got != rows[i].want_exit) {
|
||||
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
|
||||
rows[i].label, got, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c, cs_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
|
||||
w6c_ww, ws_s, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww failed\n",
|
||||
rows[i].label);
|
||||
fail++; runwait(rmcmd); continue;
|
||||
}
|
||||
if (slurp_eq(cs_s, ws_s) != 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: cstage/wwstage .s DIFFER (rule-10 "
|
||||
"byte-id violation)\n", rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
runwait(rmcmd);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "%d/%d valstruct-subsize tests failed\n",
|
||||
fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("valstruct_subsize: %d/%d ok (cstage run + cs==ww byte-id)\n",
|
||||
n, n);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
* 989_nestfield_run — #44/#55 struct-layout SSoT: a struct with a nested
|
||||
* sub-8 composite field plus a successor must address EVERY field at the
|
||||
* checker's natural offset, identically on the write (construction) and
|
||||
* read (field-access) paths.
|
||||
*
|
||||
* THE BUG (cat-A silent miscompile, gate-blind): wwstage had TWO struct-
|
||||
* layout sources. `registerstruct` (selfhost/cmd/wcc/cgenutil.ww) rebuilt
|
||||
* each field's `fi.foff` via `fieldsize` — SLOT-padded, so a nested
|
||||
* `inner{x:u8,y:u8}` (size 2, slotsize 8) pushed every successor to an 8B
|
||||
* boundary. The READ path (cgplaceaddr/dotbaseaddr) reads the checker's
|
||||
* tfield.offset — NATURAL (inner align 1 → p at offset 1, z packed right
|
||||
* after). So ww WROTE p/z at the slot-padded offset and READ them at the
|
||||
* natural offset → garbage. The shape (nested sub-8 composite + a field
|
||||
* after it) is corpus-ABSENT — ww uses both sources on its OWN structs, so
|
||||
* if such a struct existed the bootstrap would mis-address itself and
|
||||
* 400-green would be impossible; the gate cannot see it, this repro is the
|
||||
* proof. cstage has no structinfo — it reads tfield directly, self-
|
||||
* consistently natural (cmd/w6c/cgen.c). THE FIX: make ww's `fi.foff` a
|
||||
* VIEW of tfield.offset (lock-step walk tstruct.list + ti.fields), so the
|
||||
* second source collapses onto cstage's natural one (cs==ww preserved).
|
||||
*
|
||||
* Each program self-checks every field (write 1/2/3/.., read back, return
|
||||
* the 1-based index of the first mismatch, 0 on all-correct). Pre-fix ww
|
||||
* constructs at slot offsets and reads at natural → a non-zero return on
|
||||
* the first composite-or-successor field, so cs(=0) != ww(!=0) AND ww !=
|
||||
* want. Both stages build+run (rule-10); the want is the absolute 0.
|
||||
*
|
||||
* This is a RUNTIME cs==ww check (both stages exit 0): it proves field
|
||||
* offsets are natural and instruction-correct RELATIVE TO the struct base.
|
||||
* It deliberately does NOT gate the absolute .s frame, which is still
|
||||
* cs!=ww on this shape via a SEPARATE pre-existing producer — wwstage
|
||||
* reserves the struct LOCAL's stack slot at slot-padded slotsize, cstage
|
||||
* at natural (task #75). The frame-absolute teeth belong to #75's fix.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.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;
|
||||
int want_exit; /* >= 0: pin the absolute value; -1: cs==ww only */
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* (1) nested2 — outer2{a:u8, p:inner}: the core divergence. Natural
|
||||
* p at offset 1; pre-fix slot-padded p at offset 8. Construction
|
||||
* (write) vs field-access (read) disagree → o.p.x / o.p.y read wrong.
|
||||
* Returns the 1-based index of the first mismatched field, 0 on ok. */
|
||||
{ "nested2",
|
||||
"package main;\n"
|
||||
"type inner = struct { x: u8, y: u8 };\n"
|
||||
"type outer2 = struct { a: u8, p: inner };\n"
|
||||
"export fn main() int = {\n"
|
||||
" let o: outer2 = outer2 { a = 5, p = inner { x = 6, y = 7 } };\n"
|
||||
" if (o.a: int != 5) { return 1; };\n"
|
||||
" if (o.p.x: int != 6) { return 2; };\n"
|
||||
" if (o.p.y: int != 7) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* (2) nested3 — outer{a:u8, p:inner, z:i64}: z proves post-composite
|
||||
* accumulation stays natural. Pre-fix z sat at the slot-padded offset
|
||||
* past the 8B-padded inner; the natural read undershoots. The wide z
|
||||
* value (0x44444444) is verified inside the program (the exit channel
|
||||
* is 8-bit), so a truncated/mis-addressed z fails the in-program cmp. */
|
||||
{ "nested3",
|
||||
"package main;\n"
|
||||
"type inner = struct { x: u8, y: u8 };\n"
|
||||
"type outer = struct { a: u8, p: inner, z: i64 };\n"
|
||||
"export fn main() int = {\n"
|
||||
" let o: outer = outer { a = 1, p = inner { x = 2, y = 3 }, z = 0x44444444i64 };\n"
|
||||
" if (o.a: int != 1) { return 1; };\n"
|
||||
" if (o.p.x: int != 2) { return 2; };\n"
|
||||
" if (o.p.y: int != 3) { return 3; };\n"
|
||||
" if (o.z != 0x44444444i64) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* (3) control — flat struct {a:u8, b:i64}, no sub-8 composite field.
|
||||
* Natural and slot-padded layouts coincide (b lands at 8 either way);
|
||||
* proves the fix leaves the common case unmoved. */
|
||||
{ "flat_ctl",
|
||||
"package main;\n"
|
||||
"type flat = struct { a: u8, b: i64 };\n"
|
||||
"export fn main() int = {\n"
|
||||
" let o: flat = flat { a = 9, b = 0x33333333i64 };\n"
|
||||
" if (o.a: int != 9) { return 1; };\n"
|
||||
" if (o.b != 0x33333333i64) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
};
|
||||
|
||||
/* run_build — build+run `src` via `driver`; returns the binary's exit
|
||||
* code, or -1 on a build failure. */
|
||||
static int
|
||||
run_build(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/nestfld_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/nestfld_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/nestfld_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -2; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
|
||||
driver, outbin, src);
|
||||
int brc = runwait(cmd);
|
||||
|
||||
int got = -1;
|
||||
if (brc == 0) got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
return brc == 0 ? got : -1;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[1024], wdrv[1024];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
int have_ww = (access(wdrv, X_OK) == 0);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
int gc = run_build(cdrv, &rows[i], i);
|
||||
/* cstage must build+run */
|
||||
if (gc < 0) {
|
||||
fprintf(stderr, "nestfield_run[cstage][%s]: build/run "
|
||||
"failed (got %d)\n", rows[i].label, gc);
|
||||
fail++;
|
||||
continue;
|
||||
}
|
||||
if (rows[i].want_exit >= 0 && gc != rows[i].want_exit) {
|
||||
fprintf(stderr, "nestfield_run[cstage][%s]: exit=%d "
|
||||
"want=%d (field mismatch)\n",
|
||||
rows[i].label, gc, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
if (!have_ww) {
|
||||
fprintf(stderr, "nestfield_run: skip wwstage (no %s)\n",
|
||||
wdrv);
|
||||
continue;
|
||||
}
|
||||
int gw = run_build(wdrv, &rows[i], i);
|
||||
/* rule-10: the cat-A invariant is cs == ww */
|
||||
if (gw != gc) {
|
||||
fprintf(stderr, "nestfield_run[%s]: cs=%d != ww=%d "
|
||||
"(struct field-offset divergence — #44/#55)\n",
|
||||
rows[i].label, gc, gw);
|
||||
fail++;
|
||||
}
|
||||
if (rows[i].want_exit >= 0 && gw != rows[i].want_exit) {
|
||||
fprintf(stderr, "nestfield_run[wwstage][%s]: exit=%d "
|
||||
"want=%d (field mismatch)\n",
|
||||
rows[i].label, gw, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "nestfield_run: %d/%d checks failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("nestfield_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -25,9 +25,9 @@
|
||||
* 683 asm_byte_identical pattern). Byte-identity subsumes the frame:
|
||||
* pre-fix nested3 differs at `TEXT main,$32` vs `$16`, `SUBQ $32` vs
|
||||
* `$16`, and every `-24(BP)` vs `-16(BP)`; post-fix the two .s are
|
||||
* byte-for-byte equal. The sibling 989_nestfield_run is the RUNTIME
|
||||
* exit-code proof (deliberately NOT frame-gated — see its header); this
|
||||
* file is the .s/frame-absolute one #75 owes.
|
||||
* byte-for-byte equal. The sibling RUNTIME exit-code proof migrated to
|
||||
* test/lang/nestfield_test.ww (@test field-read rows, cs==ww via the T2
|
||||
* byte-id gate); this file is the .s/frame-absolute one #75 owes.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
7
test/wcc/data/arr_nested_ellipsis_reject/case.ww
Normal file
7
test/wcc/data/arr_nested_ellipsis_reject/case.ww
Normal file
@@ -0,0 +1,7 @@
|
||||
//ww:error "repeat with nested-array elements"
|
||||
// #156/rule-7 carrier: a `...` repeat marker with a nested-array element must
|
||||
// REJECT on both stages (no consumer needs it; powers_of_ten is fully
|
||||
// enumerated). From test/wcc/919_array_static_init_run.c nested_ellipsis_reject.
|
||||
package main;
|
||||
let A: [4][2]u64 = [[1u64, 2u64]...];
|
||||
export fn main() i32 = { return 0; };
|
||||
8
test/wcc/data/const_divzero_reject/case.ww
Normal file
8
test/wcc/data/const_divzero_reject/case.ww
Normal file
@@ -0,0 +1,8 @@
|
||||
//ww:error "division by zero"
|
||||
// #133/rule-7 carrier: a div-by-zero const-expr module-global init stays a
|
||||
// COMPILE error in BOTH stages, NOT silently zeroed (the eval_def_const error
|
||||
// path survives the let-hook). From test/wcc/947_inferred_scalar_global_run.c
|
||||
// const_divzero.
|
||||
package main;
|
||||
let s = 7 / 0;
|
||||
export fn main() i32 = { return s: i32; };
|
||||
8
test/wcc/data/slice_of_str_reject/case.ww
Normal file
8
test/wcc/data/slice_of_str_reject/case.ww
Normal file
@@ -0,0 +1,8 @@
|
||||
//ww:error "slice-of-{str,slice,tagged}"
|
||||
// #117/rule-7 carrier: a non-tuple aggregate element (a bare []str) in a
|
||||
// const-slice static-init stays a LOUD reject in BOTH stages — #117 is narrow
|
||||
// to the (str,*fn) tuple form. From test/wcc/946_const_slice_aggregate_run.c
|
||||
// loud_slice_of_str.
|
||||
package main;
|
||||
const xs: []str = ["a", "b"];
|
||||
export fn main() i32 = { return len(xs): i32; };
|
||||
Reference in New Issue
Block a user