wcc: tagged-union foundations (never, void, flatten, exhaust)
- `never` bottom type: TY_NEVER, assignable to anything; size 0.
- Type-set normalization for N_TTAGGED in resolve_type:
- flatten nested anonymous (A|B)|C → (A|B|C); named aliases stay
nominal (not flattened through)
- dedup duplicates (NAMED pointer-id; others structural)
- drop `never` variants
- collapse single-element set: (T|never) → T, (T|T) → T
- Match exhaustiveness: error when a variant is unhandled and no
default arm covers it. Multi-pattern `case T1 | T2 =>` counts
each alt.
- (T | void) optionals: bare `return;` from a tagged-union-returning
fn emits the void variant's tag (payload undefined; void size 0).
selfhost mirrored: TY_NEVER constant + tynever in tctx + seedprim
entry; voidvariantindex helper; cgreturn bare-return handling.
This commit is contained in:
@@ -120,9 +120,21 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
};
|
||||
cgexpr(c, rhs);
|
||||
} else {
|
||||
// Bare `return;` in a void fn — zero AX so the caller
|
||||
// sees a deterministic value (matches C cgen, which
|
||||
// always falls through to `cgexpr_int(c, 0)`).
|
||||
// Bare `return;` from a tagged-union-returning fn is
|
||||
// the void variant: emit its tag. Payload is undefined
|
||||
// (void has size 0). Otherwise zero AX for determinism.
|
||||
if (istaggedtype(c.fnret)) {
|
||||
let idx: i32 = voidvariantindex(c.fnret);
|
||||
if (idx < 0) { idx = 0; };
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(idx: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\tBP, SP\n");
|
||||
emitline("\tPOPQ\tBP\n");
|
||||
emitline("\tRET\n");
|
||||
c.lastwasreturn = 1;
|
||||
return;
|
||||
};
|
||||
emitline("\tMOVQ\t$0, AX\n");
|
||||
};
|
||||
// SysV: 16-byte aggregates (str, 2-tuple) return in (AX, DX).
|
||||
|
||||
@@ -876,6 +876,24 @@ fn istaggedtype(t: *node) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
// voidvariantindex — find the 0-based index of the `void` variant in a
|
||||
// tagged-union type expr, -1 if absent. Used by cgreturn to map bare
|
||||
// `return;` in a tagged-union-returning fn to the void variant's tag.
|
||||
fn voidvariantindex(tagged: *node) i32 = {
|
||||
if (tagged == nil) { return -1; };
|
||||
if (tagged.kind != N_TTAGGED) { return -1; };
|
||||
let v: *node = tagged.list;
|
||||
let idx: i32 = 0;
|
||||
for (v != nil) {
|
||||
if (v.kind == N_TNAME) {
|
||||
if (streq(v.str, "void")) { return idx; };
|
||||
};
|
||||
v = v.next;
|
||||
idx += 1;
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
// rhstargetname — for a returned value, what's its declared (or
|
||||
// surface-inferred) type name? `expr: T` casts dictate T directly;
|
||||
// bare strlit/intlit fall back to a primitive name.
|
||||
|
||||
@@ -52,6 +52,7 @@ fn seedprimitives(c: *checker) void = {
|
||||
scopedefine(c.top, "f32", SK_TYPE, c.tc.tyf32, nil);
|
||||
scopedefine(c.top, "f64", SK_TYPE, c.tc.tyf64, nil);
|
||||
scopedefine(c.top, "str", SK_TYPE, c.tc.tystr, nil);
|
||||
scopedefine(c.top, "never", SK_TYPE, c.tc.tynever, nil);
|
||||
// `nil`, `true`, `false` are keywords — handled at the lex/parser
|
||||
// level, no symbol needed.
|
||||
// `len`, `alloc`, `free` are pseudo-builtins; scopedefine them so
|
||||
|
||||
Reference in New Issue
Block a user