Rosie K Languet: 1 hare::types: a few fixes. 6 files changed, 462 insertions(+), 463 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~sircmpwn/hare-dev/patches/46880/mbox | git am -3Learn more about email & git
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.
hare::unit is in need of a complete rewrite tbh, not just implementing TODOs in the existing code. The existing code was written a while ago, back before it was apparent what idiomatic Hare code looks like, so the interface really isn't great, and could be implemented much better from the ground up. The things you've already done in hare::unit are still valuable and I hope will see some use in hare::unit in the future, but for now I'd recommend not doing *too* much in hare::unit. A lot of stuff there is blocked on things like tagged union semantics, for instance. The future of hare::types is uncertain, but for now your patches are definitely welcome. :) I haven't yet done a thorough review of this patch; I'll do that probably either later today or tomorrow, but just wanted to let you know what the current state of things is.
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
builds.sr.ht <builds@sr.ht>hare/patches: SUCCESS in 2m30s [hare::types: a few fixes.][0] from [Rosie K Languet][1] [0]: https://lists.sr.ht/~sircmpwn/hare-dev/patches/46880 [1]: mailto:rkl@rosiesworkshop.net ✓ #1098246 SUCCESS hare/patches/freebsd.yml https://builds.sr.ht/~sircmpwn/job/1098246 ✓ #1098245 SUCCESS hare/patches/alpine.yml https://builds.sr.ht/~sircmpwn/job/1098245