commit 090dd37eb360d5168131bf47fb078debd9fc94e2
parent 6582a2e34285025529ac057a4c773f7b4aef2b0d
Author: Alexey Yerin <yyp@disroot.org>
Date: Fri, 12 Nov 2021 15:29:51 +0300
check: structs must be either auto-filled or initialized completely
Prior to this, harec didn't care if a struct was half-initialized. Now
it will yield an error in this case so you'll either have to use
'...' or manually add missing fields.
Signed-off-by: Alexey Yerin <yyp@disroot.org>
Diffstat:
2 files changed, 48 insertions(+), 5 deletions(-)
diff --git a/src/check.c b/src/check.c
@@ -374,7 +374,7 @@ check_expr_append(struct context *ctx,
avalue = avalue->next) {
struct append_values *value = *next =
xcalloc(sizeof(struct append_values), 1);
- value->expr =
+ value->expr =
xcalloc(sizeof(struct expression), 1);
check_expression(ctx, avalue->expr, value->expr, memb);
if (!type_is_assignable(memb, value->expr->result)) {
@@ -1771,7 +1771,7 @@ check_expr_insert(struct context *ctx,
avalue = avalue->next) {
struct append_values *value = *next =
xcalloc(sizeof(struct append_values), 1);
- value->expr =
+ value->expr =
xcalloc(sizeof(struct expression), 1);
check_expression(ctx, avalue->expr, value->expr, memb);
if (!type_is_assignable(memb, value->expr->result)) {
@@ -2284,6 +2284,47 @@ check_expr_slice(struct context *ctx,
}
static void
+check_struct_exhaustive(struct context *ctx,
+ const struct ast_expression *aexpr,
+ struct expression *expr,
+ const struct type *stype)
+{
+ stype = type_dealias(stype);
+ if (stype->storage == STORAGE_UNION) {
+ return;
+ }
+ assert(stype->storage == STORAGE_STRUCT);
+ struct struct_field *sf = stype->struct_union.fields;
+ struct ast_field_value *af = aexpr->_struct.fields;
+
+ // XXX: O(n^2)?
+ while (sf) {
+ bool found = false;
+ for (struct ast_field_value *f = af; f;
+ f = f->next) {
+ if (!sf->name) {
+ check_struct_exhaustive(ctx, aexpr, expr,
+ sf->type);
+ found = true;
+ continue;
+ }
+ if (strcmp(f->name, sf->name) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ error(ctx, aexpr->loc, expr,
+ "Field '%s' is uninitialized",
+ sf->name);
+ }
+
+ sf = sf->next;
+ }
+}
+
+static void
check_expr_struct(struct context *ctx,
const struct ast_expression *aexpr,
struct expression *expr,
@@ -2380,7 +2421,9 @@ check_expr_struct(struct context *ctx,
}
if (stype) {
- // TODO: Test for exhaustiveness
+ if (!expr->_struct.autofill) {
+ check_struct_exhaustive(ctx, aexpr, expr, stype);
+ }
expr->result = stype;
} else {
expr->result = type_store_lookup_atype(ctx->store, &satype);
@@ -3792,7 +3835,7 @@ check_internal(struct type_store *ts,
// - Creating a top-level scope for the whole unit, to which
// declarations are added.
// - Creating a scope for each sub-unit, and populating it with imports.
- //
+ //
// Further down the call frame, subsequent functions will create
// sub-scopes for each declaration, expression-list, etc.
ctx.unit = scope_push(&ctx.scope, SCOPE_UNIT);
diff --git a/tests/32-copy.ha b/tests/32-copy.ha
@@ -31,7 +31,7 @@ export fn main() void = {
assert(a.z == b.z);
// unions
- let a = anyint { _16 = 10 };
+ let a = anyint { _16 = 10, ... };
let b = a;
assert(&a != &b);
assert(a._16 == b._16);