w6c+wcc: widen struct/tagged-subset, parse ... spread
Three tagged-union gaps:
1. Struct-payload widening was broken at every site (call, let,
assign, return, struct-field init). cg_widen_tagged_store now
materialises str / scalar / struct-lit / struct-ident / tagged
payloads at slot+8+field_off and writes the tag last. Call sites
route through cg_widen_tagged_push (scratch slot + push high→low).
2. Tagged → wider tagged widening forwarded the source tag verbatim.
cg_widen_tag_remap emits a CMPQ-chain switch that translates each
source variant index to the destination's, then zero-pads to the
wider slot. type_eq grew a TY_TAGGED arm (was returning 1 for any
two unions); type_assignable now accepts variant-subset and
rejects the rest.
3. `(...inner | T)` spread parses (cmd/wcc/parse.c, lib/ww/parse).
Marks Node.op = TK_ELLIPSIS; resolve_type unwraps NAMED + flattens
when the spread bit is set so aliases inline like Hare's
tagged_type unwrap flag.
Selfhost mirror: spread parser ported. Cgen widen helpers not yet
mirrored — wwstage stays byte-identical to cstage on the existing
test corpus, but will emit wrong asm if user code uses the new
patterns (probe sp2 shows the divergence).
700_e2e: 9 new rows covering call/let/assign/return × struct +
tagged subset, plus the spread-flatten case.
This commit is contained in:
@@ -828,6 +828,133 @@ static const struct row rows[] = {
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};", 3 },
|
||||
/* Struct variant of a tagged union at the call site. The arg is
|
||||
* an N_STRUCTLIT, the param is (str|point). Widening at the call
|
||||
* site must zero-fill the scratch slot, store each field at
|
||||
* slot+8+field_off, then push the slot words high→low. */
|
||||
{ "type point = struct { x: i32, y: i32 };\n"
|
||||
"fn classify(r: (str | point)) i32 = {\n"
|
||||
" match (r) {\n"
|
||||
" case let s: str => return 0 - s.len: i32;\n"
|
||||
" case let p: point => return p.x + p.y;\n"
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" return classify(point { x = 10, y = 20 });\n"
|
||||
"};", 30 },
|
||||
/* Struct variant passed as a typed local. Widening copies the
|
||||
* struct words from the local into the scratch slot at +8. */
|
||||
{ "type point = struct { x: i32, y: i32 };\n"
|
||||
"fn classify(r: (str | point)) i32 = {\n"
|
||||
" match (r) {\n"
|
||||
" case let s: str => return 0 - s.len: i32;\n"
|
||||
" case let p: point => return p.x + p.y;\n"
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let p: point = point { x = 11, y = 22 };\n"
|
||||
" return classify(p);\n"
|
||||
"};", 33 },
|
||||
/* let-init of a tagged-union local from a struct literal: the
|
||||
* field stores go into slot+8+field_off in-place; tag patched
|
||||
* last. */
|
||||
{ "type point = struct { x: i32, y: i32 };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let r: (str | point) = point { x = 7, y = 35 };\n"
|
||||
" match (r) {\n"
|
||||
" case let s: str => return 0 - s.len: i32;\n"
|
||||
" case let p: point => return p.x + p.y;\n"
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};", 42 },
|
||||
/* Reassign a tagged-union local to a struct literal. Same path
|
||||
* as let-init but writing into an already-allocated slot. */
|
||||
{ "type point = struct { x: i32, y: i32 };\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let r: (str | point) = \"init\";\n"
|
||||
" r = point { x = 100, y = 23 };\n"
|
||||
" match (r) {\n"
|
||||
" case let s: str => return 0 - s.len: i32;\n"
|
||||
" case let p: point => return p.x + p.y;\n"
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};", 123 },
|
||||
/* Return a struct variant of the fn's tagged return type. The
|
||||
* scratch-slot path materialises the struct payload then loads
|
||||
* AX/DX/CX from it. */
|
||||
{ "type point = struct { x: i32, y: i32 };\n"
|
||||
"fn make() (str | point) = {\n"
|
||||
" return point { x = 12, y = 30 };\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let r: (str | point) = make();\n"
|
||||
" match (r) {\n"
|
||||
" case let s: str => return 0 - s.len: i32;\n"
|
||||
" case let p: point => return p.x + p.y;\n"
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};", 42 },
|
||||
/* Widen a smaller tagged union to a wider one across slot sizes
|
||||
* AND remapped variant indices. (i32 | rune) is 16B with i32 at
|
||||
* tag 0; (str | i32 | rune) is 24B with i32 at tag 1. The widen
|
||||
* path copies the slot words, zero-pads to 24B, then runs a
|
||||
* CMPQ-chain switch to remap src tag 0 → dst tag 1. */
|
||||
{ "fn classify(r: (str | i32 | rune)) i32 = {\n"
|
||||
" match (r) {\n"
|
||||
" case let s: str => return 1;\n"
|
||||
" case let n: i32 => return n;\n"
|
||||
" case let c: rune => return c: i32 + 100;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let inner: (i32 | rune) = 42: i32;\n"
|
||||
" return classify(inner);\n"
|
||||
"};", 42 },
|
||||
/* Same shape but the rune variant of inner exercises the tag
|
||||
* remap from src tag 1 → dst tag 2. The rune literal needs an
|
||||
* explicit `: rune` cast — `'A'` is an untyped rune and the
|
||||
* variant search picks the first variant that accepts it (i32,
|
||||
* which also accepts untyped runes). 'A' = 65 + 100 = 165. */
|
||||
{ "fn classify(r: (str | i32 | rune)) i32 = {\n"
|
||||
" match (r) {\n"
|
||||
" case let s: str => return 1;\n"
|
||||
" case let n: i32 => return n;\n"
|
||||
" case let c: rune => return c: i32 + 100;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let inner: (i32 | rune) = 'A': rune;\n"
|
||||
" return classify(inner);\n"
|
||||
"};", 165 },
|
||||
/* let-init of a wider tagged union from a smaller-tagged local. */
|
||||
{ "fn main() i32 = {\n"
|
||||
" let inner: (i32 | rune) = 42: i32;\n"
|
||||
" let r: (str | i32 | rune) = inner;\n"
|
||||
" match (r) {\n"
|
||||
" case let s: str => return 1;\n"
|
||||
" case let n: i32 => return n;\n"
|
||||
" case let c: rune => return c: i32 + 100;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};", 42 },
|
||||
/* Spread variant in tagged-union type — `(...inner | T)`. The
|
||||
* checker flattens the spread's variants into the enclosing
|
||||
* union so `outer` has variants {i32, rune, str}. */
|
||||
{ "type inner = (i32 | rune);\n"
|
||||
"type outer = (...inner | str);\n"
|
||||
"fn main() i32 = {\n"
|
||||
" let r: outer = 42: i32;\n"
|
||||
" match (r) {\n"
|
||||
" case let n: i32 => return n;\n"
|
||||
" case let c: rune => return c: i32;\n"
|
||||
" case let s: str => return 0;\n"
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};", 42 },
|
||||
/* Plan 9-style sentinel error idiom: `def NAME: error = "lit"`
|
||||
* inlines as the (ptr, len) pair at use sites. */
|
||||
{ "type error = str;\n"
|
||||
|
||||
Reference in New Issue
Block a user