harec

[hare] Hare compiler, written in C11 for POSIX OSs
Log | Files | Refs | README | LICENSE

commit 72f020c5ee0b4e0e2de17f06fcf3ffb1c8c17c46
parent 2106aeb4546a4d143363f789a66d27a0114bba91
Author: Sebastian <sebastian@sebsite.pw>
Date:   Mon, 16 Jan 2023 04:41:14 -0500

eval: default value for tuples

This also now correctly returns EVAL_INVALID when trying to get the
default value of a tagged union (the error message isn't very good,
though).

Signed-off-by: Sebastian <sebastian@sebsite.pw>

Diffstat:
Msrc/eval.c | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mtests/06-structs.ha | 26+++++++++++++-------------
Mtests/21-tuples.ha | 3+++
3 files changed, 71 insertions(+), 27 deletions(-)

diff --git a/src/eval.c b/src/eval.c @@ -403,20 +403,19 @@ eval_const(struct context *ctx, struct expression *in, struct expression *out) if (storage == STORAGE_ENUM) { storage = type_dealias(out->result)->alias.type->storage; } - struct array_constant **next; switch (storage) { case STORAGE_ALIAS: case STORAGE_ENUM: assert(0); // Handled above - case STORAGE_ARRAY: - next = &out->constant.array; + case STORAGE_ARRAY:; + struct array_constant **anext = &out->constant.array; for (struct array_constant *arr = in->constant.array; arr; arr = arr->next) { - struct array_constant *aconst = *next = + struct array_constant *aconst = *anext = xcalloc(sizeof(struct array_constant), 1); aconst->value = xcalloc(sizeof(struct expression), 1); eval_expr(ctx, arr->value, aconst->value); - next = &aconst->next; + anext = &aconst->next; } break; case STORAGE_FUNCTION: @@ -436,8 +435,19 @@ eval_const(struct context *ctx, struct expression *in, struct expression *out) out->constant.tagged.value); case STORAGE_STRUCT: case STORAGE_UNION: - case STORAGE_TUPLE: assert(0); // TODO + case STORAGE_TUPLE:; + struct tuple_constant **tnext = &out->constant.tuple; + for (struct tuple_constant *tuple = in->constant.tuple; tuple; + tuple = tuple->next) { + struct tuple_constant *tconst = *tnext = + xcalloc(1, sizeof(struct tuple_constant)); + tconst->field = tuple->field; + tconst->value = xcalloc(1, sizeof(struct expression)); + eval_expr(ctx, tuple->value, tconst->value); + tnext = &tconst->next; + } + break; case STORAGE_BOOL: case STORAGE_CHAR: case STORAGE_ERROR: @@ -693,7 +703,7 @@ eval_measurement(struct context *ctx, struct expression *in, struct expression * assert(0); } -static void +static enum eval_result constant_default(struct context *ctx, struct expression *v) { struct expression b = {0}; @@ -742,11 +752,26 @@ constant_default(struct context *ctx, struct expression *v) v->constant.array->value->type = EXPR_CONSTANT; v->constant.array->value->result = type_dealias(v->result)->array.members; - constant_default(ctx, v->constant.array->value); + return constant_default(ctx, v->constant.array->value); break; case STORAGE_TAGGED: - case STORAGE_TUPLE: - assert(0); // TODO + return EVAL_INVALID; + case STORAGE_TUPLE:; + struct tuple_constant **c = &v->constant.tuple; + for (const struct type_tuple *t = &type_dealias(v->result)->tuple; + t != NULL; t = t->next) { + *c = xcalloc(1, sizeof(struct tuple_constant)); + (*c)->field = t; + (*c)->value = xcalloc(1, sizeof(struct expression)); + (*c)->value->type = EXPR_CONSTANT; + (*c)->value->result = t->type; + enum eval_result r = constant_default(ctx, (*c)->value); + if (r != EVAL_OK) { + return r; + } + c = &(*c)->next; + } + break; case STORAGE_ALIAS: case STORAGE_FUNCTION: case STORAGE_VALIST: @@ -754,6 +779,8 @@ constant_default(struct context *ctx, struct expression *v) case STORAGE_VOID: break; // no-op } + + return EVAL_OK; } static int @@ -780,14 +807,18 @@ count_struct_fields(const struct type *type) return n; } -static void +static enum eval_result autofill_struct(struct context *ctx, const struct type *type, struct struct_constant **fields) { + enum eval_result r; assert(type->storage == STORAGE_STRUCT || type->storage == STORAGE_UNION); for (const struct struct_field *field = type->struct_union.fields; field; field = field->next) { if (!field->name) { - autofill_struct(ctx, type_dealias(field->type), fields); + r = autofill_struct(ctx, type_dealias(field->type), fields); + if (r != EVAL_OK) { + return r; + } continue; } size_t i = 0; @@ -804,9 +835,16 @@ autofill_struct(struct context *ctx, const struct type *type, struct struct_cons fields[i]->value = xcalloc(1, sizeof(struct expression)); fields[i]->value->type = EXPR_CONSTANT; fields[i]->value->result = field->type; - constant_default(ctx, fields[i]->value); + // TODO: there should probably be a better error message + // when this happens + r = constant_default(ctx, fields[i]->value); + if (r != EVAL_OK) { + return r; + } } } + + return EVAL_OK; } static enum eval_result @@ -839,7 +877,10 @@ eval_struct(struct context *ctx, struct expression *in, struct expression *out) assert(in->_struct.autofill || i == n); if (in->_struct.autofill) { - autofill_struct(ctx, type, fields); + enum eval_result r = autofill_struct(ctx, type, fields); + if (r != EVAL_OK) { + return r; + } } qsort(fields, n, sizeof(struct struct_constant *), field_compar); diff --git a/tests/06-structs.ha b/tests/06-structs.ha @@ -115,7 +115,7 @@ type coords = struct { x: int, y: int }; type coords3 = struct { coords, z: int }; type embed = struct { a: uint, b: u8 }; // complex embedded hierarchy -type me = struct { embed, coords3, f: int }; +type me = struct { embed, coords3, f: int, g: (int, str) }; let g1: me = me { b = 4, x = -1, y = -2, z = -3, f = 20, ... }; let g2: me = me { x = -1, y = -2, z = -3, f = 20, ... }; let g3: me = me { y = -2, z = -3, f = 20, ... }; @@ -132,12 +132,12 @@ fn autofill() void = { let x = coords { x = 10, ... }; assert(x.x == 10 && x.y == 0); - assert(g1.a == 0 && g1.b == 4 && g1.x == -1 && g1.y == -2 && g1.z == -3 && g1.f == 20); - assert(g2.a == 0 && g2.b == 0 && g2.x == -1 && g2.y == -2 && g2.z == -3 && g2.f == 20); - assert(g3.a == 0 && g3.b == 0 && g3.x == 0 && g3.y == -2 && g3.z == -3 && g3.f == 20); - assert(g4.a == 0 && g4.b == 0 && g4.x == 0 && g4.y == 0 && g4.z == -3 && g4.f == 20); - assert(g5.a == 0 && g5.b == 0 && g5.x == 0 && g5.y == 0 && g5.z == 0 && g5.f == 20); - assert(g6.a == 0 && g6.b == 0 && g6.x == 0 && g6.y == 0 && g6.z == 0 && g6.f == 0 ); + assert(g1.a == 0 && g1.b == 4 && g1.x == -1 && g1.y == -2 && g1.z == -3 && g1.f == 20 && g1.g.0 == 0 && g1.g.1 == ""); + assert(g2.a == 0 && g2.b == 0 && g2.x == -1 && g2.y == -2 && g2.z == -3 && g2.f == 20 && g2.g.0 == 0 && g2.g.1 == ""); + assert(g3.a == 0 && g3.b == 0 && g3.x == 0 && g3.y == -2 && g3.z == -3 && g3.f == 20 && g3.g.0 == 0 && g3.g.1 == ""); + assert(g4.a == 0 && g4.b == 0 && g4.x == 0 && g4.y == 0 && g4.z == -3 && g4.f == 20 && g4.g.0 == 0 && g4.g.1 == ""); + assert(g5.a == 0 && g5.b == 0 && g5.x == 0 && g5.y == 0 && g5.z == 0 && g5.f == 20 && g5.g.0 == 0 && g5.g.1 == ""); + assert(g6.a == 0 && g6.b == 0 && g6.x == 0 && g6.y == 0 && g6.z == 0 && g6.f == 0 && g6.g.0 == 0 && g6.g.1 == ""); let l1: me = me { b = 4, x = -1, y = -2, z = -3, f = 20, ... }; let l2: me = me { x = -1, y = -2, z = -3, f = 20, ... }; @@ -146,12 +146,12 @@ fn autofill() void = { let l5: me = me { f = 20, ... }; let l6: me = me { ... }; - assert(l1.a == 0 && l1.b == 4 && l1.x == -1 && l1.y == -2 && l1.z == -3 && l1.f == 20); - assert(l2.a == 0 && l2.b == 0 && l2.x == -1 && l2.y == -2 && l2.z == -3 && l2.f == 20); - assert(l3.a == 0 && l3.b == 0 && l3.x == 0 && l3.y == -2 && l3.z == -3 && l3.f == 20); - assert(l4.a == 0 && l4.b == 0 && l4.x == 0 && l4.y == 0 && l4.z == -3 && l4.f == 20); - assert(l5.a == 0 && l5.b == 0 && l5.x == 0 && l5.y == 0 && l5.z == 0 && l5.f == 20); - assert(l6.a == 0 && l6.b == 0 && l6.x == 0 && l6.y == 0 && l6.z == 0 && l6.f == 0 ); + assert(l1.a == 0 && l1.b == 4 && l1.x == -1 && l1.y == -2 && l1.z == -3 && l1.f == 20 && l1.g.0 == 0 && l1.g.1 == ""); + assert(l2.a == 0 && l2.b == 0 && l2.x == -1 && l2.y == -2 && l2.z == -3 && l2.f == 20 && l2.g.0 == 0 && l2.g.1 == ""); + assert(l3.a == 0 && l3.b == 0 && l3.x == 0 && l3.y == -2 && l3.z == -3 && l3.f == 20 && l3.g.0 == 0 && l3.g.1 == ""); + assert(l4.a == 0 && l4.b == 0 && l4.x == 0 && l4.y == 0 && l4.z == -3 && l4.f == 20 && l4.g.0 == 0 && l4.g.1 == ""); + assert(l5.a == 0 && l5.b == 0 && l5.x == 0 && l5.y == 0 && l5.z == 0 && l5.f == 20 && l5.g.0 == 0 && l5.g.1 == ""); + assert(l6.a == 0 && l6.b == 0 && l6.x == 0 && l6.y == 0 && l6.z == 0 && l6.f == 0 && l6.g.0 == 0 && l6.g.1 == ""); }; fn invariants() void = { diff --git a/tests/21-tuples.ha b/tests/21-tuples.ha @@ -1,5 +1,7 @@ use rt::{compile, exited, EXIT_SUCCESS}; +def CONST: (int, str) = (15, "foo"); + fn storage() void = { let x: (int, size) = (42, 1337); assert(size((int, size)) == size(size) * 2); @@ -20,6 +22,7 @@ fn indexing() void = { fn func(in: (int, size)) (int, size) = (in.0 + 1, in.1 + 1); fn eval_expr_access() void = { static assert((42, 0).0 == 42 && (42, 0).1 == 0); + static assert(CONST.0 == 15 && CONST.1 == "foo"); }; fn eval_expr_tuple() void = {