harec

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 2853a1eb9a2dda5d3afcbcc2202d797bc17d291f
parent 4e8d5e57ee57c83907b996d5a6740f9784818db0
Author: Christopher M. Riedl <cmr@bluescreens.de>
Date:   Sun, 19 Sep 2021 14:46:41 -0500

Fix field offset calculation for unions and structs

Refactor shift_fields() to modify a field in-place to fix two problems
with offset calculations for union/struct fields:

 1. Union field offsets should be zero.
 2. Calculate the correct field offset for nested, embedded fields by
    calling shift_fields() recursively.

Also add two new tests.

Diffstat:
Msrc/type_store.c | 31+++++++++++++++++--------------
Mtests/06-structs.ha | 43+++++++++++++++++++++++++++++++++++++++++++
2 files changed, 60 insertions(+), 14 deletions(-)

diff --git a/src/type_store.c b/src/type_store.c @@ -218,12 +218,12 @@ struct_insert_field(struct type_store *store, struct struct_field **fields, static const struct type *type_store_lookup_type(struct type_store *store, const struct type *type); -static const struct type * -shift_fields(struct type_store *store, const struct type *type, size_t offset) +void +shift_fields(struct type_store *store, struct struct_field *parent) { - if (type->storage == STORAGE_ALIAS - && type_dealias(type)->storage != STORAGE_STRUCT - && type_dealias(type)->storage != STORAGE_UNION) { + if (parent->type->storage == STORAGE_ALIAS + && type_dealias(parent->type)->storage != STORAGE_STRUCT + && type_dealias(parent->type)->storage != STORAGE_UNION) { // TODO struct location loc = { .path = "<unknown>", @@ -232,16 +232,16 @@ shift_fields(struct type_store *store, const struct type *type, size_t offset) }; error(store->check_context, loc, "Cannot embed non-struct non-union alias"); - return &builtin_type_void; + return; } - if (offset == 0) { + if (parent->offset == 0) { // We need to return early here in order to avoid dealiasing an // embedded alias. This is acceptable at nonzero offsets, but we // need to keep the alias if it's at offset 0 because of // subtyping. - return type; + return; } - type = type_dealias(type); + const struct type *type = type_dealias(parent->type); assert(type->storage == STORAGE_STRUCT || type->storage == STORAGE_UNION); struct type new = { @@ -257,13 +257,17 @@ shift_fields(struct type_store *store, const struct type *type, size_t offset) struct struct_field *new = *next = xcalloc(1, sizeof(struct struct_field)); next = &new->next; + new->type = field->type; + new->offset = field->offset; if (field->name) { new->name = strdup(field->name); + new->offset += parent->offset; + } else { + shift_fields(store, new); } - new->type = field->type; - new->offset = field->offset + offset; } - return type_store_lookup_type(store, &new); + + parent->type = type_store_lookup_type(store, &new); } static void @@ -284,8 +288,7 @@ struct_init_from_atype(struct type_store *store, enum type_storage storage, // type_get_field far easier to implement and doesn't // cause any trouble in gen since offsets are only used // there for sorting fields. - field->type = shift_fields(store, field->type, - *size - field->type->size); + shift_fields(store, field); } atype = atype->next; } diff --git a/tests/06-structs.ha b/tests/06-structs.ha @@ -149,6 +149,48 @@ fn invariants() void = { }; }; +fn fields() void = { + let n: u32 = 0; + + let up: *union { + struct { + a: u8, + b: u8, + }, + c: u32, + } = &n: uintptr: *union { + struct { + a: u8, + b: u8, + }, + c: u32, + }; + assert(&up.a: uintptr == &n: uintptr); + assert(&up.b: uintptr == &n: uintptr + 1); + assert(&up.c: uintptr == &n: uintptr); + + let sp: *struct { + a: u8, + struct { + b: u8, + struct { + c: u8, + }, + }, + } = &n: uintptr: *struct { + a: u8, + struct { + b: u8, + struct { + c: u8, + }, + }, + }; + assert(&sp.a: uintptr == &n: uintptr); + assert(&sp.b: uintptr == &n: uintptr + 1); + assert(&sp.c: uintptr == &n: uintptr + 2); +}; + export fn main() void = { padding(); storage(); @@ -158,6 +200,7 @@ export fn main() void = { named(); autofill(); invariants(); + fields(); // TODO: // - Union tests // - Embedded structs