harec

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

commit 7a9448df40e2927eb8ae01b408d5eac57ab1aa15
parent 782d0b67d28bb6883b6d4864bcaa903dfb9d8e33
Author: Eyal Sawady <ecs@d2evs.net>
Date:   Fri,  3 Sep 2021 10:35:06 +0000

Rework embedded structs

The old version had incorrect padding and made struct subtyping
difficult to implement. As a bonus, we get aliased embeds pretty much
for free.

Signed-off-by: Eyal Sawady <ecs@d2evs.net>

Diffstat:
Minclude/ast.h | 17++---------------
Msrc/check.c | 41+++++++++++++++++++----------------------
Msrc/parse.c | 25++++++++++++++-----------
Msrc/type_store.c | 138+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/typedef.c | 5+++--
Msrc/types.c | 16+++++++++++++---
6 files changed, 129 insertions(+), 113 deletions(-)

diff --git a/include/ast.h b/include/ast.h @@ -70,24 +70,11 @@ struct ast_tuple_type { struct ast_tuple_type *next; }; -enum struct_union_member_type { - MEMBER_TYPE_FIELD, - MEMBER_TYPE_EMBEDDED, - MEMBER_TYPE_ALIAS, -}; - struct ast_struct_union_type { - enum struct_union_member_type member_type; struct ast_struct_union_type *next; struct ast_expression *offset; - union { - struct { - char *name; - struct ast_type *type; - } field; - struct ast_type *embedded; - struct identifier alias; - }; + char *name; + struct ast_type *type; }; struct ast_type { diff --git a/src/check.c b/src/check.c @@ -10,6 +10,7 @@ #include "mod.h" #include "scope.h" #include "type_store.h" +#include "typedef.h" #include "types.h" #include "util.h" @@ -38,6 +39,17 @@ expect(const struct location *loc, bool constraint, char *fmt, ...) } } +static char * +gen_typename(const struct type *type) +{ + size_t sz = 0; + char *ptr = NULL; + FILE *f = open_memstream(&ptr, &sz); + emit_type(type, f); + fclose(f); + return ptr; +} + static void handle_errors(struct errors *errors) { @@ -990,7 +1002,7 @@ check_expr_call(struct context *ctx, if (!type_is_assignable(param->type, arg->value->result)) { error(ctx, aarg->value->loc, expr, - "Argument is not assignable to parameter type"); + "Argument type %s is not assignable to parameter type %s", gen_typename(arg->value->result), gen_typename(param->type)); return; } arg->value = lower_implicit_cast(param->type, arg->value); @@ -2241,11 +2253,10 @@ check_expr_struct(struct context *ctx, const struct type *ftype; if (!stype) { - tfield->member_type = MEMBER_TYPE_FIELD; - tfield->field.name = afield->field.name; - tfield->field.type = afield->field.type; + tfield->name = afield->field.name; + tfield->type = afield->field.type; ftype = type_store_lookup_atype( - ctx->store, tfield->field.type); + ctx->store, tfield->type); } else { if (!afield->field.name) { error(ctx, afield->field.initializer->loc, @@ -2302,7 +2313,7 @@ check_expr_struct(struct context *ctx, sexpr = &expr->_struct.fields; while (tfield) { const struct struct_field *field = type_get_field( - expr->result, tfield->field.name); + expr->result, tfield->name); if (!field) { // TODO: Use more specific error location error(ctx, aexpr->loc, expr, @@ -3005,22 +3016,8 @@ type_is_specified(struct context *ctx, const struct ast_type *atype) if (!expr_is_specified(ctx, stype->offset)) { return false; } - switch (stype->member_type) { - case MEMBER_TYPE_FIELD: - if (!type_is_specified(ctx, stype->field.type)) { - return false; - } - break; - case MEMBER_TYPE_EMBEDDED: - if (!type_is_specified(ctx, stype->embedded)) { - return false; - } - break; - case MEMBER_TYPE_ALIAS: - if (!scope_lookup(ctx->scope, &stype->alias)) { - return false; - } - break; + if (!type_is_specified(ctx, stype->type)) { + return false; } } return true; diff --git a/src/parse.c b/src/parse.c @@ -465,17 +465,18 @@ parse_struct_union_type(struct lexer *lexer) switch (lex(lexer, &tok)) { case T_NAME: name = tok.name; - struct identifier *i; + struct location loc = tok.loc; switch (lex(lexer, &tok)) { case T_COLON: - next->member_type = MEMBER_TYPE_FIELD; - next->field.name = name; - next->field.type = parse_type(lexer); + next->name = name; + next->type = parse_type(lexer); break; case T_DOUBLE_COLON: - next->member_type = MEMBER_TYPE_ALIAS; - i = &next->alias; - parse_identifier(lexer, i, false); + next->type = mktype(&loc); + next->type->storage = STORAGE_ALIAS; + next->type->unwrap = false; + parse_identifier(lexer, &next->type->alias, false); + struct identifier *i = &next->type->alias; while (i->ns != NULL) { i = i->ns; } @@ -484,16 +485,18 @@ parse_struct_union_type(struct lexer *lexer) break; default: unlex(lexer, &tok); - next->member_type = MEMBER_TYPE_ALIAS; - next->alias.name = name; + next->type = mktype(&loc); + next->type->storage = STORAGE_ALIAS; + next->type->alias.name = name; + next->type->unwrap = false; break; } break; case T_STRUCT: case T_UNION: - next->member_type = MEMBER_TYPE_EMBEDDED; unlex(lexer, &tok); - next->embedded = parse_struct_union_type(lexer); + next->name = NULL; + next->type = parse_type(lexer); break; default: synassert(false, &tok, T_NAME, T_STRUCT, T_UNION, T_EOF); diff --git a/src/type_store.c b/src/type_store.c @@ -150,36 +150,32 @@ builtin_for_type(const struct type *type) return builtin_type_for_storage(type->storage, is_const); } -static void +static struct struct_field * struct_insert_field(struct type_store *store, struct struct_field **fields, enum type_storage storage, size_t *size, size_t *usize, size_t *align, const struct ast_struct_union_type *atype, bool *ccompat) { - assert(atype->member_type == MEMBER_TYPE_FIELD); - while (fields && *fields && strcmp((*fields)->name, atype->field.name) < 0) { + while (*fields && (!atype->name || !(*fields)->name || strcmp((*fields)->name, atype->name) < 0)) { fields = &(*fields)->next; } - struct struct_field *field, _temp = {0}; - if (fields != NULL) { - field = *fields; - if (field != NULL && strcmp(field->name, atype->field.name) == 0) { - error(store->check_context, atype->field.type->loc, - "Duplicate struct/union member '%s'", atype->field.name); - return; - } - *fields = xcalloc(1, sizeof(struct struct_field)); - (*fields)->next = field; - field = *fields; - } else { - field = &_temp; + struct struct_field *field = *fields; + if (field != NULL && atype->name && field->name && strcmp(field->name, atype->name) == 0) { + error(store->check_context, atype->type->loc, + "Duplicate struct/union member '%s'", atype->name); + return NULL; } + *fields = xcalloc(1, sizeof(struct struct_field)); + (*fields)->next = field; + field = *fields; - field->name = strdup(atype->field.name); - field->type = type_store_lookup_atype(store, atype->field.type); + if (atype->name) { + field->name = strdup(atype->name); + } + field->type = type_store_lookup_atype(store, atype->type); if (field->type->size == 0) { - error(store->check_context, atype->field.type->loc, + error(store->check_context, atype->type->loc, "Struct field size cannot be zero"); - return; + return NULL; } if (atype->offset) { @@ -217,6 +213,57 @@ struct_insert_field(struct type_store *store, struct struct_field **fields, *usize = field->type->size > *usize ? field->type->size : *usize; } *align = field->type->align > *align ? field->type->align : *align; + return field; +} + +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) +{ + if (type->storage == STORAGE_ALIAS + && type_dealias(type)->storage != STORAGE_STRUCT + && type_dealias(type)->storage != STORAGE_UNION) { + // TODO + struct location loc = { + .path = "<unknown>", + .lineno = 0, + .colno = 0, + }; + error(store->check_context, loc, + "Cannot embed non-struct non-union alias"); + return &builtin_type_void; + } + if (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; + } + type = type_dealias(type); + assert(type->storage == STORAGE_STRUCT + || type->storage == STORAGE_UNION); + struct type new = { + .storage = type->storage, + .flags = type->flags, + .size = type->size, + .align = type->align, + .struct_union.c_compat = type->struct_union.c_compat, + }; + struct struct_field **next = &new.struct_union.fields; + for (struct struct_field *field = type->struct_union.fields; field; + field = field->next) { + struct struct_field *new = *next = + xcalloc(1, sizeof(struct struct_field)); + next = &new->next; + if (field->name) { + new->name = strdup(field->name); + } + new->type = field->type; + new->offset = field->offset + offset; + } + return type_store_lookup_type(store, &new); } static void @@ -228,44 +275,17 @@ struct_init_from_atype(struct type_store *store, enum type_storage storage, size_t usize = 0; assert(storage == STORAGE_STRUCT || storage == STORAGE_UNION); while (atype) { - size_t sub = *size; - switch (atype->member_type) { - case MEMBER_TYPE_FIELD: - struct_insert_field(store, fields, storage, - size, &usize, align, atype, ccompat); - break; - case MEMBER_TYPE_EMBEDDED: - if (atype->embedded->storage == STORAGE_UNION) { - *ccompat = false; - // We need to set the offset of all union - // members to the maximum alignment of the union - // members, so first we do a dry run to compute - // it: - size_t offs = 0, align_1 = 0; - struct_init_from_atype(store, STORAGE_UNION, - &offs, &align_1, NULL, - &atype->embedded->struct_union, ccompat); - // Insert padding per the results: - *size += *size % align_1; - // Then insert the fields for real: - sub = *size; - struct_init_from_atype(store, STORAGE_UNION, - &sub, align, fields, - &atype->embedded->struct_union, ccompat); - } else { - struct_init_from_atype(store, STORAGE_STRUCT, - &sub, align, fields, - &atype->embedded->struct_union, ccompat); - } - - if (storage == STORAGE_UNION) { - usize = sub > usize ? sub : usize; - } else { - *size += sub; - } - break; - case MEMBER_TYPE_ALIAS: - assert(0); // TODO + struct struct_field *field = struct_insert_field(store, fields, + storage, size, &usize, align, atype, ccompat); + if (!field->name) { + // We need to shift the embedded struct/union's fields + // so that their offsets are from the start of the + // parent type. This is a bit of a hack, but it makes + // 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); } atype = atype->next; } @@ -476,8 +496,6 @@ tuple_init_from_atype(struct type_store *store, } } -static const struct type *type_store_lookup_type(struct type_store *store, const struct type *type); - static void type_init_from_atype(struct type_store *store, struct type *type, diff --git a/src/typedef.c b/src/typedef.c @@ -176,8 +176,9 @@ emit_struct(const struct type *type, FILE *out) for (size_t i = 0; i < n; ++i) { const struct struct_field *f = fields[i]; if (!type->struct_union.c_compat) { - fprintf(out, "@offset(%zd) %s: ", f->offset, f->name); - } else { + fprintf(out, "@offset(%zd) ", f->offset); + } + if (f->name) { fprintf(out, "%s: ", f->name); } emit_type(f->type, out); diff --git a/src/types.c b/src/types.c @@ -44,8 +44,16 @@ type_get_field(const struct type *type, const char *name) || type->storage == STORAGE_UNION); struct struct_field *field = type->struct_union.fields; while (field) { - if (strcmp(field->name, name) == 0) { - return field; + if (field->name) { + if (strcmp(field->name, name) == 0) { + return field; + } + } else { + const struct struct_field *f = + type_get_field(field->type, name); + if (f != NULL) { + return f; + } } field = field->next; } @@ -415,7 +423,9 @@ type_hash(const struct type *type) case STORAGE_UNION: for (const struct struct_field *field = type->struct_union.fields; field; field = field->next) { - hash = fnv1a_s(hash, field->name); + if (field->name) { + hash = fnv1a_s(hash, field->name); + } hash = fnv1a_u32(hash, type_hash(field->type)); hash = fnv1a_size(hash, field->offset); }