Files
ww/selfhost/cmd/wcc/typ.ww
Hojun-Cho 2c33228b7e ww: rename toolchain to w-prefix + hare-style build/run/test driver
Plan 9-style w-prefix on the per-arch tools, disambiguating from the
real Plan 9 6c/6a/6l in ref/plan9front/:

    cmd/wwc/      → cmd/wcc/        libwwc.a → libwcc.a
    cmd/6{c,a,l}  → cmd/w6{c,a,l}   binary names too
    test/wwc/     → test/wcc/       6 test files w/ w6 prefix
    selfhost/cmd  mirror in lockstep
    bootstrap/amd64/{w6c,w6a,w6l}   snapshot binaries (gitignored)
    WW_6{C,A,L}   → WW_W6{C,A,L}    env-var overrides

Plan 9 source-tree refs ("Plan 9 6c shape", ref/plan9front/, etc.)
preserved. Hare-style driver, both C and ww sides:

    ww test [path]   discover *_test.ww in a directory module, run
                     each; single-file mode for `ww test foo.ww`
    Module-by-name   `ww build foo` resolves to foo.ww or foo/foo.ww
                     via search path (cwd : -I dirs : $WW_LIB)
    Default-to-cwd   `ww build` / `ww test` build the cwd module
    Run pass-through `ww run path arg1 arg2` reaches the program

lib/os: getcwd (79) and getdents64 (217) syscalls power `.` resolution
and directory enumeration on the ww side.

Makefile: wwstage tool deps now include lib/os/os.ww (+ lib/strconv
for wwdump_ww) so lib/* edits force their rebuild instead of leaving
stale binaries — surfaced when test 995 first failed against a stale
w6c_ww built before the lib/os additions.

Test 993 byte-identical parity gate (C-side ww vs ww-side ww_ww on a
build corpus) stays green; all 19 tests pass.
2026-05-11 13:49:27 +09:00

330 lines
9.2 KiB
Plaintext

// selfhost/cmd/wcc/type.ww — port of cmd/wcc/type.c.
//
// Status: full structural port. The C version uses module-globals for
// the primitive types (ty_void, ty_i32, …); ww doesn't have writable
// global storage yet, so we bundle the primitives into a `tctx` that
// the checker passes around explicitly. typesinit fills the tctx
// once per arena.
use os;
use mem;
// ---- TypeKind ---------------------------------------------------------
// Numeric values must stay aligned with cmd/wcc/ww.h TypeKind so the
// next diff signal (typed-AST printer / cgen) can compare across the
// two implementations.
def TY_NONE: i32 = 0;
def TY_VOID: i32 = 1;
def TY_BOOL: i32 = 2;
def TY_RUNE: i32 = 3;
def TY_I8: i32 = 4;
def TY_I16: i32 = 5;
def TY_I32: i32 = 6;
def TY_I64: i32 = 7;
def TY_U8: i32 = 8;
def TY_U16: i32 = 9;
def TY_U32: i32 = 10;
def TY_U64: i32 = 11;
def TY_UINT: i32 = 12;
def TY_INT: i32 = 13;
def TY_UINTPTR: i32 = 14;
def TY_F32: i32 = 15;
def TY_F64: i32 = 16;
def TY_STR: i32 = 17;
def TY_PTR: i32 = 18;
def TY_SLICE: i32 = 19;
def TY_ARRAY: i32 = 20;
def TY_STRUCT: i32 = 21;
def TY_FN: i32 = 22;
def TY_CHAN: i32 = 23;
def TY_NAMED: i32 = 24;
def TY_TUPLE: i32 = 25;
def TY_TAGGED: i32 = 26;
def TY_ERR: i32 = 27;
def TY_UNTYPED_INT: i32 = 28;
def TY_UNTYPED_FLOAT: i32 = 29;
def TY_UNTYPED_STR: i32 = 30;
def TY_UNTYPED_RUNE: i32 = 31;
def TY_UNTYPED_BOOL: i32 = 32;
def TY_UNTYPED_NIL: i32 = 33;
// ---- tinfo / tfield / tparam -----------------------------------------
type tfield = struct {
name: str,
type_: *tinfo,
offset: u64,
tnext: *tfield,
};
type tparam = struct {
name: str,
type_: *tinfo,
tnext: *tparam,
};
type tinfo = struct {
kind: i32,
size: u64,
align: u64,
sub: *tinfo, // ptr/slice/array/chan element
alen: u64,
fields: *tfield,
params: *tparam,
ret: *tinfo,
variadic: i32,
name: str,
under: *tinfo,
};
// ---- tctx — the box of primitive types -------------------------------
type tctx = struct {
a: *arena,
ty_void: *tinfo,
ty_bool: *tinfo,
ty_rune: *tinfo,
ty_i8: *tinfo,
ty_i16: *tinfo,
ty_i32: *tinfo,
ty_i64: *tinfo,
ty_u8: *tinfo,
ty_u16: *tinfo,
ty_u32: *tinfo,
ty_u64: *tinfo,
ty_int: *tinfo,
ty_uint: *tinfo,
ty_uintptr: *tinfo,
ty_f32: *tinfo,
ty_f64: *tinfo,
ty_str: *tinfo,
ty_err: *tinfo,
ty_untyped_int: *tinfo,
ty_untyped_float: *tinfo,
ty_untyped_str: *tinfo,
ty_untyped_rune: *tinfo,
ty_untyped_bool: *tinfo,
ty_untyped_nil: *tinfo,
};
// ---- constructors -----------------------------------------------------
export fn newtype(a: *arena, k: i32) *tinfo = {
let t: *tinfo = amalloc(a, 96u64): *tinfo;
t.kind = k;
return t;
};
fn prim(a: *arena, k: i32, nm: str, sz: u64, al: u64) *tinfo = {
let t: *tinfo = newtype(a, k);
t.name = nm;
t.size = sz;
if (al > 0u64) { t.align = al; } else { t.align = sz; };
return t;
};
export fn typesinit(c: *tctx, a: *arena) void = {
c.a = a;
c.ty_void = prim(a, TY_VOID, "void", 0u64, 1u64);
c.ty_bool = prim(a, TY_BOOL, "bool", 1u64, 1u64);
c.ty_rune = prim(a, TY_RUNE, "rune", 4u64, 4u64);
c.ty_i8 = prim(a, TY_I8, "i8", 1u64, 1u64);
c.ty_i16 = prim(a, TY_I16, "i16", 2u64, 2u64);
c.ty_i32 = prim(a, TY_I32, "i32", 4u64, 4u64);
c.ty_i64 = prim(a, TY_I64, "i64", 8u64, 8u64);
c.ty_u8 = prim(a, TY_U8, "u8", 1u64, 1u64);
c.ty_u16 = prim(a, TY_U16, "u16", 2u64, 2u64);
c.ty_u32 = prim(a, TY_U32, "u32", 4u64, 4u64);
c.ty_u64 = prim(a, TY_U64, "u64", 8u64, 8u64);
c.ty_int = prim(a, TY_INT, "int", 8u64, 8u64);
c.ty_uint = prim(a, TY_UINT, "uint", 8u64, 8u64);
c.ty_uintptr= prim(a, TY_UINTPTR, "uintptr", 8u64, 8u64);
c.ty_f32 = prim(a, TY_F32, "f32", 4u64, 4u64);
c.ty_f64 = prim(a, TY_F64, "f64", 8u64, 8u64);
c.ty_str = prim(a, TY_STR, "str", 16u64, 8u64);
c.ty_err = prim(a, TY_ERR, "<err>", 0u64, 1u64);
c.ty_untyped_int = prim(a, TY_UNTYPED_INT, "untyped_int", 0u64, 1u64);
c.ty_untyped_float = prim(a, TY_UNTYPED_FLOAT, "untyped_float", 0u64, 1u64);
c.ty_untyped_str = prim(a, TY_UNTYPED_STR, "untyped_str", 0u64, 1u64);
c.ty_untyped_rune = prim(a, TY_UNTYPED_RUNE, "untyped_rune", 0u64, 1u64);
c.ty_untyped_bool = prim(a, TY_UNTYPED_BOOL, "untyped_bool", 0u64, 1u64);
c.ty_untyped_nil = prim(a, TY_UNTYPED_NIL, "untyped_nil", 0u64, 1u64);
};
export fn type_ptr(a: *arena, sub: *tinfo) *tinfo = {
let t: *tinfo = newtype(a, TY_PTR);
t.sub = sub;
t.size = 8u64;
t.align = 8u64;
return t;
};
export fn type_slice(a: *arena, sub: *tinfo) *tinfo = {
let t: *tinfo = newtype(a, TY_SLICE);
t.sub = sub;
t.size = 24u64;
t.align = 8u64;
return t;
};
export fn type_array(a: *arena, sub: *tinfo, n: u64) *tinfo = {
let t: *tinfo = newtype(a, TY_ARRAY);
t.sub = sub;
t.alen = n;
if (sub != nil) {
t.size = sub.size * n;
t.align = sub.align;
} else {
t.align = 1u64;
};
return t;
};
export fn type_chan(a: *arena, sub: *tinfo) *tinfo = {
let t: *tinfo = newtype(a, TY_CHAN);
t.sub = sub;
t.size = 8u64;
t.align = 8u64;
return t;
};
export fn type_named(a: *arena, name: str, under: *tinfo) *tinfo = {
let t: *tinfo = newtype(a, TY_NAMED);
t.name = name;
t.under = under;
if (under != nil) {
t.size = under.size;
t.align = under.align;
};
return t;
};
// ---- predicates -------------------------------------------------------
export fn type_isint(t: *tinfo) bool = {
if (t == nil) { return false; };
let k: i32 = t.kind;
if (k == TY_I8) { return true; };
if (k == TY_I16) { return true; };
if (k == TY_I32) { return true; };
if (k == TY_I64) { return true; };
if (k == TY_U8) { return true; };
if (k == TY_U16) { return true; };
if (k == TY_U32) { return true; };
if (k == TY_U64) { return true; };
if (k == TY_INT) { return true; };
if (k == TY_UINT){ return true; };
if (k == TY_UINTPTR) { return true; };
if (k == TY_RUNE){ return true; };
if (k == TY_UNTYPED_INT) { return true; };
if (k == TY_UNTYPED_RUNE) { return true; };
if (k == TY_NAMED) { return type_isint(t.under); };
return false;
};
export fn type_isfloat(t: *tinfo) bool = {
if (t == nil) { return false; };
let k: i32 = t.kind;
if (k == TY_F32) { return true; };
if (k == TY_F64) { return true; };
if (k == TY_UNTYPED_FLOAT) { return true; };
if (k == TY_NAMED) { return type_isfloat(t.under); };
return false;
};
export fn type_isnum(t: *tinfo) bool = {
if (type_isint(t)) { return true; };
return type_isfloat(t);
};
export fn type_isunsigned(t: *tinfo) bool = {
if (t == nil) { return false; };
let k: i32 = t.kind;
if (k == TY_U8) { return true; };
if (k == TY_U16) { return true; };
if (k == TY_U32) { return true; };
if (k == TY_U64) { return true; };
if (k == TY_UINT){ return true; };
if (k == TY_UINTPTR) { return true; };
if (k == TY_NAMED) { return type_isunsigned(t.under); };
return false;
};
export fn type_isuntyped(t: *tinfo) bool = {
if (t == nil) { return false; };
let k: i32 = t.kind;
if (k == TY_UNTYPED_INT) { return true; };
if (k == TY_UNTYPED_FLOAT) { return true; };
if (k == TY_UNTYPED_STR) { return true; };
if (k == TY_UNTYPED_RUNE) { return true; };
if (k == TY_UNTYPED_BOOL) { return true; };
if (k == TY_UNTYPED_NIL) { return true; };
return false;
};
// type_eq — structural equality. Named types compare nominally.
export fn type_eq(a: *tinfo, b: *tinfo) bool = {
if (a == b) { return true; };
if (a == nil) { return false; };
if (b == nil) { return false; };
if (a.kind != b.kind) { return false; };
let k: i32 = a.kind;
if (k == TY_PTR) { return type_eq(a.sub, b.sub); };
if (k == TY_SLICE) { return type_eq(a.sub, b.sub); };
if (k == TY_CHAN) { return type_eq(a.sub, b.sub); };
if (k == TY_ARRAY) {
if (a.alen != b.alen) { return false; };
return type_eq(a.sub, b.sub);
};
if (k == TY_FN) {
if (a.variadic != b.variadic) { return false; };
if (!type_eq(a.ret, b.ret)) { return false; };
let pa: *tparam = a.params;
let pb: *tparam = b.params;
for (true) {
if (pa == nil) { if (pb == nil) { return true; }; return false; };
if (pb == nil) { return false; };
if (!type_eq(pa.type_, pb.type_)) { return false; };
pa = pa.tnext;
pb = pb.tnext;
};
return true;
};
if (k == TY_STRUCT) {
let fa: *tfield = a.fields;
let fb: *tfield = b.fields;
for (true) {
if (fa == nil) { if (fb == nil) { return true; }; return false; };
if (fb == nil) { return false; };
let na: str = fa.name;
let nb: str = fb.name;
if (na.len != nb.len) { return false; };
let i: i32 = 0;
for (i < na.len) {
if (na[i] != nb[i]) { return false; };
i += 1;
};
if (!type_eq(fa.type_, fb.type_)) { return false; };
fa = fa.tnext;
fb = fb.tnext;
};
return true;
};
if (k == TY_NAMED) { return false; }; // nominal: only same ptr
if (k == TY_TUPLE) {
let pa: *tparam = a.params;
let pb: *tparam = b.params;
for (true) {
if (pa == nil) { if (pb == nil) { return true; }; return false; };
if (pb == nil) { return false; };
if (!type_eq(pa.type_, pb.type_)) { return false; };
pa = pa.tnext;
pb = pb.tnext;
};
return true;
};
return true; // primitives match by kind alone
};