selfhost/cmd/wcc/check+test: fold size(T)/align(T)/offset(e.f) at check time
Mirror cstage cmd/wcc/check.c:907-960. Three typed-builtin intercepts that cstage already had: - size(T) — folds to a literal integer at check time from a newly-introduced astsize walker over the type AST. Mirrors the size computation in cstage resolve_type at check.c:286-528. - align(T) — same, via astalign. - offset(e.f) — folds the byte offset of field f in e's struct type via astoffset. Peels exactly one N_TPTR for `p.field`. seedprimitives registers the three names as SK_FN nil; exprtype's N_CALL arm gates on a same-module shadow check (per #23 alloc precedent) and consumes the parser-planted type-expression arg. The fold is in-place — foldtointlit mutates N_CALL into N_INTLIT so cgen sees a plain integer. resolvewalk's N_CALL trigger invokes exprtype so the fold fires from non-let contexts too (e.g. inside `if (size(T) != …)`). selfhost/test/smoke.ww gains a probe-8 block: size/align/offset assertions across str, primitive widths, ptrs, slices, and two structs (`point`, `mixalign`) covering both no-padding and i8+i64 natural-align padding cases. Known divergences NOT in #42 scope: - size((*T|void)) ≠ 8 on the cstage nullable-ptr fold (#13 family, unreachable through current grammar). - 8B-struct bare-let zero-init wwstage skip vs cstage emit (#59). - Same-module shadow gate added here, cstage has none — sibling shape to #26 (free/append/len gates). Closes the original chain that started with the user's call to fix the structural debt — six precondition fixes (#51, #52, #53, #55, #56, #50) landed before this fold could safely live in the check pass. Unblocks #43 (sweep literal 16s → size(str)) and #1 (str → 24B becomes one line).
This commit is contained in:
@@ -7082,6 +7082,15 @@ fn seedprimitives(c: *checker) void = {
|
||||
scopedefine(c.top, "alloc", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "free", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "append", skind.SK_FN, nil, nil);
|
||||
// #42: typed builtins folded to integer literals at check time —
|
||||
// `size(T)` / `align(T)` (arg is a type-expression planted by the
|
||||
// parser at lib/ww/parse/expr.ww:254-267) and `offset(e.f)` (arg is
|
||||
// an N_DOT). exprtype intercepts these and rewrites the N_CALL to
|
||||
// N_INTLIT so cgen never sees an unresolved size/align/offset symbol.
|
||||
// Mirrors cmd/wcc/check.c:907-955.
|
||||
scopedefine(c.top, "size", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "align", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "offset", skind.SK_FN, nil, nil);
|
||||
};
|
||||
|
||||
// declmod — module-tag stamp for a top-level decl.
|
||||
@@ -7399,6 +7408,24 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// #42: trigger the size/align/offset fold here so the mutation
|
||||
// fires regardless of context (if-conditions, expression statements,
|
||||
// etc.) — wwstage's exprtype is otherwise called only from
|
||||
// checkletassign / checkretassign / TRYPROP, and an unwrapped
|
||||
// `if (size(str) != 16)` would otherwise leave the N_CALL alone
|
||||
// and cgen would emit a stray `CALL size(SB)`. Mirrors cstage
|
||||
// cstmt's recursive cexpr discipline.
|
||||
if (k == nkind.N_CALL) {
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == nkind.N_IDENT) {
|
||||
let nm: str = n.lhs.str;
|
||||
if (streq(nm, "size") || streq(nm, "align") || streq(nm, "offset")) {
|
||||
let _t: *node = exprtype(c, n);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// After walking children: a local `let X: T = init;` registers
|
||||
// `X` so subsequent statements can resolve it. Top-level lets
|
||||
// are installed in installdecl, so this duplicate install at
|
||||
@@ -7585,6 +7612,201 @@ fn mktname(c: *checker, nm: str) *node = {
|
||||
return n;
|
||||
};
|
||||
|
||||
// #42: AST-level layout helpers for the size(T)/align(T)/offset(e.f)
|
||||
// fold. Mirror cstage resolve_type's size/align computation
|
||||
// (cmd/wcc/check.c:286-528) on AST nodes — wwstage check.ww never
|
||||
// materialises tinfo for user types so the fold has to walk the AST
|
||||
// directly. Struct layout follows cstage check.c:471-526 (align each
|
||||
// field, max align for the whole record, round size up to alignment).
|
||||
fn astalign(c: *checker, t: *node) i64 = {
|
||||
if (t == nil) { return 1i64; };
|
||||
let k: nkind = t.kind;
|
||||
if (k == nkind.N_TBANG) { return astalign(c, t.lhs); };
|
||||
if (k == nkind.N_TPTR) { return 8i64; };
|
||||
if (k == nkind.N_TSLICE) { return 8i64; };
|
||||
if (k == nkind.N_TCHAN) { return 8i64; };
|
||||
if (k == nkind.N_TFN) { return 8i64; };
|
||||
if (k == nkind.N_TARRAY) { return astalign(c, t.lhs); };
|
||||
if (k == nkind.N_TTAGGED) { return 8i64; };
|
||||
if (k == nkind.N_TTUPLE) {
|
||||
let m: i64 = 1i64;
|
||||
let p: *node = t.list;
|
||||
for (p != nil) {
|
||||
let pa: i64 = astalign(c, p);
|
||||
if (pa > m) { m = pa; };
|
||||
p = p.next;
|
||||
};
|
||||
return m;
|
||||
};
|
||||
if (k == nkind.N_TSTRUCT) {
|
||||
let m: i64 = 1i64;
|
||||
let f: *node = t.list;
|
||||
for (f != nil) {
|
||||
if (f.kind == nkind.N_TFIELD) {
|
||||
let fa: i64 = astalign(c, f.lhs);
|
||||
if (fa > m) { m = fa; };
|
||||
};
|
||||
f = f.next;
|
||||
};
|
||||
return m;
|
||||
};
|
||||
if (k == nkind.N_TENUM) {
|
||||
if (t.lhs != nil) { return astalign(c, t.lhs); };
|
||||
return 4i64;
|
||||
};
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "void") || streq(nm, "bool") || streq(nm, "i8") || streq(nm, "u8")) { return 1i64; };
|
||||
if (streq(nm, "i16") || streq(nm, "u16")) { return 2i64; };
|
||||
if (streq(nm, "i32") || streq(nm, "u32") || streq(nm, "f32") || streq(nm, "rune")) { return 4i64; };
|
||||
if (streq(nm, "i64") || streq(nm, "u64") || streq(nm, "f64") || streq(nm, "int") || streq(nm, "uint") || streq(nm, "uintptr") || streq(nm, "str")) { return 8i64; };
|
||||
let resolved: *node = resolvealias(c, t);
|
||||
if (resolved != nil && resolved != t) {
|
||||
return astalign(c, resolved);
|
||||
};
|
||||
};
|
||||
return 1i64;
|
||||
};
|
||||
|
||||
fn astsize(c: *checker, t: *node) i64 = {
|
||||
if (t == nil) { return 0i64; };
|
||||
let k: nkind = t.kind;
|
||||
if (k == nkind.N_TBANG) { return astsize(c, t.lhs); };
|
||||
if (k == nkind.N_TPTR) { return 8i64; };
|
||||
if (k == nkind.N_TSLICE) { return 24i64; };
|
||||
if (k == nkind.N_TCHAN) { return 8i64; };
|
||||
if (k == nkind.N_TFN) { return 8i64; };
|
||||
if (k == nkind.N_TARRAY) {
|
||||
let elen: i64 = 0i64;
|
||||
if (t.rhs != nil) {
|
||||
if (t.rhs.kind == nkind.N_INTLIT) { elen = t.rhs.uval: i64; };
|
||||
};
|
||||
return astsize(c, t.lhs) * elen;
|
||||
};
|
||||
if (k == nkind.N_TTUPLE) {
|
||||
let total: i64 = 0i64;
|
||||
let p: *node = t.list;
|
||||
for (p != nil) {
|
||||
total += astsize(c, p);
|
||||
p = p.next;
|
||||
};
|
||||
return total;
|
||||
};
|
||||
if (k == nkind.N_TSTRUCT) {
|
||||
let off: i64 = 0i64;
|
||||
let maxal: i64 = 1i64;
|
||||
let f: *node = t.list;
|
||||
for (f != nil) {
|
||||
if (f.kind == nkind.N_TFIELD) {
|
||||
let fa: i64 = astalign(c, f.lhs);
|
||||
if (fa > maxal) { maxal = fa; };
|
||||
off = (off + fa - 1i64) & ~(fa - 1i64);
|
||||
off += astsize(c, f.lhs);
|
||||
};
|
||||
f = f.next;
|
||||
};
|
||||
return (off + maxal - 1i64) & ~(maxal - 1i64);
|
||||
};
|
||||
if (k == nkind.N_TTAGGED) {
|
||||
// 8 (tag) + max variant payload, rounded up to 8.
|
||||
let maxsz: i64 = 0i64;
|
||||
let v: *node = t.list;
|
||||
for (v != nil) {
|
||||
let sz: i64 = astsize(c, v);
|
||||
if (sz > maxsz) { maxsz = sz; };
|
||||
v = v.next;
|
||||
};
|
||||
let pad: i64 = (maxsz + 7i64) & ~7i64;
|
||||
return 8i64 + pad;
|
||||
};
|
||||
if (k == nkind.N_TENUM) {
|
||||
if (t.lhs != nil) { return astsize(c, t.lhs); };
|
||||
return 4i64;
|
||||
};
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "void")) { return 0i64; };
|
||||
if (streq(nm, "bool")) { return 1i64; };
|
||||
if (streq(nm, "i8") || streq(nm, "u8")) { return 1i64; };
|
||||
if (streq(nm, "i16") || streq(nm, "u16")) { return 2i64; };
|
||||
if (streq(nm, "i32") || streq(nm, "u32") || streq(nm, "f32") || streq(nm, "rune")) { return 4i64; };
|
||||
if (streq(nm, "i64") || streq(nm, "u64") || streq(nm, "f64") || streq(nm, "int") || streq(nm, "uint") || streq(nm, "uintptr")) { return 8i64; };
|
||||
if (streq(nm, "str")) { return 16i64; };
|
||||
let resolved: *node = resolvealias(c, t);
|
||||
if (resolved != nil && resolved != t) {
|
||||
return astsize(c, resolved);
|
||||
};
|
||||
};
|
||||
return 0i64;
|
||||
};
|
||||
|
||||
// astoffset — byte offset of `dot.str` inside the struct type of
|
||||
// `dot.lhs`. Mirrors cstage cmd/wcc/check.c:932-961: peel one N_TPTR
|
||||
// (for `p.field` where p is *Struct), require N_TSTRUCT, walk fields
|
||||
// honouring per-field alignment, return -1 if the field name is
|
||||
// absent so the caller can flag the error and fold to 0.
|
||||
fn astoffset(c: *checker, dot: *node) i64 = {
|
||||
if (dot == nil) { return -1i64; };
|
||||
if (dot.kind != nkind.N_DOT) { return -1i64; };
|
||||
let recv: *node = scruttype(c, dot.lhs);
|
||||
if (recv == nil) { return -1i64; };
|
||||
let rt: *node = resolvealias(c, unwrapbang(recv));
|
||||
if (rt == nil) { return -1i64; };
|
||||
if (rt.kind == nkind.N_TPTR) {
|
||||
rt = resolvealias(c, unwrapbang(rt.lhs));
|
||||
};
|
||||
if (rt == nil) { return -1i64; };
|
||||
if (rt.kind != nkind.N_TSTRUCT) { return -1i64; };
|
||||
let off: i64 = 0i64;
|
||||
let f: *node = rt.list;
|
||||
for (f != nil) {
|
||||
if (f.kind == nkind.N_TFIELD) {
|
||||
let fa: i64 = astalign(c, f.lhs);
|
||||
off = (off + fa - 1i64) & ~(fa - 1i64);
|
||||
if (streq(f.str, dot.str)) { return off; };
|
||||
off += astsize(c, f.lhs);
|
||||
};
|
||||
f = f.next;
|
||||
};
|
||||
return -1i64;
|
||||
};
|
||||
|
||||
// arenau64tos — decimal string for the folded INTLIT's `str` field.
|
||||
// Cstage uses aprintf("%llu") at the same site (cmd/wcc/check.c:921);
|
||||
// wwstage cgen only reads `uval` for N_INTLIT codegen so `str` is
|
||||
// just for the AST printer, but set it for parity with the parser's
|
||||
// own literal-emit shape.
|
||||
fn arenau64tos(a: *arena, v: u64) str = {
|
||||
let buf: *u8 = amalloc(a, 24u64): *u8;
|
||||
let i: i32 = 23;
|
||||
buf[i] = 0u8;
|
||||
if (v == 0u64) { i -= 1; buf[i] = 48u8; };
|
||||
let n: u64 = v;
|
||||
for (n > 0u64) {
|
||||
i -= 1;
|
||||
buf[i] = (48u64 + (n % 10u64)): u8;
|
||||
n /= 10u64;
|
||||
};
|
||||
let r: str;
|
||||
r.ptr = buf + (i: u64);
|
||||
r.len = 23 - i;
|
||||
return r;
|
||||
};
|
||||
|
||||
// foldtointlit — mutate `n` in place to an N_INTLIT with value `v`.
|
||||
// Used by the #42 size/align/offset intercepts so cgen sees the
|
||||
// folded literal rather than an unresolved call. Mirrors cstage
|
||||
// cmd/wcc/check.c:919-927 / :951-958.
|
||||
fn foldtointlit(c: *checker, n: *node, v: i64) void = {
|
||||
n.kind = nkind.N_INTLIT;
|
||||
n.uval = v: u64;
|
||||
n.str = arenau64tos(c.a, v: u64);
|
||||
n.lhs = nil;
|
||||
n.list = nil;
|
||||
let empty: str;
|
||||
n.tsuffix = empty;
|
||||
};
|
||||
|
||||
// exprtype — best-effort type-AST inference for an expression
|
||||
// node. Handles literals, identifiers, calls, and casts; returns
|
||||
// nil for shapes we don't statically know (binary ops, struct
|
||||
@@ -7661,6 +7883,57 @@ fn exprtype(c: *checker, e: *node) *node = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// #42: size(T) / align(T) / offset(e.f) typed-builtin intercepts.
|
||||
// Fold the N_CALL in place to an N_INTLIT so cgen never sees an
|
||||
// unresolved size/align/offset symbol. Same-module shadow gate
|
||||
// mirrors the alloc precedent (#23) so a user `fn size(...)`
|
||||
// inside this module suppresses the builtin. Mirrors cstage
|
||||
// cmd/wcc/check.c:907-960.
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
let bname: str = callee.str;
|
||||
let issize: bool = streq(bname, "size");
|
||||
let isalign: bool = streq(bname, "align");
|
||||
let isoffset: bool = streq(bname, "offset");
|
||||
if (issize || isalign || isoffset) {
|
||||
let shadowed: bool = false;
|
||||
if (c.curmod.len > 0) {
|
||||
if (scopelookupinmodule(c.cur, c.curmod, bname) != nil) {
|
||||
shadowed = true;
|
||||
};
|
||||
};
|
||||
if (!shadowed) {
|
||||
if (e.list != nil) {
|
||||
if (issize) {
|
||||
let v: i64 = astsize(c, e.list);
|
||||
foldtointlit(c, e, v);
|
||||
return mktname(c, "i32");
|
||||
};
|
||||
if (isalign) {
|
||||
let v: i64 = astalign(c, e.list);
|
||||
foldtointlit(c, e, v);
|
||||
return mktname(c, "i32");
|
||||
};
|
||||
// offset(e.f): the arg is a value expression
|
||||
// (N_DOT), parsed via parsearglist — not a
|
||||
// type expression.
|
||||
if (isoffset) {
|
||||
if (e.list.next == nil && e.list.kind == nkind.N_DOT) {
|
||||
let off: i64 = astoffset(c, e.list);
|
||||
if (off < 0i64) {
|
||||
os.write(2, "offset: no field '".ptr, 18u64);
|
||||
os.write(2, e.list.str.ptr, e.list.str.len: u64);
|
||||
os.write(2, "'\n".ptr, 2u64);
|
||||
c.errs += 1;
|
||||
off = 0i64;
|
||||
};
|
||||
foldtointlit(c, e, off);
|
||||
return mktname(c, "i32");
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
let nm: str;
|
||||
nm.ptr = nil; nm.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) { nm = callee.str; };
|
||||
|
||||
@@ -88,6 +88,15 @@ fn seedprimitives(c: *checker) void = {
|
||||
scopedefine(c.top, "alloc", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "free", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "append", skind.SK_FN, nil, nil);
|
||||
// #42: typed builtins folded to integer literals at check time —
|
||||
// `size(T)` / `align(T)` (arg is a type-expression planted by the
|
||||
// parser at lib/ww/parse/expr.ww:254-267) and `offset(e.f)` (arg is
|
||||
// an N_DOT). exprtype intercepts these and rewrites the N_CALL to
|
||||
// N_INTLIT so cgen never sees an unresolved size/align/offset symbol.
|
||||
// Mirrors cmd/wcc/check.c:907-955.
|
||||
scopedefine(c.top, "size", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "align", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "offset", skind.SK_FN, nil, nil);
|
||||
};
|
||||
|
||||
// declmod — module-tag stamp for a top-level decl.
|
||||
@@ -405,6 +414,24 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// #42: trigger the size/align/offset fold here so the mutation
|
||||
// fires regardless of context (if-conditions, expression statements,
|
||||
// etc.) — wwstage's exprtype is otherwise called only from
|
||||
// checkletassign / checkretassign / TRYPROP, and an unwrapped
|
||||
// `if (size(str) != 16)` would otherwise leave the N_CALL alone
|
||||
// and cgen would emit a stray `CALL size(SB)`. Mirrors cstage
|
||||
// cstmt's recursive cexpr discipline.
|
||||
if (k == nkind.N_CALL) {
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == nkind.N_IDENT) {
|
||||
let nm: str = n.lhs.str;
|
||||
if (streq(nm, "size") || streq(nm, "align") || streq(nm, "offset")) {
|
||||
let _t: *node = exprtype(c, n);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// After walking children: a local `let X: T = init;` registers
|
||||
// `X` so subsequent statements can resolve it. Top-level lets
|
||||
// are installed in installdecl, so this duplicate install at
|
||||
@@ -591,6 +618,201 @@ fn mktname(c: *checker, nm: str) *node = {
|
||||
return n;
|
||||
};
|
||||
|
||||
// #42: AST-level layout helpers for the size(T)/align(T)/offset(e.f)
|
||||
// fold. Mirror cstage resolve_type's size/align computation
|
||||
// (cmd/wcc/check.c:286-528) on AST nodes — wwstage check.ww never
|
||||
// materialises tinfo for user types so the fold has to walk the AST
|
||||
// directly. Struct layout follows cstage check.c:471-526 (align each
|
||||
// field, max align for the whole record, round size up to alignment).
|
||||
fn astalign(c: *checker, t: *node) i64 = {
|
||||
if (t == nil) { return 1i64; };
|
||||
let k: nkind = t.kind;
|
||||
if (k == nkind.N_TBANG) { return astalign(c, t.lhs); };
|
||||
if (k == nkind.N_TPTR) { return 8i64; };
|
||||
if (k == nkind.N_TSLICE) { return 8i64; };
|
||||
if (k == nkind.N_TCHAN) { return 8i64; };
|
||||
if (k == nkind.N_TFN) { return 8i64; };
|
||||
if (k == nkind.N_TARRAY) { return astalign(c, t.lhs); };
|
||||
if (k == nkind.N_TTAGGED) { return 8i64; };
|
||||
if (k == nkind.N_TTUPLE) {
|
||||
let m: i64 = 1i64;
|
||||
let p: *node = t.list;
|
||||
for (p != nil) {
|
||||
let pa: i64 = astalign(c, p);
|
||||
if (pa > m) { m = pa; };
|
||||
p = p.next;
|
||||
};
|
||||
return m;
|
||||
};
|
||||
if (k == nkind.N_TSTRUCT) {
|
||||
let m: i64 = 1i64;
|
||||
let f: *node = t.list;
|
||||
for (f != nil) {
|
||||
if (f.kind == nkind.N_TFIELD) {
|
||||
let fa: i64 = astalign(c, f.lhs);
|
||||
if (fa > m) { m = fa; };
|
||||
};
|
||||
f = f.next;
|
||||
};
|
||||
return m;
|
||||
};
|
||||
if (k == nkind.N_TENUM) {
|
||||
if (t.lhs != nil) { return astalign(c, t.lhs); };
|
||||
return 4i64;
|
||||
};
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "void") || streq(nm, "bool") || streq(nm, "i8") || streq(nm, "u8")) { return 1i64; };
|
||||
if (streq(nm, "i16") || streq(nm, "u16")) { return 2i64; };
|
||||
if (streq(nm, "i32") || streq(nm, "u32") || streq(nm, "f32") || streq(nm, "rune")) { return 4i64; };
|
||||
if (streq(nm, "i64") || streq(nm, "u64") || streq(nm, "f64") || streq(nm, "int") || streq(nm, "uint") || streq(nm, "uintptr") || streq(nm, "str")) { return 8i64; };
|
||||
let resolved: *node = resolvealias(c, t);
|
||||
if (resolved != nil && resolved != t) {
|
||||
return astalign(c, resolved);
|
||||
};
|
||||
};
|
||||
return 1i64;
|
||||
};
|
||||
|
||||
fn astsize(c: *checker, t: *node) i64 = {
|
||||
if (t == nil) { return 0i64; };
|
||||
let k: nkind = t.kind;
|
||||
if (k == nkind.N_TBANG) { return astsize(c, t.lhs); };
|
||||
if (k == nkind.N_TPTR) { return 8i64; };
|
||||
if (k == nkind.N_TSLICE) { return 24i64; };
|
||||
if (k == nkind.N_TCHAN) { return 8i64; };
|
||||
if (k == nkind.N_TFN) { return 8i64; };
|
||||
if (k == nkind.N_TARRAY) {
|
||||
let elen: i64 = 0i64;
|
||||
if (t.rhs != nil) {
|
||||
if (t.rhs.kind == nkind.N_INTLIT) { elen = t.rhs.uval: i64; };
|
||||
};
|
||||
return astsize(c, t.lhs) * elen;
|
||||
};
|
||||
if (k == nkind.N_TTUPLE) {
|
||||
let total: i64 = 0i64;
|
||||
let p: *node = t.list;
|
||||
for (p != nil) {
|
||||
total += astsize(c, p);
|
||||
p = p.next;
|
||||
};
|
||||
return total;
|
||||
};
|
||||
if (k == nkind.N_TSTRUCT) {
|
||||
let off: i64 = 0i64;
|
||||
let maxal: i64 = 1i64;
|
||||
let f: *node = t.list;
|
||||
for (f != nil) {
|
||||
if (f.kind == nkind.N_TFIELD) {
|
||||
let fa: i64 = astalign(c, f.lhs);
|
||||
if (fa > maxal) { maxal = fa; };
|
||||
off = (off + fa - 1i64) & ~(fa - 1i64);
|
||||
off += astsize(c, f.lhs);
|
||||
};
|
||||
f = f.next;
|
||||
};
|
||||
return (off + maxal - 1i64) & ~(maxal - 1i64);
|
||||
};
|
||||
if (k == nkind.N_TTAGGED) {
|
||||
// 8 (tag) + max variant payload, rounded up to 8.
|
||||
let maxsz: i64 = 0i64;
|
||||
let v: *node = t.list;
|
||||
for (v != nil) {
|
||||
let sz: i64 = astsize(c, v);
|
||||
if (sz > maxsz) { maxsz = sz; };
|
||||
v = v.next;
|
||||
};
|
||||
let pad: i64 = (maxsz + 7i64) & ~7i64;
|
||||
return 8i64 + pad;
|
||||
};
|
||||
if (k == nkind.N_TENUM) {
|
||||
if (t.lhs != nil) { return astsize(c, t.lhs); };
|
||||
return 4i64;
|
||||
};
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "void")) { return 0i64; };
|
||||
if (streq(nm, "bool")) { return 1i64; };
|
||||
if (streq(nm, "i8") || streq(nm, "u8")) { return 1i64; };
|
||||
if (streq(nm, "i16") || streq(nm, "u16")) { return 2i64; };
|
||||
if (streq(nm, "i32") || streq(nm, "u32") || streq(nm, "f32") || streq(nm, "rune")) { return 4i64; };
|
||||
if (streq(nm, "i64") || streq(nm, "u64") || streq(nm, "f64") || streq(nm, "int") || streq(nm, "uint") || streq(nm, "uintptr")) { return 8i64; };
|
||||
if (streq(nm, "str")) { return 16i64; };
|
||||
let resolved: *node = resolvealias(c, t);
|
||||
if (resolved != nil && resolved != t) {
|
||||
return astsize(c, resolved);
|
||||
};
|
||||
};
|
||||
return 0i64;
|
||||
};
|
||||
|
||||
// astoffset — byte offset of `dot.str` inside the struct type of
|
||||
// `dot.lhs`. Mirrors cstage cmd/wcc/check.c:932-961: peel one N_TPTR
|
||||
// (for `p.field` where p is *Struct), require N_TSTRUCT, walk fields
|
||||
// honouring per-field alignment, return -1 if the field name is
|
||||
// absent so the caller can flag the error and fold to 0.
|
||||
fn astoffset(c: *checker, dot: *node) i64 = {
|
||||
if (dot == nil) { return -1i64; };
|
||||
if (dot.kind != nkind.N_DOT) { return -1i64; };
|
||||
let recv: *node = scruttype(c, dot.lhs);
|
||||
if (recv == nil) { return -1i64; };
|
||||
let rt: *node = resolvealias(c, unwrapbang(recv));
|
||||
if (rt == nil) { return -1i64; };
|
||||
if (rt.kind == nkind.N_TPTR) {
|
||||
rt = resolvealias(c, unwrapbang(rt.lhs));
|
||||
};
|
||||
if (rt == nil) { return -1i64; };
|
||||
if (rt.kind != nkind.N_TSTRUCT) { return -1i64; };
|
||||
let off: i64 = 0i64;
|
||||
let f: *node = rt.list;
|
||||
for (f != nil) {
|
||||
if (f.kind == nkind.N_TFIELD) {
|
||||
let fa: i64 = astalign(c, f.lhs);
|
||||
off = (off + fa - 1i64) & ~(fa - 1i64);
|
||||
if (streq(f.str, dot.str)) { return off; };
|
||||
off += astsize(c, f.lhs);
|
||||
};
|
||||
f = f.next;
|
||||
};
|
||||
return -1i64;
|
||||
};
|
||||
|
||||
// arenau64tos — decimal string for the folded INTLIT's `str` field.
|
||||
// Cstage uses aprintf("%llu") at the same site (cmd/wcc/check.c:921);
|
||||
// wwstage cgen only reads `uval` for N_INTLIT codegen so `str` is
|
||||
// just for the AST printer, but set it for parity with the parser's
|
||||
// own literal-emit shape.
|
||||
fn arenau64tos(a: *arena, v: u64) str = {
|
||||
let buf: *u8 = amalloc(a, 24u64): *u8;
|
||||
let i: i32 = 23;
|
||||
buf[i] = 0u8;
|
||||
if (v == 0u64) { i -= 1; buf[i] = 48u8; };
|
||||
let n: u64 = v;
|
||||
for (n > 0u64) {
|
||||
i -= 1;
|
||||
buf[i] = (48u64 + (n % 10u64)): u8;
|
||||
n /= 10u64;
|
||||
};
|
||||
let r: str;
|
||||
r.ptr = buf + (i: u64);
|
||||
r.len = 23 - i;
|
||||
return r;
|
||||
};
|
||||
|
||||
// foldtointlit — mutate `n` in place to an N_INTLIT with value `v`.
|
||||
// Used by the #42 size/align/offset intercepts so cgen sees the
|
||||
// folded literal rather than an unresolved call. Mirrors cstage
|
||||
// cmd/wcc/check.c:919-927 / :951-958.
|
||||
fn foldtointlit(c: *checker, n: *node, v: i64) void = {
|
||||
n.kind = nkind.N_INTLIT;
|
||||
n.uval = v: u64;
|
||||
n.str = arenau64tos(c.a, v: u64);
|
||||
n.lhs = nil;
|
||||
n.list = nil;
|
||||
let empty: str;
|
||||
n.tsuffix = empty;
|
||||
};
|
||||
|
||||
// exprtype — best-effort type-AST inference for an expression
|
||||
// node. Handles literals, identifiers, calls, and casts; returns
|
||||
// nil for shapes we don't statically know (binary ops, struct
|
||||
@@ -667,6 +889,57 @@ fn exprtype(c: *checker, e: *node) *node = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// #42: size(T) / align(T) / offset(e.f) typed-builtin intercepts.
|
||||
// Fold the N_CALL in place to an N_INTLIT so cgen never sees an
|
||||
// unresolved size/align/offset symbol. Same-module shadow gate
|
||||
// mirrors the alloc precedent (#23) so a user `fn size(...)`
|
||||
// inside this module suppresses the builtin. Mirrors cstage
|
||||
// cmd/wcc/check.c:907-960.
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
let bname: str = callee.str;
|
||||
let issize: bool = streq(bname, "size");
|
||||
let isalign: bool = streq(bname, "align");
|
||||
let isoffset: bool = streq(bname, "offset");
|
||||
if (issize || isalign || isoffset) {
|
||||
let shadowed: bool = false;
|
||||
if (c.curmod.len > 0) {
|
||||
if (scopelookupinmodule(c.cur, c.curmod, bname) != nil) {
|
||||
shadowed = true;
|
||||
};
|
||||
};
|
||||
if (!shadowed) {
|
||||
if (e.list != nil) {
|
||||
if (issize) {
|
||||
let v: i64 = astsize(c, e.list);
|
||||
foldtointlit(c, e, v);
|
||||
return mktname(c, "i32");
|
||||
};
|
||||
if (isalign) {
|
||||
let v: i64 = astalign(c, e.list);
|
||||
foldtointlit(c, e, v);
|
||||
return mktname(c, "i32");
|
||||
};
|
||||
// offset(e.f): the arg is a value expression
|
||||
// (N_DOT), parsed via parsearglist — not a
|
||||
// type expression.
|
||||
if (isoffset) {
|
||||
if (e.list.next == nil && e.list.kind == nkind.N_DOT) {
|
||||
let off: i64 = astoffset(c, e.list);
|
||||
if (off < 0i64) {
|
||||
os.write(2, "offset: no field '".ptr, 18u64);
|
||||
os.write(2, e.list.str.ptr, e.list.str.len: u64);
|
||||
os.write(2, "'\n".ptr, 2u64);
|
||||
c.errs += 1;
|
||||
off = 0i64;
|
||||
};
|
||||
foldtointlit(c, e, off);
|
||||
return mktname(c, "i32");
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
let nm: str;
|
||||
nm.ptr = nil; nm.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) { nm = callee.str; };
|
||||
|
||||
@@ -7082,6 +7082,15 @@ fn seedprimitives(c: *checker) void = {
|
||||
scopedefine(c.top, "alloc", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "free", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "append", skind.SK_FN, nil, nil);
|
||||
// #42: typed builtins folded to integer literals at check time —
|
||||
// `size(T)` / `align(T)` (arg is a type-expression planted by the
|
||||
// parser at lib/ww/parse/expr.ww:254-267) and `offset(e.f)` (arg is
|
||||
// an N_DOT). exprtype intercepts these and rewrites the N_CALL to
|
||||
// N_INTLIT so cgen never sees an unresolved size/align/offset symbol.
|
||||
// Mirrors cmd/wcc/check.c:907-955.
|
||||
scopedefine(c.top, "size", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "align", skind.SK_FN, nil, nil);
|
||||
scopedefine(c.top, "offset", skind.SK_FN, nil, nil);
|
||||
};
|
||||
|
||||
// declmod — module-tag stamp for a top-level decl.
|
||||
@@ -7399,6 +7408,24 @@ fn resolvewalk(c: *checker, n: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// #42: trigger the size/align/offset fold here so the mutation
|
||||
// fires regardless of context (if-conditions, expression statements,
|
||||
// etc.) — wwstage's exprtype is otherwise called only from
|
||||
// checkletassign / checkretassign / TRYPROP, and an unwrapped
|
||||
// `if (size(str) != 16)` would otherwise leave the N_CALL alone
|
||||
// and cgen would emit a stray `CALL size(SB)`. Mirrors cstage
|
||||
// cstmt's recursive cexpr discipline.
|
||||
if (k == nkind.N_CALL) {
|
||||
if (n.lhs != nil) {
|
||||
if (n.lhs.kind == nkind.N_IDENT) {
|
||||
let nm: str = n.lhs.str;
|
||||
if (streq(nm, "size") || streq(nm, "align") || streq(nm, "offset")) {
|
||||
let _t: *node = exprtype(c, n);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// After walking children: a local `let X: T = init;` registers
|
||||
// `X` so subsequent statements can resolve it. Top-level lets
|
||||
// are installed in installdecl, so this duplicate install at
|
||||
@@ -7585,6 +7612,201 @@ fn mktname(c: *checker, nm: str) *node = {
|
||||
return n;
|
||||
};
|
||||
|
||||
// #42: AST-level layout helpers for the size(T)/align(T)/offset(e.f)
|
||||
// fold. Mirror cstage resolve_type's size/align computation
|
||||
// (cmd/wcc/check.c:286-528) on AST nodes — wwstage check.ww never
|
||||
// materialises tinfo for user types so the fold has to walk the AST
|
||||
// directly. Struct layout follows cstage check.c:471-526 (align each
|
||||
// field, max align for the whole record, round size up to alignment).
|
||||
fn astalign(c: *checker, t: *node) i64 = {
|
||||
if (t == nil) { return 1i64; };
|
||||
let k: nkind = t.kind;
|
||||
if (k == nkind.N_TBANG) { return astalign(c, t.lhs); };
|
||||
if (k == nkind.N_TPTR) { return 8i64; };
|
||||
if (k == nkind.N_TSLICE) { return 8i64; };
|
||||
if (k == nkind.N_TCHAN) { return 8i64; };
|
||||
if (k == nkind.N_TFN) { return 8i64; };
|
||||
if (k == nkind.N_TARRAY) { return astalign(c, t.lhs); };
|
||||
if (k == nkind.N_TTAGGED) { return 8i64; };
|
||||
if (k == nkind.N_TTUPLE) {
|
||||
let m: i64 = 1i64;
|
||||
let p: *node = t.list;
|
||||
for (p != nil) {
|
||||
let pa: i64 = astalign(c, p);
|
||||
if (pa > m) { m = pa; };
|
||||
p = p.next;
|
||||
};
|
||||
return m;
|
||||
};
|
||||
if (k == nkind.N_TSTRUCT) {
|
||||
let m: i64 = 1i64;
|
||||
let f: *node = t.list;
|
||||
for (f != nil) {
|
||||
if (f.kind == nkind.N_TFIELD) {
|
||||
let fa: i64 = astalign(c, f.lhs);
|
||||
if (fa > m) { m = fa; };
|
||||
};
|
||||
f = f.next;
|
||||
};
|
||||
return m;
|
||||
};
|
||||
if (k == nkind.N_TENUM) {
|
||||
if (t.lhs != nil) { return astalign(c, t.lhs); };
|
||||
return 4i64;
|
||||
};
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "void") || streq(nm, "bool") || streq(nm, "i8") || streq(nm, "u8")) { return 1i64; };
|
||||
if (streq(nm, "i16") || streq(nm, "u16")) { return 2i64; };
|
||||
if (streq(nm, "i32") || streq(nm, "u32") || streq(nm, "f32") || streq(nm, "rune")) { return 4i64; };
|
||||
if (streq(nm, "i64") || streq(nm, "u64") || streq(nm, "f64") || streq(nm, "int") || streq(nm, "uint") || streq(nm, "uintptr") || streq(nm, "str")) { return 8i64; };
|
||||
let resolved: *node = resolvealias(c, t);
|
||||
if (resolved != nil && resolved != t) {
|
||||
return astalign(c, resolved);
|
||||
};
|
||||
};
|
||||
return 1i64;
|
||||
};
|
||||
|
||||
fn astsize(c: *checker, t: *node) i64 = {
|
||||
if (t == nil) { return 0i64; };
|
||||
let k: nkind = t.kind;
|
||||
if (k == nkind.N_TBANG) { return astsize(c, t.lhs); };
|
||||
if (k == nkind.N_TPTR) { return 8i64; };
|
||||
if (k == nkind.N_TSLICE) { return 24i64; };
|
||||
if (k == nkind.N_TCHAN) { return 8i64; };
|
||||
if (k == nkind.N_TFN) { return 8i64; };
|
||||
if (k == nkind.N_TARRAY) {
|
||||
let elen: i64 = 0i64;
|
||||
if (t.rhs != nil) {
|
||||
if (t.rhs.kind == nkind.N_INTLIT) { elen = t.rhs.uval: i64; };
|
||||
};
|
||||
return astsize(c, t.lhs) * elen;
|
||||
};
|
||||
if (k == nkind.N_TTUPLE) {
|
||||
let total: i64 = 0i64;
|
||||
let p: *node = t.list;
|
||||
for (p != nil) {
|
||||
total += astsize(c, p);
|
||||
p = p.next;
|
||||
};
|
||||
return total;
|
||||
};
|
||||
if (k == nkind.N_TSTRUCT) {
|
||||
let off: i64 = 0i64;
|
||||
let maxal: i64 = 1i64;
|
||||
let f: *node = t.list;
|
||||
for (f != nil) {
|
||||
if (f.kind == nkind.N_TFIELD) {
|
||||
let fa: i64 = astalign(c, f.lhs);
|
||||
if (fa > maxal) { maxal = fa; };
|
||||
off = (off + fa - 1i64) & ~(fa - 1i64);
|
||||
off += astsize(c, f.lhs);
|
||||
};
|
||||
f = f.next;
|
||||
};
|
||||
return (off + maxal - 1i64) & ~(maxal - 1i64);
|
||||
};
|
||||
if (k == nkind.N_TTAGGED) {
|
||||
// 8 (tag) + max variant payload, rounded up to 8.
|
||||
let maxsz: i64 = 0i64;
|
||||
let v: *node = t.list;
|
||||
for (v != nil) {
|
||||
let sz: i64 = astsize(c, v);
|
||||
if (sz > maxsz) { maxsz = sz; };
|
||||
v = v.next;
|
||||
};
|
||||
let pad: i64 = (maxsz + 7i64) & ~7i64;
|
||||
return 8i64 + pad;
|
||||
};
|
||||
if (k == nkind.N_TENUM) {
|
||||
if (t.lhs != nil) { return astsize(c, t.lhs); };
|
||||
return 4i64;
|
||||
};
|
||||
if (k == nkind.N_TNAME) {
|
||||
let nm: str = t.str;
|
||||
if (streq(nm, "void")) { return 0i64; };
|
||||
if (streq(nm, "bool")) { return 1i64; };
|
||||
if (streq(nm, "i8") || streq(nm, "u8")) { return 1i64; };
|
||||
if (streq(nm, "i16") || streq(nm, "u16")) { return 2i64; };
|
||||
if (streq(nm, "i32") || streq(nm, "u32") || streq(nm, "f32") || streq(nm, "rune")) { return 4i64; };
|
||||
if (streq(nm, "i64") || streq(nm, "u64") || streq(nm, "f64") || streq(nm, "int") || streq(nm, "uint") || streq(nm, "uintptr")) { return 8i64; };
|
||||
if (streq(nm, "str")) { return 16i64; };
|
||||
let resolved: *node = resolvealias(c, t);
|
||||
if (resolved != nil && resolved != t) {
|
||||
return astsize(c, resolved);
|
||||
};
|
||||
};
|
||||
return 0i64;
|
||||
};
|
||||
|
||||
// astoffset — byte offset of `dot.str` inside the struct type of
|
||||
// `dot.lhs`. Mirrors cstage cmd/wcc/check.c:932-961: peel one N_TPTR
|
||||
// (for `p.field` where p is *Struct), require N_TSTRUCT, walk fields
|
||||
// honouring per-field alignment, return -1 if the field name is
|
||||
// absent so the caller can flag the error and fold to 0.
|
||||
fn astoffset(c: *checker, dot: *node) i64 = {
|
||||
if (dot == nil) { return -1i64; };
|
||||
if (dot.kind != nkind.N_DOT) { return -1i64; };
|
||||
let recv: *node = scruttype(c, dot.lhs);
|
||||
if (recv == nil) { return -1i64; };
|
||||
let rt: *node = resolvealias(c, unwrapbang(recv));
|
||||
if (rt == nil) { return -1i64; };
|
||||
if (rt.kind == nkind.N_TPTR) {
|
||||
rt = resolvealias(c, unwrapbang(rt.lhs));
|
||||
};
|
||||
if (rt == nil) { return -1i64; };
|
||||
if (rt.kind != nkind.N_TSTRUCT) { return -1i64; };
|
||||
let off: i64 = 0i64;
|
||||
let f: *node = rt.list;
|
||||
for (f != nil) {
|
||||
if (f.kind == nkind.N_TFIELD) {
|
||||
let fa: i64 = astalign(c, f.lhs);
|
||||
off = (off + fa - 1i64) & ~(fa - 1i64);
|
||||
if (streq(f.str, dot.str)) { return off; };
|
||||
off += astsize(c, f.lhs);
|
||||
};
|
||||
f = f.next;
|
||||
};
|
||||
return -1i64;
|
||||
};
|
||||
|
||||
// arenau64tos — decimal string for the folded INTLIT's `str` field.
|
||||
// Cstage uses aprintf("%llu") at the same site (cmd/wcc/check.c:921);
|
||||
// wwstage cgen only reads `uval` for N_INTLIT codegen so `str` is
|
||||
// just for the AST printer, but set it for parity with the parser's
|
||||
// own literal-emit shape.
|
||||
fn arenau64tos(a: *arena, v: u64) str = {
|
||||
let buf: *u8 = amalloc(a, 24u64): *u8;
|
||||
let i: i32 = 23;
|
||||
buf[i] = 0u8;
|
||||
if (v == 0u64) { i -= 1; buf[i] = 48u8; };
|
||||
let n: u64 = v;
|
||||
for (n > 0u64) {
|
||||
i -= 1;
|
||||
buf[i] = (48u64 + (n % 10u64)): u8;
|
||||
n /= 10u64;
|
||||
};
|
||||
let r: str;
|
||||
r.ptr = buf + (i: u64);
|
||||
r.len = 23 - i;
|
||||
return r;
|
||||
};
|
||||
|
||||
// foldtointlit — mutate `n` in place to an N_INTLIT with value `v`.
|
||||
// Used by the #42 size/align/offset intercepts so cgen sees the
|
||||
// folded literal rather than an unresolved call. Mirrors cstage
|
||||
// cmd/wcc/check.c:919-927 / :951-958.
|
||||
fn foldtointlit(c: *checker, n: *node, v: i64) void = {
|
||||
n.kind = nkind.N_INTLIT;
|
||||
n.uval = v: u64;
|
||||
n.str = arenau64tos(c.a, v: u64);
|
||||
n.lhs = nil;
|
||||
n.list = nil;
|
||||
let empty: str;
|
||||
n.tsuffix = empty;
|
||||
};
|
||||
|
||||
// exprtype — best-effort type-AST inference for an expression
|
||||
// node. Handles literals, identifiers, calls, and casts; returns
|
||||
// nil for shapes we don't statically know (binary ops, struct
|
||||
@@ -7661,6 +7883,57 @@ fn exprtype(c: *checker, e: *node) *node = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// #42: size(T) / align(T) / offset(e.f) typed-builtin intercepts.
|
||||
// Fold the N_CALL in place to an N_INTLIT so cgen never sees an
|
||||
// unresolved size/align/offset symbol. Same-module shadow gate
|
||||
// mirrors the alloc precedent (#23) so a user `fn size(...)`
|
||||
// inside this module suppresses the builtin. Mirrors cstage
|
||||
// cmd/wcc/check.c:907-960.
|
||||
if (callee.kind == nkind.N_IDENT) {
|
||||
let bname: str = callee.str;
|
||||
let issize: bool = streq(bname, "size");
|
||||
let isalign: bool = streq(bname, "align");
|
||||
let isoffset: bool = streq(bname, "offset");
|
||||
if (issize || isalign || isoffset) {
|
||||
let shadowed: bool = false;
|
||||
if (c.curmod.len > 0) {
|
||||
if (scopelookupinmodule(c.cur, c.curmod, bname) != nil) {
|
||||
shadowed = true;
|
||||
};
|
||||
};
|
||||
if (!shadowed) {
|
||||
if (e.list != nil) {
|
||||
if (issize) {
|
||||
let v: i64 = astsize(c, e.list);
|
||||
foldtointlit(c, e, v);
|
||||
return mktname(c, "i32");
|
||||
};
|
||||
if (isalign) {
|
||||
let v: i64 = astalign(c, e.list);
|
||||
foldtointlit(c, e, v);
|
||||
return mktname(c, "i32");
|
||||
};
|
||||
// offset(e.f): the arg is a value expression
|
||||
// (N_DOT), parsed via parsearglist — not a
|
||||
// type expression.
|
||||
if (isoffset) {
|
||||
if (e.list.next == nil && e.list.kind == nkind.N_DOT) {
|
||||
let off: i64 = astoffset(c, e.list);
|
||||
if (off < 0i64) {
|
||||
os.write(2, "offset: no field '".ptr, 18u64);
|
||||
os.write(2, e.list.str.ptr, e.list.str.len: u64);
|
||||
os.write(2, "'\n".ptr, 2u64);
|
||||
c.errs += 1;
|
||||
off = 0i64;
|
||||
};
|
||||
foldtointlit(c, e, off);
|
||||
return mktname(c, "i32");
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
let nm: str;
|
||||
nm.ptr = nil; nm.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) { nm = callee.str; };
|
||||
|
||||
@@ -3287,6 +3287,20 @@ fn count_emit(ctx: *void, b: u8) void = {
|
||||
c.n += 1;
|
||||
};
|
||||
|
||||
// --- size/align/offset typed-builtin fixtures (#42) --------------------
|
||||
|
||||
type point = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
};
|
||||
|
||||
// Mixed-alignment struct: i8 lays at 0, then i64 needs to skip to
|
||||
// offset 8 (the i64's natural align). Probe asserts both ends.
|
||||
type mixalign = struct {
|
||||
tag: i8,
|
||||
val: i64,
|
||||
};
|
||||
|
||||
// --- byte scanner like lex.ww's hot path -------------------------------
|
||||
|
||||
fn count_digits(s: str) i32 = {
|
||||
@@ -3380,6 +3394,33 @@ export fn main() i32 = {
|
||||
os.close(fd);
|
||||
if (n <= 0i64) { return 22; };
|
||||
|
||||
// Probe 8 — size(T) / align(T) / offset(e.f) typed-builtin folds
|
||||
// (#42). Each call folds to an N_INTLIT at check time; cgen
|
||||
// materialises the literal as a plain `MOVQ $N, AX`. Mirrors
|
||||
// cstage cmd/wcc/check.c:907-960 byte-for-byte on this corpus.
|
||||
if (size(str) != 16) { return 23; };
|
||||
if (size(i64) != 8) { return 24; };
|
||||
if (size(i32) != 4) { return 25; };
|
||||
if (align(i64) != 8) { return 26; };
|
||||
if (align(i32) != 4) { return 27; };
|
||||
// Initialize struct locals explicitly so the cgen path doesn't
|
||||
// drift from cstage on bare `let X: T;` zero-init (pre-existing
|
||||
// wwstage divergence outside #42).
|
||||
let pt: point = point { x = 0, y = 0 };
|
||||
if (offset(pt.x) != 0) { return 28; };
|
||||
if (offset(pt.y) != 4) { return 29; };
|
||||
let mx: mixalign = mixalign { tag = 0i8, val = 0i64 };
|
||||
if (offset(mx.tag) != 0) { return 30; };
|
||||
if (offset(mx.val) != 8) { return 31; }; // align-padded to 8
|
||||
// Width breadth: smallest prim, ptr, slice, struct (8B + padded),
|
||||
// covering astsize's TPTR/TSLICE/TNAME-resolve-to-struct arms.
|
||||
if (size(i8) != 1) { return 32; };
|
||||
if (align(i8) != 1) { return 33; };
|
||||
if (size(*i32) != 8) { return 34; };
|
||||
if (size([]i32) != 24) { return 35; };
|
||||
if (size(point) != 8) { return 36; };
|
||||
if (size(mixalign) != 16) { return 37; };
|
||||
|
||||
return 42;
|
||||
};
|
||||
|
||||
|
||||
@@ -74,6 +74,20 @@ fn count_emit(ctx: *void, b: u8) void = {
|
||||
c.n += 1;
|
||||
};
|
||||
|
||||
// --- size/align/offset typed-builtin fixtures (#42) --------------------
|
||||
|
||||
type point = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
};
|
||||
|
||||
// Mixed-alignment struct: i8 lays at 0, then i64 needs to skip to
|
||||
// offset 8 (the i64's natural align). Probe asserts both ends.
|
||||
type mixalign = struct {
|
||||
tag: i8,
|
||||
val: i64,
|
||||
};
|
||||
|
||||
// --- byte scanner like lex.ww's hot path -------------------------------
|
||||
|
||||
fn count_digits(s: str) i32 = {
|
||||
@@ -167,5 +181,32 @@ export fn main() i32 = {
|
||||
os.close(fd);
|
||||
if (n <= 0i64) { return 22; };
|
||||
|
||||
// Probe 8 — size(T) / align(T) / offset(e.f) typed-builtin folds
|
||||
// (#42). Each call folds to an N_INTLIT at check time; cgen
|
||||
// materialises the literal as a plain `MOVQ $N, AX`. Mirrors
|
||||
// cstage cmd/wcc/check.c:907-960 byte-for-byte on this corpus.
|
||||
if (size(str) != 16) { return 23; };
|
||||
if (size(i64) != 8) { return 24; };
|
||||
if (size(i32) != 4) { return 25; };
|
||||
if (align(i64) != 8) { return 26; };
|
||||
if (align(i32) != 4) { return 27; };
|
||||
// Initialize struct locals explicitly so the cgen path doesn't
|
||||
// drift from cstage on bare `let X: T;` zero-init (pre-existing
|
||||
// wwstage divergence outside #42).
|
||||
let pt: point = point { x = 0, y = 0 };
|
||||
if (offset(pt.x) != 0) { return 28; };
|
||||
if (offset(pt.y) != 4) { return 29; };
|
||||
let mx: mixalign = mixalign { tag = 0i8, val = 0i64 };
|
||||
if (offset(mx.tag) != 0) { return 30; };
|
||||
if (offset(mx.val) != 8) { return 31; }; // align-padded to 8
|
||||
// Width breadth: smallest prim, ptr, slice, struct (8B + padded),
|
||||
// covering astsize's TPTR/TSLICE/TNAME-resolve-to-struct arms.
|
||||
if (size(i8) != 1) { return 32; };
|
||||
if (align(i8) != 1) { return 33; };
|
||||
if (size(*i32) != 8) { return 34; };
|
||||
if (size([]i32) != 24) { return 35; };
|
||||
if (size(point) != 8) { return 36; };
|
||||
if (size(mixalign) != 16) { return 37; };
|
||||
|
||||
return 42;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user