commit 553c994eadc52936d2a65f367c522de9646643c8
parent 9a9199ee9d8fac4e82ecd3337d1286b1d83c7a0f
Author: Drew DeVault <sir@cmpwn.com>
Date: Sun, 31 Jan 2021 15:11:05 -0500
check: implement named struct initializers
Diffstat:
3 files changed, 98 insertions(+), 34 deletions(-)
diff --git a/src/check.c b/src/check.c
@@ -1139,15 +1139,27 @@ check_expr_struct(struct context *ctx,
const struct type *hint)
{
trenter(TR_CHECK, "struct");
- assert(!aexpr->_struct.autofill); // TODO
- assert(!aexpr->_struct.type.name); // TODO
expr->type = EXPR_STRUCT;
- struct ast_type stype = {
+ assert(!aexpr->_struct.autofill); // TODO
+
+ const struct type *stype = NULL;
+ if (aexpr->_struct.type.name) {
+ const struct scope_object *obj = scope_lookup(ctx->scope,
+ &aexpr->_struct.type);
+ expect(&aexpr->loc, obj, "Unknown type alias");
+ expect(&aexpr->loc, obj->otype == O_TYPE,
+ "Name does not refer to a type");
+ stype = type_dealias(obj->type);
+ expect(&aexpr->loc, stype->storage == TYPE_STORAGE_STRUCT,
+ "Object named is not a struct type");
+ }
+
+ struct ast_type satype = {
.storage = TYPE_STORAGE_STRUCT,
.flags = TYPE_CONST,
};
- struct ast_struct_union_type *tfield = &stype.struct_union;
+ struct ast_struct_union_type *tfield = &satype.struct_union;
struct ast_struct_union_type **tnext = &tfield->next;
struct expression_struct *sexpr = &expr->_struct;
struct expression_struct **snext = &sexpr->next;
@@ -1156,17 +1168,41 @@ check_expr_struct(struct context *ctx,
while (afield) {
assert(!afield->is_embedded); // TODO
- tfield->member_type = MEMBER_TYPE_FIELD;
- tfield->field.name = afield->field.name;
- tfield->field.type = afield->field.type;
+ const struct type *ftype;
+ if (!stype) {
+ tfield->member_type = MEMBER_TYPE_FIELD;
+ tfield->field.name = afield->field.name;
+ tfield->field.type = afield->field.type;
+ ftype = type_store_lookup_atype(
+ ctx->store, tfield->field.type);
+ } else {
+ expect(&afield->field.initializer->loc, afield->field.name,
+ "Anonymous fields are not permitted for named struct type");
+ // XXX: ^ Is that correct?
+ sexpr->field = type_get_field(stype, afield->field.name);
+ expect(&afield->field.initializer->loc, sexpr->field,
+ "No field by this name exists for this type");
+ ftype = sexpr->field->type;
+ }
+
sexpr->value = xcalloc(1, sizeof(struct expression));
- check_expression(ctx, afield->field.initializer, sexpr->value,
- type_store_lookup_atype(ctx->store, tfield->field.type));
+ check_expression(ctx, afield->field.initializer,
+ sexpr->value, ftype);
+
+ if (stype) {
+ expect(&afield->field.initializer->loc,
+ type_is_assignable(sexpr->field->type, sexpr->value->result),
+ "Initializer is not assignable to struct field");
+ sexpr->value = lower_implicit_cast(
+ sexpr->field->type, sexpr->value);
+ }
if (afield->next) {
- *tnext = tfield = xcalloc(
- 1, sizeof(struct ast_struct_union_type));
- tnext = &tfield->next;
+ if (!stype) {
+ *tnext = tfield = xcalloc(
+ 1, sizeof(struct ast_struct_union_type));
+ tnext = &tfield->next;
+ }
*snext = sexpr = xcalloc(
1, sizeof(struct expression_struct));
snext = &sexpr->next;
@@ -1175,28 +1211,34 @@ check_expr_struct(struct context *ctx,
afield = afield->next;
}
- expr->result = type_store_lookup_atype(ctx->store, &stype);
-
- tfield = &stype.struct_union;
- sexpr = &expr->_struct;
- while (tfield) {
- const struct struct_field *field = type_get_field(
- expr->result, tfield->field.name);
- // TODO: Use more specific error location
- expect(&aexpr->loc, field, "No field by this name exists for this type");
- expect(&aexpr->loc,
- type_is_assignable(field->type, sexpr->value->result),
- "Cannot initialize struct field '%s' from value of this type",
- field->name);
- sexpr->field = field;
- sexpr->value = lower_implicit_cast(field->type, sexpr->value);
-
- struct ast_struct_union_type *next = tfield->next;
- if (tfield != &stype.struct_union) {
- free(tfield);
+ if (stype) {
+ // TODO: Test for exhaustiveness
+ expr->result = stype;
+ } else {
+ expr->result = type_store_lookup_atype(ctx->store, &satype);
+
+ tfield = &satype.struct_union;
+ sexpr = &expr->_struct;
+ while (tfield) {
+ const struct struct_field *field = type_get_field(
+ expr->result, tfield->field.name);
+ // TODO: Use more specific error location
+ expect(&aexpr->loc, field,
+ "No field by this name exists for this type");
+ expect(&aexpr->loc,
+ type_is_assignable(field->type, sexpr->value->result),
+ "Cannot initialize struct field '%s' from value of this type",
+ field->name);
+ sexpr->field = field;
+ sexpr->value = lower_implicit_cast(field->type, sexpr->value);
+
+ struct ast_struct_union_type *next = tfield->next;
+ if (tfield != &satype.struct_union) {
+ free(tfield);
+ }
+ tfield = next;
+ sexpr = sexpr->next;
}
- tfield = next;
- sexpr = sexpr->next;
}
trleave(TR_CHECK, NULL);
diff --git a/src/gen.c b/src/gen.c
@@ -964,6 +964,10 @@ gen_cast_from_tagged(struct gen_context *ctx,
const struct qbe_value *out,
const struct type *to)
{
+ if (type_dealias(to)->storage == TYPE_STORAGE_VOID) {
+ return;
+ }
+
if (type_dealias(to)->storage == TYPE_STORAGE_TAGGED) {
assert(0); // TODO
}
diff --git a/tests/06-structs.ha b/tests/06-structs.ha
@@ -75,16 +75,34 @@ fn nested() void = {
assert(s.y.a.c == 7331);
};
+type coords = struct { x: int, y: int };
+
+fn named() void = {
+ let x = coords { y = 10, x = 20 };
+ assert(x.x == 20 && x.y == 10);
+};
+
fn invariants() void = {
const failures = [
// Assign field from non-assignable type:
"fn test() void = { let x: struct { y: int } = struct { y: int = 10u }; };",
+
+ "
+ type coords = struct { x: int, y: int };
+
+ fn test() void = {
+ let x = coords { x: int = 10u, y: int = 20u };
+ };
+ ",
+
// Dereference non-nullable pointer:
"fn test() void = { let x: nullable *struct { y: int } = null; x.y; };",
// Select field from non-struct object:
"fn test() void = { let x = 10; x.y; };",
// Unknown field:
"fn test() void = { let x: struct { y: int } = struct { y: int = 10 }; x.z; };",
+ // Untyped field for unnamed struct:
+ "fn test() void = { let x = struct { x = 10 }; };",
];
for (let i = 0z; i < len(failures); i += 1z) {
assert(rt::compile(failures[i]) != 0);
@@ -98,9 +116,9 @@ export fn main() void = {
deref();
nested();
invariants();
+ named();
// TODO:
// - Union tests
- // - Named structs
// - Embedded structs
// - offset()
// - Autofilling (...) and its invariants