hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 082cd4adc1cc382e9258f293c3e5f5ae3a040e96
parent b2383f5c1ff04f361ef36eecd2ce9706dd3e2bbb
Author: Drew DeVault <sir@cmpwn.com>
Date:   Tue,  4 May 2021 12:16:33 -0400

hare::types: initial support for structs

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mhare/types/+test.ha | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Mhare/types/store.ha | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 145 insertions(+), 0 deletions(-)

diff --git a/hare/types/+test.ha b/hare/types/+test.ha @@ -33,3 +33,53 @@ fn parse_type(in: str) ast::_type = { let htype = htype._type as pointer; assert(htype.referent._type as builtin == builtin::INT); }; + +@test fn structs() void = { + let st = store(x86_64, null, null); + defer store_free(st); + + // Basic struct + let atype = parse_type("struct { x: int, y: int }"); + defer ast::type_free(atype); + let htype = lookup(st, &atype)!; + let stype = htype._type as _struct; + assert(stype.kind == struct_union::STRUCT); + assert(len(stype.fields) == 2); + + let x = stype.fields[0]; + assert(x.name == "x"); + assert(x.offs == 0); + assert(x._type._type as builtin == builtin::INT); + + let y = stype.fields[1]; + assert(y.name == "y"); + assert(y.offs == 4); + assert(y._type._type as builtin == builtin::INT); + + // Basic union + // TODO + + // Alignment + // TODO + + // Sort order + // TODO + + // Embedded struct + // TODO + + // Embedded union + // TODO + + // Embedded (struct) alias + // TODO + + // Embedded (union) alias + // TODO + + // Explicit offsets + // TODO + + // C compatibility testing + // TODO +}; diff --git a/hare/types/store.ha b/hare/types/store.ha @@ -190,6 +190,32 @@ fn fromast(store: *typestore, atype: *ast::_type) (_type | deferred | error) = { flags = p.flags: pointer_flags, }; }, + st: ast::struct_type => { + let st = struct_from_ast(store, st, false)?; + 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; + }; + }; + st; + }, + un: ast::union_type => { + let st = struct_from_ast(store, un, true)?; + 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; + }; + }; + st; + }, * => abort(), // TODO }; return _type { @@ -200,6 +226,75 @@ fn fromast(store: *typestore, atype: *ast::_type) (_type | deferred | error) = { }; }; +fn _struct_from_ast( + store: *typestore, + membs: []ast::struct_member, + is_union: bool, + fields: *[]struct_field, + offs: *size, +) (void | deferred | error) = { + for (let i = 0z; i < len(membs); i += 1) { + *offs = match (membs[i]._offset) { + ex: *ast::expr => match (store.resolve) { + null => return noresolver, + // TODO: Why can't I propagate this? + res: *resolver => match (res(store.rstate, store, ex)) { + z: size => z, + deferred => return deferred, + err: errors::opaque => return err, + }, + }, + null => *offs, + }; + + let memb = match (membs[i].member) { + se: ast::struct_embedded => { + let membs: []ast::struct_member = match (se._type) { + st: ast::struct_type => st, + ut: ast::union_type => ut, + * => abort(), // Invariant + }; + _struct_from_ast(store, membs, + se._type is ast::union_type, + fields, offs)?; + continue; + }, + se: ast::struct_alias => abort(), // TODO + sf: ast::struct_field => sf, + }; + + assert(!is_union); // TODO + let _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, + }); + + *offs += _type.sz; + }; +}; + +fn struct_from_ast( + store: *typestore, + membs: []ast::struct_member, + is_union: bool, +) (_struct | deferred | error) = { + let fields: []struct_field = []; + let offs = 0z; + _struct_from_ast(store, membs, is_union, &fields, &offs)?; + // TODO: Sort fields + return _struct { + kind = if (is_union) struct_union::UNION else struct_union::STRUCT, + fields = fields, + c_compat = false, // TODO: Determine C compat properly + }; +}; + fn type_finish(t: *_type) void = { match (t._type) { a: alias => ast::ident_free(a.id),