ww+wcc: Hare-strict enum types — back out the int↔enum relaxation
Cascades the four enum kinds through every signature and local that
holds one of their values, then removes the type_assignable /
unify_arith relaxation that previously let bare i32 mix with the
named enum types.
Signature updates:
- kwlookup() now returns `tkind` (not i32); tokname() takes `tkind`
- accepttok / expecttok / bprec / isassignop take `tkind`
- parsearglist's closekind is `tkind`
- newtype / prim take `tykind`; scopedefine takes `skind`
- newnode / nkname take `nkind`
Struct fields:
- tok.kind is `tkind`; parser.curkind is `tkind`
- node.kind is `nkind`; node.op is `tkind`
- tinfo.kind is `tykind`; sym.skind is `skind`
Locals holding kinds across lex/parse/check/cgen are now typed with
their enum, including sentinel patterns like `let lkind: nkind =
nkind.N_NONE; if (...) lkind = tn.kind;`.
The selfhost cgen had a load-width bug exposed by this: fieldsize()
fell back to 8 bytes for any TNAME that wasn't a struct or primitive.
For a tkind-typed field that gave `MOVQ (BX), AX` instead of `MOVL`,
diverging from the C cgen on tok.kind / parser.curkind / etc. Two
fixes:
- fieldsize now consults the enum registry and returns the storage
type's size (4 for `enum i32`)
- collectenums runs before collectstructs in cgfile so the registry
is populated when registerstruct asks for field sizes
All 22 tests stay green; 990/993/995 byte-identity probes pass with
the strict typing in place.
This commit is contained in:
@@ -531,17 +531,6 @@ unify_arith(Checker *c, Pos p, Type *a, Type *b)
|
||||
if (type_isuntyped(a) && type_assignable(b, a)) return b;
|
||||
if (type_isuntyped(b) && type_assignable(a, b)) return a;
|
||||
if (type_eq(a, b)) return a;
|
||||
/* Enum ↔ integer storage: pair `(tkind, i32)` operands unify to
|
||||
* the storage int. Mirrors the same relaxation in type_assignable;
|
||||
* lets `cur.kind == TK_FN` typecheck without a cast on either side. */
|
||||
{
|
||||
Type *au = (a->kind == TY_NAMED) ? a->under : a;
|
||||
Type *bu = (b->kind == TY_NAMED) ? b->under : b;
|
||||
if (au && au->kind == TY_ENUM && type_isint(b) &&
|
||||
type_eq(au->sub, b)) return b;
|
||||
if (bu && bu->kind == TY_ENUM && type_isint(a) &&
|
||||
type_eq(a, bu->sub)) return a;
|
||||
}
|
||||
return err(c, p, "operands have differing types %s and %s",
|
||||
type_name(c->a, a), type_name(c->a, b));
|
||||
}
|
||||
|
||||
@@ -295,21 +295,6 @@ type_assignable(Type *dst, Type *src)
|
||||
if (dst->kind == TY_NAMED && type_eq(dst->under, src)) return 1;
|
||||
if (src->kind == TY_NAMED && type_eq(dst, src->under)) return 1;
|
||||
|
||||
/* Enum ↔ integer storage: bare i32 flows into a `tkind` slot and
|
||||
* vice-versa as long as the storage type matches. Hare-strict
|
||||
* would require an explicit cast, but the ww frontend's tkind /
|
||||
* nkind enums have hundreds of `let k: i32 = expr` sites we'd
|
||||
* otherwise have to migrate in lockstep — the relaxation is
|
||||
* explicit and limited to int-typed enums. */
|
||||
{
|
||||
Type *du = (dst->kind == TY_NAMED) ? dst->under : dst;
|
||||
Type *su = (src->kind == TY_NAMED) ? src->under : src;
|
||||
if (du && du->kind == TY_ENUM && type_isint(src) &&
|
||||
type_eq(du->sub, src)) return 1;
|
||||
if (su && su->kind == TY_ENUM && type_isint(dst) &&
|
||||
type_eq(dst, su->sub)) return 1;
|
||||
}
|
||||
|
||||
/* Tuple-to-tuple: element-wise assignable. */
|
||||
if (dst->kind == TY_TUPLE && src->kind == TY_TUPLE) {
|
||||
Tparam *pa = dst->params, *pb = src->params;
|
||||
|
||||
Reference in New Issue
Block a user