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:
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),