330 lines
9.2 KiB
Plaintext
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 typeptr(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 typeslice(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 typearray(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 typechan(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 typenamed(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 typeisint(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 typeisint(t.under); };
|
|
return false;
|
|
};
|
|
|
|
export fn typeisfloat(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 typeisfloat(t.under); };
|
|
return false;
|
|
};
|
|
|
|
export fn typeisnum(t: *tinfo) bool = {
|
|
if (typeisint(t)) { return true; };
|
|
return typeisfloat(t);
|
|
};
|
|
|
|
export fn typeisunsigned(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 typeisunsigned(t.under); };
|
|
return false;
|
|
};
|
|
|
|
export fn typeisuntyped(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;
|
|
};
|
|
|
|
// typeeq — structural equality. Named types compare nominally.
|
|
export fn typeeq(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 typeeq(a.sub, b.sub); };
|
|
if (k == TY_SLICE) { return typeeq(a.sub, b.sub); };
|
|
if (k == TY_CHAN) { return typeeq(a.sub, b.sub); };
|
|
if (k == TY_ARRAY) {
|
|
if (a.alen != b.alen) { return false; };
|
|
return typeeq(a.sub, b.sub);
|
|
};
|
|
if (k == TY_FN) {
|
|
if (a.variadic != b.variadic) { return false; };
|
|
if (!typeeq(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 (!typeeq(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 (!typeeq(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 (!typeeq(pa.type_, pb.type_)) { return false; };
|
|
pa = pa.tnext;
|
|
pb = pb.tnext;
|
|
};
|
|
return true;
|
|
};
|
|
return true; // primitives match by kind alone
|
|
};
|