fixes an incorrect and untested builtin_never which previously had its
id copy-pasted from builtin_opaque.
removes redundant and divergent computations for arch-independent
builtins from fromast().
remove a missleading and unreachable branch from dealias().
Signed-off-by: Rosie K Languet <rkl@rosiesworkshop.net>
---
For the last several months I've been casually working on the many
TODOs in hare::{unit,types}. The curious can see my work over at [1],
but it's mostly not ready to send here.
This commit contains some of the very first changes I made as I started
poking arround. Having rebasing it mindlessly I don't know how many
times now, I figure I should just send it before it gets too stale.
I expect this will need many revisions.. Thanks in advance for your
feedback!
[1]: https://git.sr.ht/~roselandgoose/hare
hare/types/+test.ha | 11 +-
hare/types/builtins.ha | 2 +-
hare/types/from_ast.ha | 387 ++++++++++++++++++++++++++++++++++
hare/types/lookup.ha | 24 ---
hare/types/store.ha | 462 +++--------------------------------------
hare/types/types.ha | 39 ++++
6 files changed, 462 insertions(+), 463 deletions(-)
create mode 100644 hare/types/from_ast.ha
delete mode 100644 hare/types/lookup.ha
diff --git a/hare/types/+test.ha b/hare/types/+test.ha
index cf01f9eb..158c2009 100644
--- a/hare/types/+test.ha
+++ b/hare/types/+test.ha
@@ -339,12 +339,11 @@ fn resolve(
let st = store(x86_64, &resolve, null);
defer store_free(st);
- const of = lookup_builtin(st, ast::builtin_type::U64);
- const al = newalias(st, ["myalias"], of);
+ const al = newalias(st, ["myalias"], &builtin_u64);
assert(al.sz == 8);
assert(al._align == 8);
assert(al.flags == 0);
- assert((al.repr as alias).secondary == of);
+ assert((al.repr as alias).secondary == &builtin_u64);
const atype = parse_type("myalias");
defer ast::type_finish(&atype);
@@ -361,12 +360,11 @@ fn resolve(
const htype = lookup(st, &atype)!;
assert((htype.repr as alias).secondary == null);
- const of = lookup_builtin(st, ast::builtin_type::U64);
- const al = newalias(st, ["myalias"], of);
+ const al = newalias(st, ["myalias"], &builtin_u64);
assert(htype.sz == 8);
assert(htype._align == 8);
assert(htype.flags == 0);
- assert((htype.repr as alias).secondary == of);
+ assert((htype.repr as alias).secondary == &builtin_u64);
};
@test fn builtins() void = {
@@ -379,6 +377,7 @@ fn resolve(
(&builtin_i32, "i32"),
(&builtin_i64, "i64"),
(&builtin_opaque, "opaque"),
+ (&builtin_never, "never"),
(&builtin_rune, "rune"),
(&builtin_u8, "u8"),
(&builtin_u16, "u16"),
diff --git a/hare/types/builtins.ha b/hare/types/builtins.ha
index 12ebd04d..93eae261 100644
--- a/hare/types/builtins.ha
+++ b/hare/types/builtins.ha
@@ -72,7 +72,7 @@ export const builtin_opaque: _type = _type {
export const builtin_never: _type = _type {
repr = builtin::NEVER,
sz = SIZE_UNDEFINED, _align = SIZE_UNDEFINED,
- id = 2374983655,
+ id = 1099590421,
...
};
diff --git a/hare/types/from_ast.ha b/hare/types/from_ast.ha
new file mode 100644
index 00000000..aaf7db2f
--- /dev/null
+++ b/hare/types/from_ast.ha
@@ -0,0 +1,387 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use hare::ast;
+use sort;
+use strings;
+
+// constructs a [[_type]] from a *[[ast::_type]].
+fn fromast(store: *typestore, atype: *ast::_type) (_type | deferred | error) = {
+ let sz = SIZE_UNDEFINED, _align = SIZE_UNDEFINED;
+ const f = atype.flags: flag;
+ const repr = match (atype.repr) {
+ case let a: ast::alias_type =>
+ // TODO: This is incomplete
+ assert(!a.unwrap);
+ yield alias {
+ id = ast::ident_dup(a.ident),
+ secondary = null,
+ };
+ case let b: ast::builtin_type =>
+ yield switch (b) {
+ case ast::builtin_type::BOOL => return with_flags(&builtin_bool, f);
+ case ast::builtin_type::F32 => return with_flags(&builtin_f32, f);
+ case ast::builtin_type::F64 => return with_flags(&builtin_f64, f);
+ case ast::builtin_type::I8 => return with_flags(&builtin_i8, f);
+ case ast::builtin_type::I16 => return with_flags(&builtin_i16, f);
+ case ast::builtin_type::I32 => return with_flags(&builtin_i32, f);
+ case ast::builtin_type::I64 => return with_flags(&builtin_i64, f);
+ case ast::builtin_type::NEVER => return with_flags(&builtin_never, f);
+ case ast::builtin_type::OPAQUE => return with_flags(&builtin_opaque, f);
+ case ast::builtin_type::RUNE => return with_flags(&builtin_rune, f);
+ case ast::builtin_type::U8 => return with_flags(&builtin_u8, f);
+ case ast::builtin_type::U16 => return with_flags(&builtin_u16, f);
+ case ast::builtin_type::U32 => return with_flags(&builtin_u32, f);
+ case ast::builtin_type::U64 => return with_flags(&builtin_u64, f);
+ case ast::builtin_type::VOID => return with_flags(&builtin_void, f);
+
+ // TODO: Tuple unpacking could improve this
+
+ case ast::builtin_type::ICONST, ast::builtin_type::FCONST,
+ ast::builtin_type::RCONST =>
+ abort(); // TODO?
+ case ast::builtin_type::INT =>
+ sz = store.arch._int;
+ _align = store.arch._int;
+ yield builtin::INT;
+ case ast::builtin_type::UINT =>
+ sz = store.arch._int;
+ _align = store.arch._int;
+ yield builtin::UINT;
+ case ast::builtin_type::SIZE =>
+ sz = store.arch._size;
+ _align = store.arch._size;
+ yield builtin::SIZE;
+ case ast::builtin_type::UINTPTR =>
+ sz = store.arch._pointer;
+ _align = store.arch._pointer;
+ yield builtin::UINTPTR;
+ case ast::builtin_type::NULL =>
+ sz = store.arch._pointer;
+ _align = store.arch._pointer;
+ yield builtin::NULL;
+ case ast::builtin_type::STR =>
+ sz = store.arch._pointer;
+ sz += sz % store.arch._size + store.arch._size;
+ sz += store.arch._size;
+ _align = if (store.arch._size > store.arch._pointer)
+ store.arch._size
+ else
+ store.arch._pointer;
+ yield builtin::STR;
+ case ast::builtin_type::VALIST =>
+ sz = store.arch.valist_size;
+ _align = store.arch.valist_align;
+ yield builtin::VALIST;
+ };
+ case let f: ast::func_type =>
+ yield func_from_ast(store, &f)?;
+ case let p: ast::pointer_type =>
+ sz = store.arch._pointer;
+ _align = store.arch._pointer;
+ yield pointer {
+ referent = lookup(store, p.referent)?,
+ flags = p.flags: pointer_flag,
+ };
+ case let st: ast::struct_type =>
+ let st = struct_from_ast(store, st, false)?;
+ sz = 0; _align = 0;
+ for (let i = 0z; i < len(st.fields); i += 1) {
+ const field = st.fields[i];
+ if (field.offs + field._type.sz > sz) {
+ sz = field.offs + field._type.sz;
+ };
+ if (field._type._align > _align) {
+ _align = field._type._align;
+ };
+ };
+ yield st;
+ case let un: ast::union_type =>
+ let st = struct_from_ast(store, un, true)?;
+ sz = 0; _align = 0;
+ for (let i = 0z; i < len(st.fields); i += 1) {
+ const field = st.fields[i];
+ if (field.offs + field._type.sz > sz) {
+ sz = field.offs + field._type.sz;
+ };
+ if (field._type._align > _align) {
+ _align = field._type._align;
+ };
+ };
+ yield st;
+ case let ta: ast::tagged_type =>
+ let ta = tagged_from_ast(store, ta)?;
+ const sz_align = tagged_complete(store, ta);
+ sz = sz_align.0; _align = sz_align.1;
+ yield ta;
+ case let tu: ast::tuple_type =>
+ let tu = tuple_from_ast(store, tu)?;
+ sz = 0; _align = 0;
+ for (let i = 0z; i < len(tu); i += 1) {
+ const value = tu[i];
+ if (value.offs + value._type.sz > sz) {
+ sz = value.offs + value._type.sz;
+ };
+ if (value._type._align > _align) {
+ _align = value._type._align;
+ };
+ };
+ yield tu;
+ case let lt: ast::list_type =>
+ let r = list_from_ast(store, <)?;
+ sz = r.0;
+ _align = r.1;
+ yield r.2;
+ case let et: ast::enum_type =>
+ abort(); // TODO
+ };
+ if (sz != SIZE_UNDEFINED && sz != 0 && sz % _align != 0) {
+ sz += _align - (sz - _align) % _align;
+ };
+ return _type {
+ id = 0, // filled in later
+ flags = f,
+ repr = repr,
+ sz = sz,
+ _align = _align,
+ };
+};
+
+fn func_from_ast(
+ store: *typestore,
+ ft: *ast::func_type,
+) (func | deferred | error) = {
+ let f = func {
+ result = lookup(store, ft.result)?,
+ variadism = switch (ft.variadism) {
+ case ast::variadism::NONE =>
+ yield variadism::NONE;
+ case ast::variadism::C =>
+ yield variadism::C;
+ case ast::variadism::HARE =>
+ yield variadism::HARE;
+ },
+ params = alloc([], len(ft.params)),
+ };
+ for (let i = 0z; i < len(ft.params); i += 1) {
+ append(f.params, lookup(store, ft.params[i]._type)?);
+ };
+ return f;
+};
+
+fn list_from_ast(
+ store: *typestore,
+ lt: *ast::list_type
+) ((size, size, (slice | array)) | deferred | error) = {
+ let sz = SIZE_UNDEFINED, _align = SIZE_UNDEFINED;
+ let memb = lookup(store, lt.members)?;
+ let t = match (lt.length) {
+ case ast::len_slice =>
+ sz = store.arch._pointer;
+ if (sz % store.arch._size != 0) {
+ sz += store.arch._size - (sz % store.arch._size);
+ };
+ sz += store.arch._size * 2;
+ _align = if (store.arch._pointer > store.arch._size)
+ store.arch._pointer
+ else store.arch._size;
+ yield memb: slice;
+ case (ast::len_unbounded | ast::len_contextual) =>
+ // Note: contextual length is handled by hare::unit when
+ // initializing bindings. We treat it like unbounded here and
+ // it's fixed up later on.
+ _align = memb._align;
+ yield array {
+ length = SIZE_UNDEFINED,
+ member = memb,
+ };
+ case let ex: *ast::expr =>
+ const resolv = match (store.resolve) {
+ case null =>
+ return noresolver;
+ case let r: *resolver =>
+ yield r;
+ };
+ const length = resolv(store.rstate, store, ex)?;
+ sz = memb.sz * length;
+ assert(sz / length == memb.sz, "overflow");
+ _align = memb._align;
+ yield array {
+ length = length,
+ member = memb,
+ };
+ };
+ return (sz, _align, t);
+};
+
+fn _struct_from_ast(
+ store: *typestore,
+ atype: ast::struct_union_type,
+ is_union: bool,
+ fields: *[]struct_field,
+ offs: *size,
+) (void | deferred | error) = {
+ const nfields = len(fields);
+ const membs = match(atype) {
+ case let atype: ast::struct_type =>
+ yield atype.members;
+ case let atype: ast::union_type =>
+ yield atype: []ast::struct_member;
+ };
+ for (let i = 0z; i < len(membs); i += 1) {
+ *offs = match (membs[i]._offset) {
+ case let ex: *ast::expr =>
+ yield match (store.resolve) {
+ case null =>
+ return noresolver;
+ case let res: *resolver =>
+ yield res(store.rstate, store, ex)?;
+ };
+ case null =>
+ yield *offs;
+ };
+
+ const memb = match (membs[i].member) {
+ case let se: ast::struct_embedded =>
+ let membs: []ast::struct_member = match (se.repr) {
+ case let st: ast::struct_type =>
+ yield st.members;
+ case let ut: ast::union_type =>
+ yield ut;
+ case =>
+ abort(); // Invariant
+ };
+ _struct_from_ast(store, membs,
+ se.repr is ast::union_type,
+ fields, offs)?;
+ continue;
+ case let se: ast::struct_alias =>
+ abort(); // TODO
+ case let sf: ast::struct_field =>
+ yield sf;
+ };
+
+ const _type = lookup(store, memb._type)?;
+ if (*offs % _type._align != 0) {
+ *offs += _type._align - (*offs % _type._align);
+ };
+
+ append(fields, struct_field {
+ name = memb.name,
+ offs = *offs,
+ _type = _type,
+ });
+
+ if (!is_union) {
+ *offs += _type.sz;
+ };
+ };
+
+ if (is_union) {
+ let max = 0z;
+ for (let i = nfields; i < len(fields); i += 1) {
+ if (fields[i].offs + fields[i]._type.sz > max) {
+ max = fields[i].offs + fields[i]._type.sz;
+ };
+ };
+ *offs = max;
+ };
+};
+
+fn struct_from_ast(
+ store: *typestore,
+ atype: ast::struct_union_type,
+ is_union: bool,
+) (_struct | deferred | error) = {
+ let fields: []struct_field = [];
+ let offs = 0z;
+ _struct_from_ast(store, atype, is_union, &fields, &offs)?;
+ sort::sort(fields, size(struct_field), &field_cmp);
+ return _struct {
+ kind = if (is_union) struct_union::UNION else struct_union::STRUCT,
+ fields = fields,
+ };
+};
+
+fn tagged_collect(
+ store: *typestore,
+ atype: ast::tagged_type,
+ types: *[]const *_type,
+) (void | deferred | error) = {
+ for (let i = 0z; i < len(atype); i += 1) match (atype[i].repr) {
+ case let ta: ast::tagged_type =>
+ tagged_collect(store, ta, types)?;
+ case =>
+ append(types, lookup(store, atype[i])?);
+ };
+};
+
+fn tagged_complete(store: *typestore, ta: tagged) (size, size) = {
+ let sz = 0z, _align = 0z;
+ for (let i = 0z; i < len(ta); i += 1) {
+ if (ta[i].sz > sz) {
+ sz = ta[i].sz;
+ };
+ if (ta[i]._align > _align) {
+ _align = ta[i]._align;
+ };
+ };
+ if (store.arch._int > _align) {
+ _align = store.arch._int;
+ };
+ sz += store.arch._int % _align + store.arch._int;
+ return (sz, _align);
+};
+
+fn tagged_cmp(a: const *opaque, b: const *opaque) int = {
+ const a = a: const **_type, b = b: const **_type;
+ return if (a.id < b.id) -1 else if (a.id == b.id) 0 else 1;
+};
+
+fn tagged_from_ast(
+ store: *typestore,
+ atype: ast::tagged_type,
+) (tagged | deferred | error) = {
+ let types: []const *_type = [];
+ //defer! free(types);
+ tagged_collect(store, atype, &types)?;
+ sort::sort(types, size(const *_type), &tagged_cmp);
+ for (let i = 1z; i < len(types); i += 1) {
+ if (types[i].id == types[i - 1].id) {
+ delete(types[i]);
+ i -= 1;
+ };
+ };
+ // TODO: Handle this gracefully
+ assert(len(types) > 1);
+ return types;
+};
+
+fn tuple_from_ast(
+ store: *typestore,
+ membs: ast::tuple_type,
+) (tuple | deferred | error) = {
+ let values: []tuple_value = [];
+ let offs = 0z;
+ for (let i = 0z; i < len(membs); i += 1) {
+ const val = membs[i];
+ const vtype = lookup(store, val)?;
+
+ if (offs % vtype._align != 0) {
+ offs += vtype._align - (offs % vtype._align);
+ };
+
+ append(values, tuple_value {
+ _type = vtype,
+ offs = offs,
+ });
+
+ offs += vtype.sz;
+ };
+ return values;
+};
+
+fn field_cmp(a: const *opaque, b: const *opaque) int = {
+ const a = a: const *struct_field, b = b: *const struct_field;
+ return strings::compare(a.name, b.name);
+};
diff --git a/hare/types/lookup.ha b/hare/types/lookup.ha
deleted file mode 100644
index 6c089a12..00000000
--- a/hare/types/lookup.ha
@@ -1,24 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-// (c) Hare authors <https://harelang.org>
-
-use hare::ast;
-
-// Unwraps a type which may be aliased and returns the underlying type.
-export fn dealias(t: *_type) const *_type = {
- for (true) match (t.repr) {
- case let a: alias =>
- t = a.secondary as const *_type;
- case =>
- break;
- };
- return t;
-};
-
-// Looks up a built-in type.
-export fn lookup_builtin(
- store: *typestore,
- _type: ast::builtin_type,
-) const *_type = lookup(store, &ast::_type {
- repr = _type,
- ...
-})!;
diff --git a/hare/types/store.ha b/hare/types/store.ha
index b61f79c4..4f7647b2 100644
--- a/hare/types/store.ha
+++ b/hare/types/store.ha
@@ -3,8 +3,6 @@
use errors;
use hare::ast;
-use sort;
-use strings;
export def BUCKETS: size = 65535;
@@ -59,7 +57,7 @@ export fn newalias(
ident: const ast::ident,
of: const *_type,
) const *_type = {
- const atype: _type = _type {
+ const atype = _type {
flags = of.flags,
repr = alias {
id = ast::ident_dup(ident),
@@ -76,7 +74,7 @@ export fn newalias(
match (lookup(store, &ast::_type {
repr = ast::alias_type {
unwrap = false,
- ident = ident,
+ ident = ast::ident_dup(ident),
},
...
})) {
@@ -89,11 +87,7 @@ export fn newalias(
*ty = atype;
return ty;
};
-
- // Or create a new alias
- let bucket = &store.map[id % BUCKETS];
- append(bucket, atype);
- return &bucket[len(bucket) - 1];
+ abort("unreachable");
};
// Returned from [[lookup]] when we are unable to resolve this type, but it does
@@ -121,10 +115,18 @@ export fn lookup(
store: *typestore,
ty: *ast::_type,
) (const *_type | deferred | error) = {
- const ty = fromast(store, ty)?;
+ return _lookup(store, fromast(store, ty)?);
+};
+
+fn _lookup(
+ store: *typestore,
+ ty: _type,
+) (const *_type | deferred | error) = {
if (ty.flags == 0) match (ty.repr) {
case let b: builtin =>
switch (b) {
+ case builtin::BOOL =>
+ return &builtin_bool;
case builtin::F32 =>
return &builtin_f32;
case builtin::F64 =>
@@ -137,6 +139,8 @@ export fn lookup(
return &builtin_i32;
case builtin::I64 =>
return &builtin_i64;
+ case builtin::NEVER =>
+ return &builtin_never;
case builtin::OPAQUE =>
return &builtin_opaque;
case builtin::RUNE =>
@@ -160,7 +164,6 @@ export fn lookup(
let bucket = &store.map[id % BUCKETS];
for (let i = 0z; i < len(bucket); i += 1) {
if (bucket[i].id == id) {
- type_finish(&ty);
return &bucket[i];
};
};
@@ -169,427 +172,22 @@ export fn lookup(
return &bucket[len(bucket) - 1];
};
-fn fromast(store: *typestore, atype: *ast::_type) (_type | deferred | error) = {
- let sz = SIZE_UNDEFINED, _align = SIZE_UNDEFINED;
- const repr = match (atype.repr) {
- case let a: ast::alias_type =>
- // TODO: This is incomplete
- assert(!a.unwrap);
- yield alias {
- id = ast::ident_dup(a.ident),
- secondary = null,
- };
- case let b: ast::builtin_type =>
- // TODO: Tuple unpacking could improve this
- yield switch (b) {
- case ast::builtin_type::BOOL =>
- sz = store.arch._int;
- _align = store.arch._int;
- yield builtin::BOOL;
- case ast::builtin_type::F32 =>
- sz = 4; _align = 4;
- yield builtin::F32;
- case ast::builtin_type::F64 =>
- sz = 8; _align = 8;
- yield builtin::F64;
- case ast::builtin_type::I16 =>
- sz = 2; _align = 2;
- yield builtin::I16;
- case ast::builtin_type::I32 =>
- sz = 4; _align = 4;
- yield builtin::I32;
- case ast::builtin_type::I64 =>
- sz = 8; _align = 8;
- yield builtin::I64;
- case ast::builtin_type::I8 =>
- sz = 1; _align = 1;
- yield builtin::I8;
- case ast::builtin_type::INT =>
- sz = store.arch._int;
- _align = store.arch._int;
- yield builtin::INT;
- case ast::builtin_type::RUNE =>
- sz = 4; _align = 4;
- yield builtin::RUNE;
- case ast::builtin_type::SIZE =>
- sz = store.arch._size;
- _align = store.arch._size;
- yield builtin::SIZE;
- case ast::builtin_type::STR =>
- sz = store.arch._pointer;
- sz += sz % store.arch._size + store.arch._size;
- sz += store.arch._size;
- _align = if (store.arch._size > store.arch._pointer)
- store.arch._size
- else
- store.arch._pointer;
- yield builtin::STR;
- case ast::builtin_type::U16 =>
- sz = 2; _align = 2;
- yield builtin::U16;
- case ast::builtin_type::U32 =>
- sz = 4; _align = 4;
- yield builtin::U32;
- case ast::builtin_type::U64 =>
- sz = 8; _align = 8;
- yield builtin::U64;
- case ast::builtin_type::U8 =>
- sz = 1; _align = 1;
- yield builtin::U8;
- case ast::builtin_type::UINT =>
- sz = store.arch._int;
- _align = store.arch._int;
- yield builtin::UINT;
- case ast::builtin_type::UINTPTR =>
- sz = store.arch._pointer;
- _align = store.arch._pointer;
- yield builtin::UINTPTR;
- case ast::builtin_type::VOID =>
- sz = 0; _align = 0;
- yield builtin::VOID;
- case ast::builtin_type::NULL =>
- sz = store.arch._pointer;
- _align = store.arch._pointer;
- yield builtin::NULL;
- case ast::builtin_type::ICONST, ast::builtin_type::FCONST,
- ast::builtin_type::RCONST =>
- abort(); // TODO?
- case ast::builtin_type::OPAQUE =>
- sz = SIZE_UNDEFINED;
- _align = SIZE_UNDEFINED;
- yield builtin::OPAQUE;
- case ast::builtin_type::NEVER =>
- sz = SIZE_UNDEFINED;
- _align = SIZE_UNDEFINED;
- yield builtin::NEVER;
- case ast::builtin_type::VALIST =>
- sz = store.arch.valist_size;
- _align = store.arch.valist_align;
- yield builtin::VALIST;
- };
- case let f: ast::func_type =>
- yield func_from_ast(store, &f)?;
- case let p: ast::pointer_type =>
- sz = store.arch._pointer;
- _align = store.arch._pointer;
- yield pointer {
- referent = lookup(store, p.referent)?,
- flags = p.flags: pointer_flag,
- };
- case let st: ast::struct_type =>
- let st = struct_from_ast(store, st, false)?;
- sz = 0; _align = 0;
- for (let i = 0z; i < len(st.fields); i += 1) {
- const field = st.fields[i];
- if (field.offs + field._type.sz > sz) {
- sz = field.offs + field._type.sz;
- };
- if (field._type._align > _align) {
- _align = field._type._align;
- };
- };
- yield st;
- case let un: ast::union_type =>
- let st = struct_from_ast(store, un, true)?;
- sz = 0; _align = 0;
- for (let i = 0z; i < len(st.fields); i += 1) {
- const field = st.fields[i];
- if (field.offs + field._type.sz > sz) {
- sz = field.offs + field._type.sz;
- };
- if (field._type._align > _align) {
- _align = field._type._align;
- };
- };
- yield st;
- case let ta: ast::tagged_type =>
- let ta = tagged_from_ast(store, ta)?;
- sz = 0; _align = 0;
- for (let i = 0z; i < len(ta); i += 1) {
- if (ta[i].sz > sz) {
- sz = ta[i].sz;
- };
- if (ta[i]._align > _align) {
- _align = ta[i]._align;
- };
- };
- if (store.arch._int > _align) {
- _align = store.arch._int;
- };
- sz += store.arch._int % _align + store.arch._int;
- yield ta;
- case let tu: ast::tuple_type =>
- let tu = tuple_from_ast(store, tu)?;
- sz = 0; _align = 0;
- for (let i = 0z; i < len(tu); i += 1) {
- const value = tu[i];
- if (value.offs + value._type.sz > sz) {
- sz = value.offs + value._type.sz;
- };
- if (value._type._align > _align) {
- _align = value._type._align;
- };
- };
- yield tu;
- case let lt: ast::list_type =>
- let r = list_from_ast(store, <)?;
- sz = r.0;
- _align = r.1;
- yield r.2;
- case let et: ast::enum_type =>
- abort(); // TODO
- };
- if (sz != SIZE_UNDEFINED && sz != 0 && sz % _align != 0) {
- sz += _align - (sz - _align) % _align;
- };
- return _type {
- id = 0, // filled in later
- flags = atype.flags: flag,
- repr = repr,
- sz = sz,
- _align = _align,
- };
-};
-
-fn func_from_ast(
- store: *typestore,
- ft: *ast::func_type,
-) (func | deferred | error) = {
- let f = func {
- result = lookup(store, ft.result)?,
- variadism = switch (ft.variadism) {
- case ast::variadism::NONE =>
- yield variadism::NONE;
- case ast::variadism::C =>
- yield variadism::C;
- case ast::variadism::HARE =>
- yield variadism::HARE;
- },
- params = alloc([], len(ft.params)),
- };
- for (let i = 0z; i < len(ft.params); i += 1) {
- append(f.params, lookup(store, ft.params[i]._type)?);
- };
- return f;
-};
-
-fn list_from_ast(
+// Looks up a built-in type.
+export fn lookup_builtin(
store: *typestore,
- lt: *ast::list_type
-) ((size, size, (slice | array)) | deferred | error) = {
- let sz = SIZE_UNDEFINED, _align = SIZE_UNDEFINED;
- let memb = lookup(store, lt.members)?;
- let t = match (lt.length) {
- case ast::len_slice =>
- sz = store.arch._pointer;
- if (sz % store.arch._size != 0) {
- sz += store.arch._size - (sz % store.arch._size);
- };
- sz += store.arch._size * 2;
- _align = if (store.arch._pointer > store.arch._size)
- store.arch._pointer
- else store.arch._size;
- yield memb: slice;
- case (ast::len_unbounded | ast::len_contextual) =>
- // Note: contextual length is handled by hare::unit when
- // initializing bindings. We treat it like unbounded here and
- // it's fixed up later on.
- _align = memb._align;
- yield array {
- length = SIZE_UNDEFINED,
- member = memb,
- };
- case let ex: *ast::expr =>
- const resolv = match (store.resolve) {
- case null =>
- return noresolver;
- case let r: *resolver =>
- yield r;
- };
- const length = resolv(store.rstate, store, ex)?;
- sz = memb.sz * length;
- assert(sz / length == memb.sz, "overflow");
- _align = memb._align;
- yield array {
- length = length,
- member = memb,
- };
- };
- return (sz, _align, t);
-};
-
-fn _struct_from_ast(
- store: *typestore,
- atype: ast::struct_union_type,
- is_union: bool,
- fields: *[]struct_field,
- offs: *size,
-) (void | deferred | error) = {
- const nfields = len(fields);
- const membs = match(atype) {
- case let atype: ast::struct_type =>
- yield atype.members;
- case let atype: ast::union_type =>
- yield atype: []ast::struct_member;
- };
- for (let i = 0z; i < len(membs); i += 1) {
- *offs = match (membs[i]._offset) {
- case let ex: *ast::expr =>
- yield match (store.resolve) {
- case null =>
- return noresolver;
- case let res: *resolver =>
- yield res(store.rstate, store, ex)?;
- };
- case null =>
- yield *offs;
- };
-
- const memb = match (membs[i].member) {
- case let se: ast::struct_embedded =>
- let membs: []ast::struct_member = match (se.repr) {
- case let st: ast::struct_type =>
- yield st.members;
- case let ut: ast::union_type =>
- yield ut;
- case =>
- abort(); // Invariant
- };
- _struct_from_ast(store, membs,
- se.repr is ast::union_type,
- fields, offs)?;
- continue;
- case let se: ast::struct_alias =>
- abort(); // TODO
- case let sf: ast::struct_field =>
- yield sf;
- };
-
- const _type = lookup(store, memb._type)?;
- if (*offs % _type._align != 0) {
- *offs += _type._align - (*offs % _type._align);
- };
-
- append(fields, struct_field {
- name = memb.name,
- offs = *offs,
- _type = _type,
- });
-
- if (!is_union) {
- *offs += _type.sz;
- };
- };
-
- if (is_union) {
- let max = 0z;
- for (let i = nfields; i < len(fields); i += 1) {
- if (fields[i].offs + fields[i]._type.sz > max) {
- max = fields[i].offs + fields[i]._type.sz;
- };
- };
- *offs = max;
- };
-};
-
-fn struct_from_ast(
- store: *typestore,
- atype: ast::struct_union_type,
- is_union: bool,
-) (_struct | deferred | error) = {
- let fields: []struct_field = [];
- let offs = 0z;
- _struct_from_ast(store, atype, is_union, &fields, &offs)?;
- sort::sort(fields, size(struct_field), &field_cmp);
- return _struct {
- kind = if (is_union) struct_union::UNION else struct_union::STRUCT,
- fields = fields,
- };
-};
-
-fn tagged_collect(
- store: *typestore,
- atype: ast::tagged_type,
- types: *[]const *_type,
-) (void | deferred | error) = {
- for (let i = 0z; i < len(atype); i += 1) match (atype[i].repr) {
- case let ta: ast::tagged_type =>
- tagged_collect(store, ta, types)?;
- case =>
- append(types, lookup(store, atype[i])?);
- };
-};
-
-fn tagged_cmp(a: const *opaque, b: const *opaque) int = {
- const a = a: const **_type, b = b: const **_type;
- return if (a.id < b.id) -1 else if (a.id == b.id) 0 else 1;
-};
-
-fn tagged_from_ast(
- store: *typestore,
- atype: ast::tagged_type,
-) (tagged | deferred | error) = {
- let types: []const *_type = [];
- //defer! free(types);
- tagged_collect(store, atype, &types)?;
- sort::sort(types, size(const *_type), &tagged_cmp);
- for (let i = 1z; i < len(types); i += 1) {
- if (types[i].id == types[i - 1].id) {
- delete(types[i]);
- i -= 1;
- };
- };
- // TODO: Handle this gracefully
- assert(len(types) > 1);
- return types;
-};
-
-fn tuple_from_ast(
- store: *typestore,
- membs: ast::tuple_type,
-) (tuple | deferred | error) = {
- let values: []tuple_value = [];
- let offs = 0z;
- for (let i = 0z; i < len(membs); i += 1) {
- const val = membs[i];
- const vtype = lookup(store, val)?;
-
- if (offs % vtype._align != 0) {
- offs += vtype._align - (offs % vtype._align);
- };
-
- append(values, tuple_value {
- _type = vtype,
- offs = offs,
- });
-
- offs += vtype.sz;
- };
- return values;
-};
-
-fn field_cmp(a: const *opaque, b: const *opaque) int = {
- const a = a: const *struct_field, b = b: *const struct_field;
- return strings::compare(a.name, b.name);
-};
-
-fn type_finish(t: *_type) void = {
- match (t.repr) {
- case let a: alias =>
- ast::ident_free(a.id);
- case array => void;
- case builtin => void;
- case let e: _enum =>
- free(e.values);
- case let f: func =>
- free(f.params);
- case pointer => void;
- case let s: slice => void;
- case let st: _struct =>
- free(st.fields);
- case let tu: tuple =>
- free(tu);
- case let ta: tagged =>
- free(ta);
+ _type: ast::builtin_type,
+) const *_type = lookup(store, &ast::_type {
+ repr = _type,
+ ...
+})!;
+
+// Used to wrap lookup* calls and abort on any [[deferred]] results.
+export fn must(
+ res: (const *_type | deferred | error)
+) (const *_type | error) = {
+ match (res) {
+ case deferred => abort("a lookup* call that musn't error did error");
+ case let ty: const *_type => return ty;
+ case let err: error => return err;
};
};
diff --git a/hare/types/types.ha b/hare/types/types.ha
index c237f804..d1910998 100644
--- a/hare/types/types.ha
+++ b/hare/types/types.ha
@@ -2,6 +2,7 @@
// (c) Hare authors <https://harelang.org>
use hare::ast;
+use sort;
// A type alias.
export type alias = struct {
@@ -120,3 +121,41 @@ export type _type = struct {
sz: size,
_align: size,
};
+
+// Unwraps a type which may be aliased and returns the underlying type.
+export fn dealias(t: *_type) const *_type = {
+ for (true) match (t.repr) {
+ case let a: alias =>
+ t = a.secondary as const *_type;
+ case =>
+ break;
+ };
+ return t;
+};
+
+fn type_finish(t: *_type) void = {
+ match (t.repr) {
+ case let a: alias =>
+ ast::ident_free(a.id);
+ case array => void;
+ case builtin => void;
+ case let e: _enum =>
+ free(e.values);
+ case let f: func =>
+ free(f.params);
+ case pointer => void;
+ case let s: slice => void;
+ case let st: _struct =>
+ free(st.fields);
+ case let tu: tuple =>
+ free(tu);
+ case let ta: tagged =>
+ free(ta);
+ };
+};
+
+fn with_flags(ty: *_type, flags: flag) _type = {
+ let ty = *ty;
+ ty.flags = flags;
+ return ty;
+};
--
2.42.1