wwstage: port struct embedding; graduate #59.13
The last frontend-gap pin: wwstage had no Hare struct embedding
(struct { hash.hash, ... }), rejecting lib/crypto/sha256 at parse.
- parse.ww: the three member forms (named / anonymous struct / bare
dotted-ident embed), consume-then-branch since this parser has no
peek; embeds carry f.str == "" and the type in f.lhs.
- check.ww N_TSTRUCT flatten: promote the inner struct's flattened
fields at base+src.offset (check.c:961-990); the embed is one
nested-struct unit in the slot ladder; the resolved inner AST is
planted on the TFIELD rhs for cgen.
- check.ww walkers: astoffset / exprtype N_DOT / #251 struct-lit
field lookups descend embeds through shared helpers; the collision
and non-struct-embed rejects live in validatestructfields (the
once-per-decl diagnostic site).
- cgenutil.ww registerstruct: regfieldrun walks the AST against the
flattened tfield cursor, descending embeds via the planted inner
AST so promoted fieldinfo entries keep the inner field's own name
and type node.
- wwi printers unchanged (both stages already emit nameless fields).
sha256_test compiles byte-identically end to end and its 6 tests
pass; 989_lib_byteid is now 44 id / 0 divergent / 0 wwreject.
Fixtures r5913_* (promoted rw, offset shift, anonymous embed,
two-level embed + promoted fn-ptr callee, three rejects); corpus pin
1485/2970.
This commit is contained in:
@@ -193,8 +193,12 @@ static const struct ent ents[] = {
|
||||
{ .fixture = "lib/ww/syntax/asttest.ww", .inc = "lib/ww",
|
||||
.mode = M_ID, .cite = "#59.12 graduated by #146 str==" },
|
||||
/* -------- wwstage front-end gaps (task #59) -------------- */
|
||||
/* #59.13 graduated: wwstage struct embedding (parser three member
|
||||
* forms, checker tinfo flatten + promoted-name walkers, cgen
|
||||
* registerstruct embed descend) — the hash.hash embed compiles
|
||||
* byte-identically end to end. Runtime pins: r5913_*. */
|
||||
{ .fixture = "lib/crypto/sha256/sha256_test.ww",
|
||||
.mode = M_WWREJECT, .cite = "#59.13" },
|
||||
.mode = M_ID, .cite = "#59.13 graduated by the embed port" },
|
||||
/* #59.14 graduated to M_ID by #27: the #27b enum-as-int cgen fix
|
||||
* (value passthrough, not a tagged assertion) + the fnmatch.ww:126
|
||||
* `'\\': u8` explicit cast (the rune-lit→u8 narrow w6c_ww rejects at
|
||||
|
||||
21
test/wcc/data/r5913_anon_embed/case.ww
Normal file
21
test/wcc/data/r5913_anon_embed/case.ww
Normal file
@@ -0,0 +1,21 @@
|
||||
//ww:run-exit 42
|
||||
package main;
|
||||
|
||||
// #59.13: anonymous embedded struct — its fields promote to the outer
|
||||
// scope like a bare-name embed's.
|
||||
|
||||
type withanon = struct {
|
||||
struct {
|
||||
ax: i32,
|
||||
ay: i32,
|
||||
},
|
||||
z: i32,
|
||||
};
|
||||
|
||||
fn main() i32 = {
|
||||
let w: withanon;
|
||||
w.ax = 13;
|
||||
w.ay = 14;
|
||||
w.z = 15;
|
||||
return w.ax + w.ay + w.z;
|
||||
};
|
||||
17
test/wcc/data/r5913_collide_reject/case.ww
Normal file
17
test/wcc/data/r5913_collide_reject/case.ww
Normal file
@@ -0,0 +1,17 @@
|
||||
//ww:error "embedded field 'x' collides with existing field"
|
||||
package main;
|
||||
|
||||
// #59.13: a promoted name colliding with an earlier named field is a
|
||||
// loud reject on both stages (cstage check.c:971-980).
|
||||
|
||||
type inner = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
};
|
||||
|
||||
type outer = struct {
|
||||
x: i32,
|
||||
inner,
|
||||
};
|
||||
|
||||
fn main() i32 = { return 0; };
|
||||
34
test/wcc/data/r5913_deep_fnptr/case.ww
Normal file
34
test/wcc/data/r5913_deep_fnptr/case.ww
Normal file
@@ -0,0 +1,34 @@
|
||||
//ww:run-exit 33
|
||||
package main;
|
||||
|
||||
// #59.13: two-level embedding (outer2 embeds deep embeds cbs) — the
|
||||
// promotion flattens transitively, and a promoted `*fn(...)` field
|
||||
// still resolves as an indirect callee (the fieldinfo run keeps the
|
||||
// inner field's own type node).
|
||||
|
||||
type cbs = struct {
|
||||
cb: *fn(x: i32) i32,
|
||||
k: i32,
|
||||
};
|
||||
|
||||
type deep = struct {
|
||||
cbs,
|
||||
extra: i32,
|
||||
};
|
||||
|
||||
type outer2 = struct {
|
||||
deep,
|
||||
tail: i32,
|
||||
};
|
||||
|
||||
fn twice(x: i32) i32 = { return x * 2; };
|
||||
|
||||
fn main() i32 = {
|
||||
let o: outer2;
|
||||
o.cb = &twice;
|
||||
o.k = 4;
|
||||
o.extra = 5;
|
||||
o.tail = 6;
|
||||
let r: i32 = o.cb(9);
|
||||
return r + o.k + o.extra + o.tail;
|
||||
};
|
||||
18
test/wcc/data/r5913_dup_after_embed_reject/case.ww
Normal file
18
test/wcc/data/r5913_dup_after_embed_reject/case.ww
Normal file
@@ -0,0 +1,18 @@
|
||||
//ww:error "duplicate field 'x'"
|
||||
package main;
|
||||
|
||||
// #59.13: a named field colliding with an earlier embed's promoted
|
||||
// name is the plain duplicate-field reject (cstage check.c:943-950
|
||||
// compares against the flattened head, which includes promotions).
|
||||
|
||||
type inner = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
};
|
||||
|
||||
type outer = struct {
|
||||
inner,
|
||||
x: i32,
|
||||
};
|
||||
|
||||
fn main() i32 = { return 0; };
|
||||
11
test/wcc/data/r5913_nonstruct_reject/case.ww
Normal file
11
test/wcc/data/r5913_nonstruct_reject/case.ww
Normal file
@@ -0,0 +1,11 @@
|
||||
//ww:error "embedded type must be a struct"
|
||||
package main;
|
||||
|
||||
// #59.13: only struct types embed (cstage check.c:964-968).
|
||||
|
||||
type outer = struct {
|
||||
i32,
|
||||
c: i32,
|
||||
};
|
||||
|
||||
fn main() i32 = { return 0; };
|
||||
37
test/wcc/data/r5913_offset_shift_nested/case.ww
Normal file
37
test/wcc/data/r5913_offset_shift_nested/case.ww
Normal file
@@ -0,0 +1,37 @@
|
||||
//ww:run-exit 42
|
||||
package main;
|
||||
|
||||
// #59.13: fields declared after an embed sit at base+inner.size, the
|
||||
// offset() fold sees promoted names, and promoted access works through
|
||||
// a pointer receiver and a nested chain (o.p.y — promoted struct field
|
||||
// then its own member).
|
||||
|
||||
type pos = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
};
|
||||
|
||||
type mid = struct {
|
||||
p: pos,
|
||||
tag: i32,
|
||||
};
|
||||
|
||||
type outer = struct {
|
||||
mid,
|
||||
c: i32,
|
||||
};
|
||||
|
||||
fn bump(o: *outer) void = {
|
||||
o.tag += 1;
|
||||
o.p.y = o.p.x + 2;
|
||||
};
|
||||
|
||||
fn main() i32 = {
|
||||
let o: outer;
|
||||
o.p.x = 5;
|
||||
o.tag = 9;
|
||||
o.c = 20;
|
||||
bump(&o);
|
||||
let shift: i64 = offset(o.c) - offset(o.tag);
|
||||
return o.p.y + o.tag + o.c + shift: i32 + 1;
|
||||
};
|
||||
23
test/wcc/data/r5913_value_promoted_rw/case.ww
Normal file
23
test/wcc/data/r5913_value_promoted_rw/case.ww
Normal file
@@ -0,0 +1,23 @@
|
||||
//ww:run-exit 42
|
||||
package main;
|
||||
|
||||
// #59.13: bare-name struct embedding — promoted fields read and write
|
||||
// like inline declarations. Min repro of the wwstage parse cascade.
|
||||
|
||||
type inner = struct {
|
||||
a: i32,
|
||||
b: i32,
|
||||
};
|
||||
|
||||
type outer = struct {
|
||||
inner,
|
||||
c: i32,
|
||||
};
|
||||
|
||||
fn main() i32 = {
|
||||
let o: outer;
|
||||
o.a = 7;
|
||||
o.b = 11;
|
||||
o.c = 24;
|
||||
return o.a + o.b + o.c;
|
||||
};
|
||||
Reference in New Issue
Block a user