/* * type.c — Type values and structural equality. * * Built-in types are constructed once and exposed as globals so the * rest of the compiler can `==`-compare them. Compound types (ptr, * slice, array, fn, struct, chan) are constructed on demand and * de-duplicated when equality is cheap (only ptr/slice for now). */ #include "ww.h" #include Type *ty_void, *ty_bool, *ty_rune; Type *ty_i8, *ty_i16, *ty_i32, *ty_i64; Type *ty_u8, *ty_u16, *ty_u32, *ty_u64; Type *ty_int, *ty_uint, *ty_uintptr; Type *ty_f32, *ty_f64, *ty_str; Type *ty_err; Type *ty_never; Type *ty_untyped_int, *ty_untyped_float, *ty_untyped_str; Type *ty_untyped_rune, *ty_untyped_bool, *ty_untyped_nil; Type * newtype(Arena *a, TypeKind k) { Type *t = amalloc(a, sizeof *t); t->kind = k; return t; } static Type * prim(Arena *a, TypeKind k, const char *nm, u64 sz, u64 al) { Type *t = newtype(a, k); t->name = nm; t->size = sz; t->align = al ? al : sz; return t; } void typesinit(Arena *a) { /* Always re-init: callers create a fresh arena per compilation unit * and free it; old globals point at freed memory. */ ty_void = prim(a, TY_VOID, "void", 0, 1); ty_bool = prim(a, TY_BOOL, "bool", 1, 1); ty_rune = prim(a, TY_RUNE, "rune", 4, 4); ty_i8 = prim(a, TY_I8, "i8", 1, 1); ty_i16 = prim(a, TY_I16, "i16", 2, 2); ty_i32 = prim(a, TY_I32, "i32", 4, 4); ty_i64 = prim(a, TY_I64, "i64", 8, 8); ty_u8 = prim(a, TY_U8, "u8", 1, 1); ty_u16 = prim(a, TY_U16, "u16", 2, 2); ty_u32 = prim(a, TY_U32, "u32", 4, 4); ty_u64 = prim(a, TY_U64, "u64", 8, 8); ty_int = prim(a, TY_INT, "int", 8, 8); /* amd64 */ ty_uint = prim(a, TY_UINT, "uint", 8, 8); ty_uintptr= prim(a, TY_UINTPTR,"uintptr", 8, 8); ty_f32 = prim(a, TY_F32, "f32", 4, 4); ty_f64 = prim(a, TY_F64, "f64", 8, 8); /* str is { *u8, len } — 16 bytes on amd64. ABI: pointer + u64. */ ty_str = prim(a, TY_STR, "str", 16, 8); ty_err = prim(a, TY_ERR, "", 0, 1); ty_never = prim(a, TY_NEVER, "never", 0, 1); ty_untyped_int = prim(a, TY_UNTYPED_INT, "untyped_int", 0, 1); ty_untyped_float = prim(a, TY_UNTYPED_FLOAT, "untyped_float", 0, 1); ty_untyped_str = prim(a, TY_UNTYPED_STR, "untyped_str", 0, 1); ty_untyped_rune = prim(a, TY_UNTYPED_RUNE, "untyped_rune", 0, 1); ty_untyped_bool = prim(a, TY_UNTYPED_BOOL, "untyped_bool", 0, 1); ty_untyped_nil = prim(a, TY_UNTYPED_NIL, "untyped_nil", 0, 1); } Type * type_ptr(Arena *a, Type *sub) { Type *t = newtype(a, TY_PTR); t->sub = sub; t->size = 8; t->align = 8; return t; } Type * type_slice(Arena *a, Type *sub) { Type *t = newtype(a, TY_SLICE); t->sub = sub; t->size = 24; /* { *T, len, cap } */ t->align = 8; return t; } Type * type_array(Arena *a, Type *sub, u64 len) { Type *t = newtype(a, TY_ARRAY); t->sub = sub; t->alen = len; t->size = sub ? sub->size * len : 0; t->align = sub ? sub->align : 1; return t; } Type * type_chan(Arena *a, Type *sub) { Type *t = newtype(a, TY_CHAN); t->sub = sub; t->size = 8; /* opaque ptr */ t->align = 8; return t; } Type * type_named(Arena *a, const char *name, Type *under) { Type *t = newtype(a, TY_NAMED); t->name = name; t->under = under; if (under) { t->size = under->size; t->align = under->align; t->iserror = under->iserror; } return t; } int type_isint(Type *t) { if (t == NULL) return 0; switch (t->kind) { case TY_I8: case TY_I16: case TY_I32: case TY_I64: case TY_U8: case TY_U16: case TY_U32: case TY_U64: case TY_INT: case TY_UINT: case TY_UINTPTR: case TY_RUNE: case TY_UNTYPED_INT: case TY_UNTYPED_RUNE: return 1; case TY_ENUM: return type_isint(t->sub); case TY_NAMED: return type_isint(t->under); default: return 0; } } int type_isfloat(Type *t) { if (t == NULL) return 0; switch (t->kind) { case TY_F32: case TY_F64: case TY_UNTYPED_FLOAT: return 1; case TY_NAMED: return type_isfloat(t->under); default: return 0; } } int type_isnum(Type *t) { return type_isint(t) || type_isfloat(t); } int type_isunsigned(Type *t) { if (t == NULL) return 0; switch (t->kind) { case TY_U8: case TY_U16: case TY_U32: case TY_U64: case TY_UINT: case TY_UINTPTR: return 1; case TY_NAMED: return type_isunsigned(t->under); default: return 0; } } int type_isuntyped(Type *t) { if (t == NULL) return 0; switch (t->kind) { case TY_UNTYPED_INT: case TY_UNTYPED_FLOAT: case TY_UNTYPED_STR: case TY_UNTYPED_RUNE: case TY_UNTYPED_BOOL: case TY_UNTYPED_NIL: return 1; default: return 0; } } Type * type_default(Type *t) { if (t == NULL) return NULL; switch (t->kind) { case TY_UNTYPED_INT: return ty_i32; case TY_UNTYPED_FLOAT: return ty_f64; case TY_UNTYPED_STR: return ty_str; case TY_UNTYPED_RUNE: return ty_rune; case TY_UNTYPED_BOOL: return ty_bool; case TY_UNTYPED_NIL: return NULL; /* needs context */ default: return t; } } int type_eq(Type *a, Type *b) { if (a == b) return 1; if (a == NULL || b == NULL) return 0; if (a->kind != b->kind) return 0; switch (a->kind) { case TY_PTR: case TY_SLICE: case TY_CHAN: return type_eq(a->sub, b->sub); case TY_ARRAY: return a->alen == b->alen && type_eq(a->sub, b->sub); case TY_FN: { if (a->variadic != b->variadic) return 0; if (!type_eq(a->ret, b->ret)) return 0; Tparam *pa = a->params, *pb = b->params; while (pa && pb) { if (pa->variadic != pb->variadic) return 0; if (!type_eq(pa->type, pb->type)) return 0; pa = pa->next; pb = pb->next; } return pa == NULL && pb == NULL; } case TY_STRUCT: { Tfield *fa = a->fields, *fb = b->fields; while (fa && fb) { if (strcmp(fa->name, fb->name) != 0) return 0; if (!type_eq(fa->type, fb->type)) return 0; fa = fa->next; fb = fb->next; } return fa == NULL && fb == NULL; } case TY_NAMED: return a == b; /* nominally equal only when same node */ case TY_TUPLE: { Tparam *pa = a->params, *pb = b->params; while (pa && pb) { if (!type_eq(pa->type, pb->type)) return 0; pa = pa->next; pb = pb->next; } return pa == NULL && pb == NULL; } case TY_TAGGED: { /* Tagged unions are structurally equal iff variant lists * match position-by-position. Nullable fold is a per-Type * flag, so equal-up-to-fold types compare not-equal here — * the caller can unwrap intentionally if needed. */ if (a->nullable != b->nullable) return 0; Tparam *pa = a->params, *pb = b->params; while (pa && pb) { if (!type_eq(pa->type, pb->type)) return 0; pa = pa->next; pb = pb->next; } return pa == NULL && pb == NULL; } default: return 1; /* primitives */ } } int type_assignable(Type *dst, Type *src) { if (dst == NULL || src == NULL) return 0; if (dst == ty_err || src == ty_err) return 1; /* swallow */ if (src == ty_never) return 1; /* bottom flows into anything */ if (type_eq(dst, src)) return 1; /* Tagged-union assignment: * - concrete → tagged: src must match one of dst's variants. * - tagged → tagged: src is assignable when every variant of * src appears as a variant of dst (Hare-style subset). Tag * remap at the use site handles different variant indices. * Checked before the untyped branch so untyped literals flow * through to a variant's typed slot. Unwraps a NAMED alias on * either side so `type result = (T|E);` accepts variants too. */ { Type *du = (dst->kind == TY_NAMED) ? dst->under : dst; Type *su = (src->kind == TY_NAMED) ? src->under : src; if (du && du->kind == TY_TAGGED && !(su && su->kind == TY_TAGGED)) { for (Tparam *p = du->params; p; p = p->next) if (type_assignable(p->type, src)) return 1; return 0; } if (du && du->kind == TY_TAGGED && su && su->kind == TY_TAGGED) { for (Tparam *sp = su->params; sp; sp = sp->next) { int ok = 0; for (Tparam *dp = du->params; dp; dp = dp->next) if (type_eq(dp->type, sp->type)) { ok = 1; break; } if (!ok) return 0; } return 1; } } /* Untyped → typed: only if the typed kind can hold the value. */ if (type_isuntyped(src)) { if (src->kind == TY_UNTYPED_INT && type_isnum(dst)) return 1; if (src->kind == TY_UNTYPED_FLOAT && type_isfloat(dst)) return 1; if (src->kind == TY_UNTYPED_STR && (dst->kind == TY_STR || (dst->kind == TY_NAMED && dst->under && dst->under->kind == TY_STR))) return 1; if (src->kind == TY_UNTYPED_RUNE && (type_isint(dst) || dst->kind == TY_RUNE)) return 1; if (src->kind == TY_UNTYPED_BOOL && (dst->kind == TY_BOOL || (dst->kind == TY_NAMED && dst->under && dst->under->kind == TY_BOOL))) return 1; if (src->kind == TY_UNTYPED_NIL) { Type *du = (dst->kind == TY_NAMED) ? dst->under : dst; if (du && (du->kind == TY_PTR || du->kind == TY_SLICE || du->kind == TY_CHAN || du->kind == TY_FN)) return 1; } return 0; } /* Named on either side: compare to the underlying. NAMED is a * distinct type from its under; but assignment from under to * named (and vice-versa) is allowed in this minimal checker. */ if (dst->kind == TY_NAMED && type_eq(dst->under, src)) return 1; if (src->kind == TY_NAMED && type_eq(dst, src->under)) return 1; /* Tuple-to-tuple: element-wise assignable. */ if (dst->kind == TY_TUPLE && src->kind == TY_TUPLE) { Tparam *pa = dst->params, *pb = src->params; while (pa && pb) { if (!type_assignable(pa->type, pb->type)) return 0; pa = pa->next; pb = pb->next; } return pa == NULL && pb == NULL; } return 0; } const char * type_name(Arena *a, Type *t) { if (t == NULL) return ""; switch (t->kind) { case TY_NONE: return ""; case TY_VOID: return "void"; case TY_BOOL: return "bool"; case TY_RUNE: return "rune"; case TY_I8: return "i8"; case TY_I16: return "i16"; case TY_I32: return "i32"; case TY_I64: return "i64"; case TY_U8: return "u8"; case TY_U16: return "u16"; case TY_U32: return "u32"; case TY_U64: return "u64"; case TY_INT: return "int"; case TY_UINT: return "uint"; case TY_UINTPTR: return "uintptr"; case TY_F32: return "f32"; case TY_F64: return "f64"; case TY_STR: return "str"; case TY_ERR: return ""; case TY_NEVER: return "never"; case TY_UNTYPED_INT: return "untyped_int"; case TY_UNTYPED_FLOAT: return "untyped_float"; case TY_UNTYPED_STR: return "untyped_str"; case TY_UNTYPED_RUNE: return "untyped_rune"; case TY_UNTYPED_BOOL: return "untyped_bool"; case TY_UNTYPED_NIL: return "untyped_nil"; case TY_PTR: return aprintf(a, "*%s", type_name(a, t->sub)); case TY_SLICE: return aprintf(a, "[]%s", type_name(a, t->sub)); case TY_ARRAY: return aprintf(a, "[%llu]%s", (unsigned long long)t->alen, type_name(a, t->sub)); case TY_CHAN: return aprintf(a, "chan %s", type_name(a, t->sub)); case TY_FN: { const char *r = t->ret ? type_name(a, t->ret) : "void"; const char *acc = ""; for (Tparam *p = t->params; p; p = p->next) { const char *pn = type_name(a, p->type); acc = acc[0] ? aprintf(a, "%s, %s", acc, pn) : pn; } return aprintf(a, "fn(%s) %s", acc, r); } case TY_STRUCT: return t->name ? t->name : "struct{...}"; case TY_NAMED: return t->name ? t->name : ""; case TY_TUPLE: { const char *acc = ""; for (Tparam *p = t->params; p; p = p->next) { const char *pn = type_name(a, p->type); acc = acc[0] ? aprintf(a, "%s, %s", acc, pn) : pn; } return aprintf(a, "(%s)", acc); } case TY_TAGGED: { const char *acc = ""; for (Tparam *p = t->params; p; p = p->next) { const char *pn = type_name(a, p->type); acc = acc[0] ? aprintf(a, "%s | %s", acc, pn) : pn; } return aprintf(a, "(%s)", acc); } case TY_ENUM: return aprintf(a, "enum %s", t->sub ? type_name(a, t->sub) : "i32"); } return "?"; }