harec

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

commit ad7125594c29eb679448a537f7f1c359e18c36db
parent 98260e9fdb61d6d4bdc9df615af344c3883c5647
Author: Eyal Sawady <ecs@d2evs.net>
Date:   Sat, 13 Mar 2021 21:09:34 -0500

Implement forward references

Also improve error handling in check.

Diffstat:
Minclude/check.h | 12++++++++++--
Msrc/check.c | 1707++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/type_store.c | 15+++++++++++----
3 files changed, 1226 insertions(+), 508 deletions(-)

diff --git a/include/check.h b/include/check.h @@ -103,9 +103,17 @@ struct scope *check_internal(struct type_store *ts, struct unit *unit, bool scan_only); -void check_expression(struct context *ctx, +struct errors { + struct location loc; + char *msg; + struct errors *next; + struct errors *prev; +}; + +struct errors *check_expression(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint); + const struct type *hint, + struct errors *errors); #endif diff --git a/src/check.c b/src/check.c @@ -40,6 +40,52 @@ expect(const struct location *loc, bool constraint, char *fmt, ...) } } +static void +handle_errors(struct errors *errors) +{ + struct errors *error = errors; + while (error && error->prev) { + error = error->prev; + } + while (error) { + fprintf(stderr, "Error %s:%d:%d: %s\n", error->loc.path, + error->loc.lineno, error->loc.colno, error->msg); + struct errors *next = error->next; + free(error); + error = next; + } + if (errors) { + abort(); + } +} + +static struct errors * +error(const struct location loc, + struct expression *expr, + struct errors *errors, + char *fmt, ...) +{ + expr->type = EXPR_CONSTANT; + expr->result = &builtin_type_void; + expr->terminates = false; + expr->loc = loc; + + va_list ap; + va_start(ap, fmt); + size_t sz = vsnprintf(NULL, 0, fmt, ap); + char *msg = xcalloc(1, sz + 1); + vsnprintf(msg, sz + 1, fmt, ap); + + struct errors *next = xcalloc(1, sizeof(struct errors)); + next->loc = loc; + next->msg = msg; + next->prev = errors; + if (errors) { + errors->next = next; + } + return next; +} + static struct expression * lower_implicit_cast(const struct type *to, struct expression *expr) { @@ -64,16 +110,12 @@ lower_implicit_cast(const struct type *to, struct expression *expr) return cast; } -void check_expression(struct context *ctx, - const struct ast_expression *aexpr, - struct expression *expr, - const struct type *hint); - -static void +static struct errors * check_expr_access(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trace(TR_CHECK, "access"); expr->type = EXPR_ACCESS; @@ -85,7 +127,10 @@ check_expr_access(struct context *ctx, obj = scope_lookup(ctx->scope, &aexpr->access.ident); char buf[1024]; identifier_unparse_static(&aexpr->access.ident, buf, sizeof(buf)); - expect(&aexpr->loc, obj, "Unknown object '%s'", buf); + if (!obj) { + return error(aexpr->loc, expr, errors, + "Unknown object '%s'", buf); + } switch (obj->otype) { case O_CONST: // Lower constants @@ -97,10 +142,11 @@ check_expr_access(struct context *ctx, expr->access.object = obj; break; case O_TYPE: - expect(&aexpr->loc, - type_dealias(obj->type)->storage == STORAGE_VOID, - "Cannot use non-void type alias '%s' as constant", - identifier_unparse(&obj->type->alias.ident)); + if (type_dealias(obj->type)->storage != STORAGE_VOID) { + return error(aexpr->loc, expr, errors, + "Cannot use non-void type alias '%s' as constant", + identifier_unparse(&obj->type->alias.ident)); + } expr->type = EXPR_CONSTANT; expr->result = obj->type; break; @@ -109,23 +155,29 @@ check_expr_access(struct context *ctx, case ACCESS_INDEX: expr->access.array = xcalloc(1, sizeof(struct expression)); expr->access.index = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->access.array, - expr->access.array, NULL); - check_expression(ctx, aexpr->access.index, - expr->access.index, NULL); + errors = check_expression(ctx, aexpr->access.array, + expr->access.array, NULL, errors); + errors = check_expression(ctx, aexpr->access.index, + expr->access.index, NULL, errors); const struct type *atype = type_dereference(expr->access.array->result); - expect(&aexpr->access.array->loc, atype, - "Cannot dereference nullable pointer for indexing"); + if (!atype) { + return error(aexpr->access.array->loc, expr, errors, + "Cannot dereference nullable pointer for indexing"); + } const struct type *itype = type_dealias(expr->access.index->result); - expect(&aexpr->access.array->loc, - atype->storage == STORAGE_ARRAY || atype->storage == STORAGE_SLICE, - "Cannot index non-array, non-slice %s object", - type_storage_unparse(atype->storage)); - expect(&aexpr->access.index->loc, type_is_integer(itype), - "Cannot use non-integer %s type as slice/array index", - type_storage_unparse(itype->storage)); + if (atype->storage != STORAGE_ARRAY + && atype->storage != STORAGE_SLICE) { + return error(aexpr->access.array->loc, expr, errors, + "Cannot index non-array, non-slice %s object", + type_storage_unparse(atype->storage)); + } + if (!type_is_integer(itype)) { + return error(aexpr->access.index->loc, expr, errors, + "Cannot use non-integer %s type as slice/array index", + type_storage_unparse(itype->storage)); + } expr->access.index = lower_implicit_cast( &builtin_type_size, expr->access.index); expr->result = type_store_lookup_with_flags(ctx->store, @@ -133,56 +185,70 @@ check_expr_access(struct context *ctx, break; case ACCESS_FIELD: expr->access._struct = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->access._struct, - expr->access._struct, NULL); + errors = check_expression(ctx, aexpr->access._struct, + expr->access._struct, NULL, errors); const struct type *stype = type_dereference(expr->access._struct->result); - expect(&aexpr->access._struct->loc, stype, - "Cannot dereference nullable pointer for field selection"); - expect(&aexpr->access._struct->loc, - stype->storage == STORAGE_STRUCT || stype->storage == STORAGE_UNION, - "Cannot select field from non-struct, non-union object"); + if (!stype) { + return error(aexpr->access._struct->loc, expr, errors, + "Cannot dereference nullable pointer for field selection"); + } + if (stype->storage != STORAGE_STRUCT + && stype->storage != STORAGE_UNION) { + return error(aexpr->access._struct->loc, expr, errors, + "Cannot select field from non-struct, non-union object"); + } expr->access.field = type_get_field(stype, aexpr->access.field); - expect(&aexpr->access._struct->loc, expr->access.field, - "No such struct field '%s'", aexpr->access.field); + if (!expr->access.field) { + return error(aexpr->access._struct->loc, expr, errors, + "No such struct field '%s'", aexpr->access.field); + } expr->result = expr->access.field->type; break; case ACCESS_TUPLE: expr->access.tuple = xcalloc(1, sizeof(struct expression)); expr->access.value = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->access.tuple, - expr->access.tuple, NULL); - check_expression(ctx, aexpr->access.value, - expr->access.value, NULL); + errors = check_expression(ctx, aexpr->access.tuple, + expr->access.tuple, NULL, errors); + errors = check_expression(ctx, aexpr->access.value, + expr->access.value, NULL, errors); assert(expr->access.value->type == EXPR_CONSTANT); const struct type *ttype = type_dereference(expr->access.tuple->result); - expect(&aexpr->access.tuple->loc, ttype, - "Cannot dereference nullable pointer for value selection"); - expect(&aexpr->access.tuple->loc, - ttype->storage == STORAGE_TUPLE, - "Cannot select value from non-tuple object"); - expect(&aexpr->access.tuple->loc, - type_is_integer(expr->access.value->result), - "Cannot use non-integer constant to select tuple value"); + if (!ttype) { + return error(aexpr->access.tuple->loc, expr, errors, + "Cannot dereference nullable pointer for value selection"); + } + if (ttype->storage != STORAGE_TUPLE) { + return error(aexpr->access.tuple->loc, expr, errors, + "Cannot select value from non-tuple object"); + } + if (!type_is_integer(expr->access.value->result)) { + return error(aexpr->access.tuple->loc, expr, errors, + "Cannot use non-integer constant to select tuple value"); + } expr->access.tvalue = type_get_value(ttype, aexpr->access.value->constant.uval); - expect(&aexpr->access.tuple->loc, expr->access.tvalue, - "No such tuple value '%zu'", - aexpr->access.value->constant.uval); + if (!expr->access.tvalue) { + return error(aexpr->access.tuple->loc, expr, errors, + "No such tuple value '%zu'", + aexpr->access.value->constant.uval); + } expr->result = expr->access.tvalue->type; break; } + return errors; } -static void +static struct errors * check_expr_alloc(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { assert(aexpr->type == EXPR_ALLOC); trace(TR_CHECK, "alloc"); @@ -194,7 +260,8 @@ check_expr_alloc(struct context *ctx, } else if (hint && type_dealias(hint)->storage == STORAGE_SLICE) { inittype = hint; } - check_expression(ctx, aexpr->alloc.expr, expr->alloc.expr, inittype); + errors = check_expression(ctx, aexpr->alloc.expr, expr->alloc.expr, + inittype, errors); inittype = expr->alloc.expr->result; int flags = 0; @@ -230,54 +297,59 @@ check_expr_alloc(struct context *ctx, switch (storage) { case STORAGE_POINTER: if (aexpr->alloc.cap != NULL) { - // We can't just expect(aexpr->alloc.cap != NULL) - // because we want to use aexpr->alloc.cap->loc - expect(&aexpr->alloc.cap->loc, false, - "Allocation with capacity must be of slice type, not %s", - type_storage_unparse(storage)); + return error(aexpr->alloc.cap->loc, expr, errors, + "Allocation with capacity must be of slice type, not %s", + type_storage_unparse(storage)); } break; case STORAGE_SLICE: if (aexpr->alloc.cap != NULL) { expr->alloc.cap = xcalloc(sizeof(struct expression), 1); - check_expression(ctx, aexpr->alloc.cap, expr->alloc.cap, - &builtin_type_size); - expect(&aexpr->alloc.cap->loc, - type_is_assignable(&builtin_type_size, - expr->alloc.cap->result), - "Allocation capacity must be assignable to size"); + errors = check_expression(ctx, aexpr->alloc.cap, + expr->alloc.cap, &builtin_type_size, errors); + if (!type_is_assignable(&builtin_type_size, + expr->alloc.cap->result)) { + return error(aexpr->alloc.cap->loc, expr, errors, + "Allocation capacity must be assignable to size"); + } } break; default: - expect(&aexpr->loc, false, + return error(aexpr->loc, expr, errors, "Allocation type must be pointer or slice, not %s", type_storage_unparse(storage)); } + return errors; } -static void +static struct errors * check_expr_append(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { assert(aexpr->type == EXPR_APPEND); trace(TR_CHECK, "append"); expr->type = EXPR_APPEND; expr->result = &builtin_type_void; expr->append.expr = xcalloc(sizeof(struct expression), 1); - check_expression(ctx, aexpr->append.expr, expr->append.expr, NULL); - expect(&aexpr->append.expr->loc, - type_dealias(expr->append.expr->result)->storage == STORAGE_SLICE, - "append must operate on a slice"); - expect(&aexpr->append.expr->loc, - !(type_dealias(expr->append.expr->result)->flags & TYPE_CONST), - "append must operate on a mutable slice"); - expect(&aexpr->append.expr->loc, - expr->append.expr->type == EXPR_ACCESS - || (expr->append.expr->type == EXPR_UNARITHM - && expr->append.expr->unarithm.op == UN_DEREF), - "append must operate on a slice object"); + errors = check_expression(ctx, aexpr->append.expr, expr->append.expr, + NULL, errors); + if (type_dealias(expr->append.expr->result)->storage != STORAGE_SLICE) { + return error(aexpr->append.expr->loc, expr, errors, + "append must operate on a slice"); + } + if (type_dealias(expr->append.expr->result)->flags & TYPE_CONST) { + return error(aexpr->append.expr->loc, expr, errors, + "append must operate on a mutable slice"); + } + if (expr->append.expr->type != EXPR_ACCESS + && (expr->append.expr->type != EXPR_UNARITHM + || expr->append.expr->unarithm.op != UN_DEREF)) { + return error(aexpr->append.expr->loc, expr, errors, + "append must operate on a slice object"); + } const struct type *memb = type_dealias(expr->append.expr->result)->array.members; struct append_values **next = &expr->append.values; @@ -287,31 +359,36 @@ check_expr_append(struct context *ctx, xcalloc(sizeof(struct append_values), 1); value->expr = xcalloc(sizeof(struct expression), 1); - check_expression(ctx, avalue->expr, value->expr, memb); - expect(&avalue->expr->loc, - type_is_assignable(memb, value->expr->result), - "appended value must be assignable to member type"); + errors = check_expression(ctx, avalue->expr, value->expr, memb, + errors); + if (!type_is_assignable(memb, value->expr->result)) { + return error(avalue->expr->loc, expr, errors, + "appended value must be assignable to member type"); + } value->expr = lower_implicit_cast(memb, value->expr); next = &value->next; } if (aexpr->append.variadic != NULL) { const struct type *type = expr->append.expr->result; expr->append.variadic = xcalloc(sizeof(struct expression), 1); - check_expression(ctx, aexpr->append.variadic, - expr->append.variadic, type); - expect(&aexpr->append.variadic->loc, - type_is_assignable(type, expr->append.variadic->result), - "appended slice must be assignable to slice type"); + errors = check_expression(ctx, aexpr->append.variadic, + expr->append.variadic, type, errors); + if (!type_is_assignable(type, expr->append.variadic->result)) { + return error(aexpr->append.variadic->loc, expr, errors, + "appended slice must be assignable to slice type"); + } expr->append.variadic = lower_implicit_cast(type, expr->append.variadic); } + return errors; } -static void +static struct errors * check_expr_assert(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trace(TR_CHECK, "assert"); expr->type = EXPR_ASSERT; @@ -320,22 +397,24 @@ check_expr_assert(struct context *ctx, if (aexpr->assert.cond != NULL) { expr->assert.cond = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->assert.cond, - expr->assert.cond, &builtin_type_bool); - expect(&aexpr->assert.cond->loc, - expr->assert.cond->result->storage == STORAGE_BOOL, - "Assertion condition must be boolean"); + errors = check_expression(ctx, aexpr->assert.cond, + expr->assert.cond, &builtin_type_bool, errors); + if (expr->assert.cond->result->storage != STORAGE_BOOL) { + return error(aexpr->assert.cond->loc, expr, errors, + "Assertion condition must be boolean"); + } } else { expr->terminates = true; } expr->assert.message = xcalloc(1, sizeof(struct expression)); if (aexpr->assert.message != NULL) { - check_expression(ctx, aexpr->assert.message, - expr->assert.message, &builtin_type_str); - expect(&aexpr->assert.message->loc, - expr->assert.message->result->storage == STORAGE_STRING, - "Assertion message must be string"); + errors = check_expression(ctx, aexpr->assert.message, + expr->assert.message, &builtin_type_str, errors); + if (expr->assert.message->result->storage != STORAGE_STRING) { + return error(aexpr->assert.message->loc, expr, errors, + "Assertion message must be string"); + } } else { int n = snprintf(NULL, 0, "Assertion failed: %s:%d:%d", aexpr->loc.path, aexpr->loc.lineno, aexpr->loc.colno); @@ -355,28 +434,36 @@ check_expr_assert(struct context *ctx, struct expression out = {0}; enum eval_result r = eval_expr(ctx, expr->assert.cond, &out); - expect(&aexpr->assert.cond->loc, r == EVAL_OK, - "Unable to evaluate static assertion at compile time"); + if (r != EVAL_OK) { + return error(aexpr->assert.cond->loc, expr, errors, + "Unable to evaluate static assertion at compile time"); + } assert(out.result->storage == STORAGE_BOOL); cond = out.constant.bval; } else { cond = false; } + // XXX: Should these abort immediately? if (aexpr->assert.message != NULL) { - expect(&aexpr->assert.cond->loc, cond, - "Static assertion failed: %s", - expr->assert.message->constant.string.value); - } else { - expect(&aexpr->loc, cond, "Static assertion failed"); + if (!cond) { + return error(aexpr->assert.cond->loc, expr, errors, + "Static assertion failed: %s", + expr->assert.message->constant.string.value); + } + } else if (!cond) { + return error(aexpr->loc, expr, errors, + "Static assertion failed"); } } + return errors; } -static void +static struct errors * check_expr_assign(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trace(TR_CHECK, "assign"); expr->type = EXPR_ASSIGN; @@ -385,43 +472,53 @@ check_expr_assign(struct context *ctx, struct expression *object = xcalloc(1, sizeof(struct expression)); struct expression *value = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->assign.object, object, NULL); + errors = check_expression(ctx, aexpr->assign.object, object, NULL, + errors); expr->assign.op = aexpr->assign.op; if (aexpr->assign.indirect) { - expect(&aexpr->loc, - object->result->storage == STORAGE_POINTER, - "Cannot dereference non-pointer type for assignment"); - expect(&aexpr->loc, - !(object->result->pointer.flags & PTR_NULLABLE), - "Cannot dereference nullable pointer type"); - check_expression(ctx, aexpr->assign.value, value, - object->result->pointer.referent); - expect(&aexpr->loc, - type_is_assignable(object->result->pointer.referent, - value->result), - "Value type is not assignable to pointer type"); + if (object->result->storage != STORAGE_POINTER) { + return error(aexpr->loc, expr, errors, + "Cannot dereference non-pointer type for assignment"); + } + if (object->result->pointer.flags & PTR_NULLABLE) { + return error(aexpr->loc, expr, errors, + "Cannot dereference nullable pointer type"); + } + errors = check_expression(ctx, aexpr->assign.value, value, + object->result->pointer.referent, errors); + if (!type_is_assignable(object->result->pointer.referent, + value->result)) { + return error(aexpr->loc, expr, errors, + "Value type is not assignable to pointer type"); + } value = lower_implicit_cast(object->result->pointer.referent, value); } else { - check_expression(ctx, aexpr->assign.value, value, object->result); + errors = check_expression(ctx, aexpr->assign.value, value, + object->result, errors); assert(object->type == EXPR_ACCESS || object->type == EXPR_SLICE); // Invariant if (object->type == EXPR_SLICE) { - expect(&aexpr->assign.object->loc, - expr->assign.op == BIN_LEQUAL, - "Slice assignments may not have a binop"); - } - expect(&aexpr->loc, !(object->result->flags & TYPE_CONST), - "Cannot assign to const object"); - expect(&aexpr->loc, - type_is_assignable(object->result, value->result), - "rvalue type is not assignable to lvalue"); + if (expr->assign.op != BIN_LEQUAL) { + return error(aexpr->assign.object->loc, expr, errors, + "Slice assignments may not have a binop"); + } + } + if (object->result->flags & TYPE_CONST) { + return error(aexpr->loc, expr, errors, + "Cannot assign to const object"); + } + if (!type_is_assignable(object->result, value->result)) { + return error(aexpr->loc, expr, errors, + "rvalue type is not assignable to lvalue"); + } value = lower_implicit_cast(object->result, value); } expr->assign.object = object; expr->assign.value = value; + return errors; } static const struct type * @@ -542,11 +639,12 @@ aexpr_is_flexible(const struct ast_expression *expr) && storage_is_flexible(expr->constant.storage); } -static void +static struct errors * check_expr_binarithm(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trace(TR_CHECK, "binarithm"); expr->type = EXPR_BINARITHM; @@ -588,8 +686,10 @@ check_expr_binarithm(struct context *ctx, *rvalue = xcalloc(1, sizeof(struct expression)); if (hint && lflex && rflex) { - check_expression(ctx, aexpr->binarithm.lvalue, lvalue, hint); - check_expression(ctx, aexpr->binarithm.rvalue, rvalue, hint); + errors = check_expression(ctx, aexpr->binarithm.lvalue, lvalue, + hint, errors); + errors = check_expression(ctx, aexpr->binarithm.rvalue, rvalue, + hint, errors); } else if (lflex && rflex) { intmax_t l = aexpr->binarithm.lvalue->constant.ival, r = aexpr->binarithm.rvalue->constant.ival, @@ -620,21 +720,25 @@ check_expr_binarithm(struct context *ctx, } } assert(storage != STORAGE_ICONST); - check_expression(ctx, aexpr->binarithm.lvalue, lvalue, - builtin_type_for_storage(storage, false)); - check_expression(ctx, aexpr->binarithm.rvalue, rvalue, - builtin_type_for_storage(storage, false)); + errors = check_expression(ctx, aexpr->binarithm.lvalue, lvalue, + builtin_type_for_storage(storage, false), errors); + errors = check_expression(ctx, aexpr->binarithm.rvalue, rvalue, + builtin_type_for_storage(storage, false), errors); } else if (!lflex && rflex) { - check_expression(ctx, aexpr->binarithm.lvalue, lvalue, hint); - check_expression(ctx, aexpr->binarithm.rvalue, rvalue, - lvalue->result); + errors = check_expression(ctx, aexpr->binarithm.lvalue, lvalue, + hint, errors); + errors = check_expression(ctx, aexpr->binarithm.rvalue, rvalue, + lvalue->result, errors); } else if (lflex && !rflex) { - check_expression(ctx, aexpr->binarithm.rvalue, rvalue, hint); - check_expression(ctx, aexpr->binarithm.lvalue, lvalue, - rvalue->result); + errors = check_expression(ctx, aexpr->binarithm.rvalue, rvalue, + hint, errors); + errors = check_expression(ctx, aexpr->binarithm.lvalue, lvalue, + rvalue->result, errors); } else { - check_expression(ctx, aexpr->binarithm.lvalue, lvalue, hint); - check_expression(ctx, aexpr->binarithm.rvalue, rvalue, hint); + errors = check_expression(ctx, aexpr->binarithm.lvalue, lvalue, + hint, errors); + errors = check_expression(ctx, aexpr->binarithm.rvalue, rvalue, + hint, errors); } expr->binarithm.lvalue = lvalue; @@ -642,7 +746,10 @@ check_expr_binarithm(struct context *ctx, const struct type *p = type_promote(ctx->store, lvalue->result, rvalue->result); - expect(&aexpr->loc, p != NULL, "Cannot promote lvalue and rvalue"); + if (p == NULL) { + return error(aexpr->loc, expr, errors, + "Cannot promote lvalue and rvalue"); + } lvalue = lower_implicit_cast(p, lvalue); rvalue = lower_implicit_cast(p, rvalue); if (numeric) { @@ -650,13 +757,15 @@ check_expr_binarithm(struct context *ctx, } else { expr->result = &builtin_type_bool; } + return errors; } -static void +static struct errors * check_expr_binding(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trace(TR_CHECK, "binding"); expr->type = EXPR_BINDING; @@ -706,15 +815,19 @@ check_expr_binding(struct context *ctx, } } - check_expression(ctx, abinding->initializer, initializer, type); + errors = check_expression(ctx, abinding->initializer, + initializer, type, errors); if (context) { - expect(&aexpr->loc, - initializer->result->storage == STORAGE_ARRAY, - "Cannot infer array length from non-array type"); - expect(&aexpr->loc, - initializer->result->array.members == type->array.members, - "Initializer is not assignable to binding type"); + if (initializer->result->storage != STORAGE_ARRAY) { + return error(aexpr->loc, expr, errors, + "Cannot infer array length from non-array type"); + } + if (initializer->result->array.members + != type->array.members) { + return error(aexpr->loc, expr, errors, + "Initializer is not assignable to binding type"); + } type = initializer->result; } @@ -733,12 +846,14 @@ check_expr_binding(struct context *ctx, } } - expect(&aexpr->loc, - type->size != 0 && type->size != SIZE_UNDEFINED, - "Cannot create binding for type of zero or undefined size"); - expect(&aexpr->loc, - type_is_assignable(type, initializer->result), - "Initializer is not assignable to binding type"); + if (type->size == 0 || type->size == SIZE_UNDEFINED) { + return error(aexpr->loc, expr, errors, + "Cannot create binding for type of zero or undefined size"); + } + if (!type_is_assignable(type, initializer->result)) { + return error(aexpr->loc, expr, errors, + "Initializer is not assignable to binding type"); + } binding->initializer = lower_implicit_cast(type, initializer); if (abinding->is_static) { @@ -746,8 +861,10 @@ check_expr_binding(struct context *ctx, xcalloc(1, sizeof(struct expression)); enum eval_result r = eval_expr( ctx, binding->initializer, value); - expect(&abinding->initializer->loc, r == EVAL_OK, - "Unable to evaluate static initializer at compile time"); + if (r != EVAL_OK) { + return error(abinding->initializer->loc, expr, errors, + "Unable to evaluate static initializer at compile time"); + } // TODO: Free initializer binding->initializer = value; } @@ -760,14 +877,16 @@ check_expr_binding(struct context *ctx, abinding = abinding->next; } + return errors; } // Lower Hare-style variadic arguments into an array literal -static void +static struct errors * lower_vaargs(struct context *ctx, const struct ast_call_argument *aarg, struct expression *vaargs, - const struct type *type) + const struct type *type, + struct errors *errors) { struct ast_expression val = { .type = EXPR_CONSTANT, @@ -791,10 +910,12 @@ lower_vaargs(struct context *ctx, // XXX: This error handling is minimum-effort and bad const struct type *hint = type_store_lookup_array( ctx->store, type, SIZE_UNDEFINED); - check_expression(ctx, &val, vaargs, hint); + errors = check_expression(ctx, &val, vaargs, hint, errors); assert(vaargs->result->storage == STORAGE_ARRAY); - expect(&val.loc, vaargs->result->array.members == type, - "Argument is not assignable to variadic parameter type"); + if (vaargs->result->array.members != type) { + return error(val.loc, vaargs, errors, + "Argument is not assignable to variadic parameter type"); + } struct ast_array_constant *item = val.constant.array; while (item) { @@ -802,27 +923,33 @@ lower_vaargs(struct context *ctx, free(item); item = next; } + return errors; } -static void +static struct errors * check_expr_call(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trenter(TR_CHECK, "call"); expr->type = EXPR_CALL; struct expression *lvalue = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->call.lvalue, lvalue, NULL); + errors = check_expression(ctx, aexpr->call.lvalue, lvalue, NULL, + errors); expr->call.lvalue = lvalue; const struct type *fntype = type_dereference(lvalue->result); - expect(&aexpr->loc, fntype, - "Cannot dereference nullable pointer type for function call"); - expect(&aexpr->loc, - fntype->storage == STORAGE_FUNCTION, - "Cannot call non-function type"); + if (!fntype) { + return error(aexpr->loc, expr, errors, + "Cannot dereference nullable pointer type for function call"); + } + if (fntype->storage != STORAGE_FUNCTION) { + return error(aexpr->loc, expr, errors, + "Cannot call non-function type"); + } expr->result = fntype->func.result; if (fntype->func.flags & FN_NORETURN) { expr->terminates = true; @@ -838,8 +965,8 @@ check_expr_call(struct context *ctx, if (!param->next && fntype->func.variadism == VARIADISM_HARE && !aarg->variadic) { - lower_vaargs(ctx, aarg, arg->value, - param->type->array.members); + errors = lower_vaargs(ctx, aarg, arg->value, + param->type->array.members, errors); arg->value = lower_implicit_cast(param->type, arg->value); param = NULL; aarg = NULL; @@ -847,11 +974,13 @@ check_expr_call(struct context *ctx, break; } - check_expression(ctx, aarg->value, arg->value, param->type); + errors = check_expression(ctx, aarg->value, arg->value, + param->type, errors); - expect(&aarg->value->loc, - type_is_assignable(param->type, arg->value->result), - "Argument is not assignable to parameter type"); + if (!type_is_assignable(param->type, arg->value->result)) { + return error(aarg->value->loc, expr, errors, + "Argument is not assignable to parameter type"); + } arg->value = lower_implicit_cast(param->type, arg->value); aarg = aarg->next; @@ -864,22 +993,31 @@ check_expr_call(struct context *ctx, // No variadic arguments, lower to empty slice arg = *next = xcalloc(1, sizeof(struct call_argument)); arg->value = xcalloc(1, sizeof(struct expression)); - lower_vaargs(ctx, NULL, arg->value, param->type->array.members); + errors = lower_vaargs(ctx, NULL, arg->value, + param->type->array.members, errors); arg->value = lower_implicit_cast(param->type, arg->value); param = param->next; } - expect(&aexpr->loc, !aarg, "Too many parameters for function call"); - expect(&aexpr->loc, !param, "Not enough parameters for function call"); + if (aarg) { + return error(aexpr->loc, expr, errors, + "Too many parameters for function call"); + } + if (param) { + return error(aexpr->loc, expr, errors, + "Not enough parameters for function call"); + } trleave(TR_CHECK, NULL); + return errors; } -static void +static struct errors * check_expr_cast(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trace(TR_CHECK, "cast"); expr->type = EXPR_CAST; @@ -888,16 +1026,19 @@ check_expr_cast(struct context *ctx, xcalloc(1, sizeof(struct expression)); const struct type *secondary = expr->cast.secondary = type_store_lookup_atype(ctx->store, aexpr->cast.type); - check_expression(ctx, aexpr->cast.value, value, secondary); + errors = check_expression(ctx, aexpr->cast.value, value, secondary, + errors); if (aexpr->cast.kind == C_ASSERTION || aexpr->cast.kind == C_TEST) { const struct type *primary = type_dealias(expr->cast.value->result); - expect(&aexpr->cast.value->loc, - primary->storage == STORAGE_TAGGED, - "Expected a tagged union type"); - expect(&aexpr->cast.type->loc, - type_is_castable(value->result, secondary), - "Invalid cast"); + if (primary->storage != STORAGE_TAGGED) { + return error(aexpr->cast.value->loc, expr, errors, + "Expected a tagged union type"); + } + if (!type_is_castable(value->result, secondary)) { + return error(aexpr->cast.type->loc, expr, errors, + "Invalid cast"); + } bool found = false; for (const struct type_tagged_union *t = &primary->tagged; t; t = t->next) { @@ -906,15 +1047,18 @@ check_expr_cast(struct context *ctx, break; } } - expect(&aexpr->cast.type->loc, found, - "Type is not a valid member of the tagged union type"); + if (!found) { + return error(aexpr->cast.type->loc, expr, errors, + "Type is not a valid member of the tagged union type"); + } } switch (aexpr->cast.kind) { case C_CAST: - expect(&aexpr->cast.type->loc, - type_is_castable(secondary, value->result), - "Invalid cast"); + if (!type_is_castable(secondary, value->result)) { + return error(aexpr->cast.type->loc, expr, errors, + "Invalid cast"); + } // Fallthrough case C_ASSERTION: expr->result = secondary; @@ -923,13 +1067,15 @@ check_expr_cast(struct context *ctx, expr->result = &builtin_type_bool; break; } + return errors; } -static void +static struct errors * check_expr_array(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { size_t len = 0; bool expandable = false; @@ -963,16 +1109,18 @@ check_expr_array(struct context *ctx, while (item) { struct expression *value = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, item->value, value, type); + errors = check_expression(ctx, item->value, value, type, + errors); cur = *next = xcalloc(1, sizeof(struct array_constant)); cur->value = value; if (!type) { type = value->result; } else { - expect(&item->value->loc, - type_is_assignable(type, value->result), - "Array members must be of a uniform type"); + if (!type_is_assignable(type, value->result)) { + return error(item->value->loc, expr, errors, + "Array members must be of a uniform type"); + } cur->value = lower_implicit_cast(type, cur->value); } @@ -988,19 +1136,26 @@ check_expr_array(struct context *ctx, } if (expandable) { - expect(&aexpr->loc, hint != NULL, - "Cannot expand array for inferred type"); - expect(&aexpr->loc, hint->storage == STORAGE_ARRAY - && hint->array.length != SIZE_UNDEFINED - && hint->array.length >= len, - "Cannot expand array into destination type"); + if (hint == NULL) { + return error(aexpr->loc, expr, errors, + "Cannot expand array for inferred type"); + } + if (hint->storage != STORAGE_ARRAY + || hint->array.length == SIZE_UNDEFINED + || hint->array.length < len) { + return error(aexpr->loc, expr, errors, + "Cannot expand array into destination type"); + } expr->result = type_store_lookup_array(ctx->store, type, hint->array.length); } else { - expect(&aexpr->loc, type != NULL, - "Cannot infer array type from context, try casting it to the desired type"); + if (type == NULL) { + return error(aexpr->loc, expr, errors, + "Cannot infer array type from context, try casting it to the desired type"); + } expr->result = type_store_lookup_array(ctx->store, type, len); } + return errors; } static const struct type * @@ -1079,11 +1234,12 @@ lower_constant(const struct type *type, struct expression *expr) return NULL; } -static void +static struct errors * check_expr_constant(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trace(TR_CHECK, "constant"); expr->type = EXPR_CONSTANT; @@ -1095,8 +1251,11 @@ check_expr_constant(struct context *ctx, } expr->constant.ival = aexpr->constant.ival; const struct type *type = lower_constant(hint, expr); - // TODO: This error message is awful - expect(&aexpr->loc, type, "Integer constant out of range"); + if (!type) { + // TODO: This error message is awful + return error(aexpr->loc, expr, errors, + "Integer constant out of range"); + } expr->result = type; } @@ -1128,7 +1287,7 @@ check_expr_constant(struct context *ctx, // No storage break; case STORAGE_ARRAY: - check_expr_array(ctx, aexpr, expr, hint); + errors = check_expr_array(ctx, aexpr, expr, hint, errors); break; case STORAGE_STRING: expr->constant.string.len = aexpr->constant.string.len; @@ -1153,64 +1312,80 @@ check_expr_constant(struct context *ctx, case STORAGE_UNION: assert(0); // Invariant } + return errors; } -static void +static struct errors * check_expr_defer(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { - expect(&aexpr->loc, !ctx->deferring, - "Cannot defer within another defer expression."); + if (ctx->deferring) { + return error(aexpr->loc, expr, errors, + "Cannot defer within another defer expression."); + } expr->type = EXPR_DEFER; expr->result = &builtin_type_void; expr->defer.deferred = xcalloc(1, sizeof(struct expression)); ctx->deferring = true; - check_expression(ctx, aexpr->defer.deferred, expr->defer.deferred, NULL); + errors = check_expression(ctx, aexpr->defer.deferred, + expr->defer.deferred, NULL, errors); ctx->deferring = false; + return errors; } -static void +static struct errors * check_expr_delete(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { expr->type = EXPR_DELETE; expr->result = &builtin_type_void; struct expression *dexpr = expr->delete.expr = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->delete.expr, expr->delete.expr, NULL); + errors = check_expression(ctx, aexpr->delete.expr, expr->delete.expr, + NULL, errors); const struct type *otype = NULL; switch (dexpr->type) { case EXPR_SLICE: otype = dexpr->slice.object->result; break; case EXPR_ACCESS: - expect(&aexpr->delete.expr->loc, dexpr->access.type == ACCESS_INDEX, - "Deleted expression must be slicing or indexing expression"); + if (dexpr->access.type != ACCESS_INDEX) { + return error(aexpr->delete.expr->loc, expr, errors, + "Deleted expression must be slicing or indexing expression"); + } otype = dexpr->access.array->result; break; default: - expect(&aexpr->delete.expr->loc, false, + return error(aexpr->delete.expr->loc, expr, errors, "Deleted expression must be slicing or indexing expression"); } otype = type_dealias(otype); while (otype->storage == STORAGE_POINTER) { otype = type_dealias(otype->pointer.referent); } - expect(&aexpr->delete.expr->loc, otype->storage == STORAGE_SLICE, - "delete must operate on a slice"); - expect(&aexpr->delete.expr->loc, !(otype->flags & TYPE_CONST), - "delete must operate on a mutable slice"); + if (otype->storage != STORAGE_SLICE) { + return error(aexpr->delete.expr->loc, expr, errors, + "delete must operate on a slice"); + } + if (otype->flags & TYPE_CONST) { + return error(aexpr->delete.expr->loc, expr, errors, + "delete must operate on a mutable slice"); + } + return errors; } -static void +static struct errors * check_expr_control(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trenter(TR_CHECK, "control"); expr->type = aexpr->type; @@ -1230,16 +1405,20 @@ check_expr_control(struct context *ctx, break; } } - expect(&aexpr->loc, scope != NULL, "Unknown label %s", - expr->control.label); + if (scope == NULL) { + return error(aexpr->loc, expr, errors, "Unknown label %s", + expr->control.label); + } trleave(TR_CHECK, NULL); + return errors; } -static void +static struct errors * check_expr_for(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trenter(TR_CHECK, "if"); expr->type = EXPR_FOR; @@ -1258,9 +1437,10 @@ check_expr_for(struct context *ctx, if (scope->label == NULL) { continue; } - expect(&aexpr->_for.label_loc, - strcmp(scope->label, expr->_for.label) != 0, - "for loop label must be unique among its ancestors"); + if (strcmp(scope->label, expr->_for.label) == 0){ + return error(aexpr->_for.label_loc, expr, errors, + "for loop label must be unique among its ancestors"); + } } } @@ -1269,56 +1449,65 @@ check_expr_for(struct context *ctx, if (aexpr->_for.bindings) { bindings = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->_for.bindings, bindings, NULL); + errors = check_expression(ctx, aexpr->_for.bindings, bindings, + NULL, errors); expr->_for.bindings = bindings; } cond = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->_for.cond, cond, &builtin_type_bool); + errors = check_expression(ctx, aexpr->_for.cond, cond, + &builtin_type_bool, errors); expr->_for.cond = cond; - expect(&aexpr->_for.cond->loc, - cond->result->storage == STORAGE_BOOL, - "Expected for condition to be boolean"); + if (cond->result->storage != STORAGE_BOOL) { + return error(aexpr->_for.cond->loc, expr, errors, + "Expected for condition to be boolean"); + } if (aexpr->_for.afterthought) { afterthought = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->_for.afterthought, - afterthought, NULL); + errors = check_expression(ctx, aexpr->_for.afterthought, + afterthought, NULL, errors); expr->_for.afterthought = afterthought; } body = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->_for.body, body, NULL); + errors = check_expression(ctx, aexpr->_for.body, body, NULL, errors); expr->_for.body = body; scope_pop(&ctx->scope, TR_CHECK); trleave(TR_CHECK, NULL); + return errors; } -static void +static struct errors * check_expr_free(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { assert(aexpr->type == EXPR_FREE); expr->type = EXPR_FREE; trace(TR_CHECK, "free"); expr->free.expr = xcalloc(sizeof(struct expression), 1); - check_expression(ctx, aexpr->free.expr, expr->free.expr, NULL); + errors = check_expression(ctx, aexpr->free.expr, expr->free.expr, NULL, + errors); enum type_storage storage = type_dealias(expr->free.expr->result)->storage; - expect(&aexpr->free.expr->loc, - storage == STORAGE_SLICE || storage == STORAGE_STRING - || storage == STORAGE_POINTER, - "free must operate on slice, string, or pointer"); + if (storage != STORAGE_SLICE && storage != STORAGE_STRING + && storage != STORAGE_POINTER) { + return error(aexpr->free.expr->loc, expr, errors, + "free must operate on slice, string, or pointer"); + } expr->result = &builtin_type_void; + return errors; } -static void +static struct errors * check_expr_if(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trenter(TR_CHECK, "if"); expr->type = EXPR_IF; @@ -1326,15 +1515,17 @@ check_expr_if(struct context *ctx, struct expression *cond, *true_branch, *false_branch = NULL; cond = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->_if.cond, cond, &builtin_type_bool); + errors = check_expression(ctx, aexpr->_if.cond, cond, + &builtin_type_bool, errors); true_branch = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->_if.true_branch, true_branch, hint); + errors = check_expression(ctx, aexpr->_if.true_branch, true_branch, + hint, errors); if (aexpr->_if.false_branch) { false_branch = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->_if.false_branch, - false_branch, hint); + errors = check_expression(ctx, aexpr->_if.false_branch, + false_branch, hint, errors); if (true_branch->terminates && false_branch->terminates) { expr->terminates = true; @@ -1366,22 +1557,25 @@ check_expr_if(struct context *ctx, expr->terminates = false; } - expect(&aexpr->_if.cond->loc, - cond->result->storage == STORAGE_BOOL, - "Expected if condition to be boolean"); + if (cond->result->storage != STORAGE_BOOL) { + return error(aexpr->_if.cond->loc, expr, errors, + "Expected if condition to be boolean"); + } expr->_if.cond = cond; expr->_if.true_branch = true_branch; expr->_if.false_branch = false_branch; trleave(TR_CHECK, NULL); + return errors; } -static void +static struct errors * check_expr_list(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trenter(TR_CHECK, "expression-list"); expr->type = EXPR_LIST; @@ -1396,7 +1590,8 @@ check_expr_list(struct context *ctx, const struct ast_expression_list *alist = &aexpr->list; while (alist) { struct expression *lexpr = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, alist->expr, lexpr, NULL); + errors = check_expression(ctx, alist->expr, lexpr, NULL, + errors); list->expr = lexpr; alist = alist->next; @@ -1412,27 +1607,30 @@ check_expr_list(struct context *ctx, scope_pop(&ctx->scope, TR_CHECK); trleave(TR_CHECK, NULL); + return errors; } -static void +static struct errors * check_expr_match(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trenter(TR_CHECK, "match"); expr->type = EXPR_MATCH; struct expression *value = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->match.value, value, NULL); + errors = check_expression(ctx, aexpr->match.value, value, NULL, errors); expr->match.value = value; const struct type *type = type_dealias(value->result); bool is_ptr = type->storage == STORAGE_POINTER && type->pointer.flags & PTR_NULLABLE; - expect(&aexpr->match.value->loc, - type->storage == STORAGE_TAGGED || is_ptr, - "match value must be tagged union or nullable pointer type"); + if (type->storage != STORAGE_TAGGED && !is_ptr) { + return error(aexpr->match.value->loc, expr, errors, + "match value must be tagged union or nullable pointer type"); + } struct type_tagged_union result_type = {0}; struct type_tagged_union *tagged = &result_type, @@ -1452,20 +1650,22 @@ check_expr_match(struct context *ctx, case STORAGE_NULL: break; case STORAGE_POINTER: - expect(&acase->type->loc, - type->pointer.referent == ctype->pointer.referent, - "Match case on incompatible pointer type"); + if (type->pointer.referent != ctype->pointer.referent) { + return error(acase->type->loc, expr, errors, + "Match case on incompatible pointer type"); + } break; default: - expect(&acase->type->loc, false, + return error(acase->type->loc, expr, errors, "Invalid type for match case (expected null or pointer type)"); - break; } } else { // TODO: Assign a score to tagged compatibility // and choose the branch with the highest score. - expect(&acase->type->loc, type_is_assignable(type, ctype), - "Invalid type for match case (match is not assignable to this type)"); + if (!type_is_assignable(type, ctype)) { + return error(acase->type->loc, expr, errors, + "Invalid type for match case (match is not assignable to this type)"); + } } } @@ -1482,7 +1682,8 @@ check_expr_match(struct context *ctx, _case->value = xcalloc(1, sizeof(struct expression)); _case->type = ctype; - check_expression(ctx, acase->value, _case->value, hint); + errors = check_expression(ctx, acase->value, _case->value, + hint, errors); if (acase->name) { scope_pop(&ctx->scope, TR_CHECK); @@ -1519,10 +1720,11 @@ check_expr_match(struct context *ctx, struct match_case *_case = expr->match.cases; struct ast_match_case *acase = aexpr->match.cases; while (_case) { - expect(&acase->value->loc, - _case->value->terminates || - type_is_assignable(expr->result, _case->value->result), - "Match case is not assignable to result type"); + if (!_case->value->terminates && !type_is_assignable( + expr->result, _case->value->result)) { + return error(acase->value->loc, expr, errors, + "Match case is not assignable to result type"); + } _case->value = lower_implicit_cast( expr->result, _case->value); _case = _case->next; @@ -1538,13 +1740,15 @@ check_expr_match(struct context *ctx, } trleave(TR_CHECK, NULL); + return errors; } -static void +static struct errors * check_expr_measure(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trenter(TR_CHECK, "measure"); expr->type = EXPR_MEASURE; @@ -1554,18 +1758,19 @@ check_expr_measure(struct context *ctx, switch (expr->measure.op) { case M_LEN: expr->measure.value = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->measure.value, - expr->measure.value, NULL); + errors = check_expression(ctx, aexpr->measure.value, + expr->measure.value, NULL, errors); enum type_storage vstor = type_dealias(expr->measure.value->result)->storage; - expect(&aexpr->measure.value->loc, - vstor == STORAGE_ARRAY - || vstor == STORAGE_SLICE - || vstor == STORAGE_STRING, - "len argument must be of an array, slice, or str type"); - expect(&aexpr->measure.value->loc, - expr->measure.value->result->size != SIZE_UNDEFINED, - "Cannot take length of array type with undefined length"); + if (vstor != STORAGE_ARRAY && vstor != STORAGE_SLICE + && vstor != STORAGE_STRING) { + return error(aexpr->measure.value->loc, expr, errors, + "len argument must be of an array, slice, or str type"); + } + if (expr->measure.value->result->size == SIZE_UNDEFINED) { + return error(aexpr->measure.value->loc, expr, errors, + "Cannot take length of array type with undefined length"); + } break; case M_SIZE: expr->measure.type = type_store_lookup_atype( @@ -1574,26 +1779,34 @@ check_expr_measure(struct context *ctx, case M_OFFSET: assert(0); // TODO } + return errors; } -static void +static struct errors * check_expr_propagate(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trenter(TR_CHECK, "propagate"); struct expression *lvalue = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->propagate.value, lvalue, hint); + errors = check_expression(ctx, aexpr->propagate.value, lvalue, hint, + errors); const struct type *intype = lvalue->result; - expect(&aexpr->loc, - type_dealias(intype)->storage == STORAGE_TAGGED, - "Cannot use error propagation with non-tagged type"); - expect(&aexpr->loc, !ctx->deferring, - "Cannot use error propagation in a defer expression"); - expect(&aexpr->loc, !(ctx->fntype->func.flags & FN_NORETURN), - "Cannot use error propagation inside @noreturn function"); + if (type_dealias(intype)->storage != STORAGE_TAGGED) { + return error(aexpr->loc, expr, errors, + "Cannot use error propagation with non-tagged type"); + } + if (ctx->deferring) { + return error(aexpr->loc, expr, errors, + "Cannot use error propagation in a defer expression"); + } + if (ctx->fntype->func.flags & FN_NORETURN) { + return error(aexpr->loc, expr, errors, + "Cannot use error propagation inside @noreturn function"); + } struct type_tagged_union result_tagged = {0}; struct type_tagged_union *tagged = &result_tagged, @@ -1626,8 +1839,10 @@ check_expr_propagate(struct context *ctx, } } - expect(&aexpr->loc, return_tagged.type, - "No error can occur here, cannot propagate"); + if (!return_tagged.type) { + return error(aexpr->loc, expr, errors, + "No error can occur here, cannot propagate"); + } const struct type *return_type; if (return_tagged.next) { @@ -1647,9 +1862,10 @@ check_expr_propagate(struct context *ctx, result_type = result_tagged.type; } - expect(&aexpr->loc, - type_is_assignable(ctx->fntype->func.result, return_type), - "Error type is not assignable to function result type"); + if (!type_is_assignable(ctx->fntype->func.result, return_type)) { + return error(aexpr->loc, expr, errors, + "Error type is not assignable to function result type"); + } // Lower to a match expression expr->type = EXPR_MATCH; @@ -1703,19 +1919,25 @@ check_expr_propagate(struct context *ctx, scope_pop(&ctx->scope, TR_CHECK); expr->result = result_type; + return errors; } -static void +static struct errors * check_expr_return(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trenter(TR_CHECK, "return"); - expect(&aexpr->loc, !ctx->deferring, - "Cannot return inside a defer expression"); - expect(&aexpr->loc, !(ctx->fntype->func.flags & FN_NORETURN), - "Cannot return inside @noreturn function"); + if (ctx->deferring) { + return error(aexpr->loc, expr, errors, + "Cannot return inside a defer expression"); + } + if (ctx->fntype->func.flags & FN_NORETURN) { + return error(aexpr->loc, expr, errors, + "Cannot return inside @noreturn function"); + } expr->type = EXPR_RETURN; expr->result = &builtin_type_void; @@ -1723,16 +1945,17 @@ check_expr_return(struct context *ctx, struct expression *rval = xcalloc(1, sizeof(struct expression)); if (aexpr->_return.value) { - check_expression(ctx, aexpr->_return.value, - rval, ctx->fntype->func.result); + errors = check_expression(ctx, aexpr->_return.value, + rval, ctx->fntype->func.result, errors); } else { rval->type = EXPR_CONSTANT; rval->result = &builtin_type_void; } - expect(&aexpr->loc, - type_is_assignable(ctx->fntype->func.result, rval->result), - "Return value is not assignable to function result type"); + if (!type_is_assignable(ctx->fntype->func.result, rval->result)) { + return error(aexpr->loc, expr, errors, + "Return value is not assignable to function result type"); + } if (ctx->fntype->func.result != rval->result) { rval = lower_implicit_cast( ctx->fntype->func.result, rval); @@ -1740,47 +1963,59 @@ check_expr_return(struct context *ctx, expr->_return.value = rval; trleave(TR_CHECK, NULL); + return errors; } -static void +static struct errors * check_expr_slice(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trenter(TR_CHECK, "slice"); expr->type = EXPR_SLICE; expr->slice.object = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->slice.object, expr->slice.object, NULL); + errors = check_expression(ctx, aexpr->slice.object, expr->slice.object, + NULL, errors); const struct type *atype = type_dereference(expr->slice.object->result); - expect(&aexpr->slice.object->loc, atype, - "Cannot dereference nullable pointer for slicing"); - expect(&aexpr->slice.object->loc, - atype->storage == STORAGE_SLICE - || atype->storage == STORAGE_ARRAY, - "Cannot slice non-array, non-slice object"); + if (!atype) { + return error(aexpr->slice.object->loc, expr, errors, + "Cannot dereference nullable pointer for slicing"); + } + if (atype->storage != STORAGE_SLICE + && atype->storage != STORAGE_ARRAY) { + return error(aexpr->slice.object->loc, expr, errors, + "Cannot slice non-array, non-slice object"); + } const struct type *itype; if (aexpr->slice.start) { expr->slice.start = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->slice.start, expr->slice.start, NULL); + errors = check_expression(ctx, aexpr->slice.start, + expr->slice.start, NULL, errors); itype = type_dealias(expr->slice.start->result); - expect(&aexpr->slice.start->loc, type_is_integer(itype), - "Cannot use non-integer %s type as slicing operand", - type_storage_unparse(itype->storage)); + if (!type_is_integer(itype)) { + return error(aexpr->slice.start->loc, expr, errors, + "Cannot use non-integer %s type as slicing operand", + type_storage_unparse(itype->storage)); + } expr->slice.start = lower_implicit_cast( &builtin_type_size, expr->slice.start); } if (aexpr->slice.end) { expr->slice.end = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->slice.end, expr->slice.end, NULL); + errors = check_expression(ctx, aexpr->slice.end, + expr->slice.end, NULL, errors); itype = type_dealias(expr->slice.end->result); - expect(&aexpr->slice.end->loc, type_is_integer(itype), - "Cannot use non-integer %s type as slicing operand", - type_storage_unparse(itype->storage)); + if (!type_is_integer(itype)) { + return error(aexpr->slice.end->loc, expr, errors, + "Cannot use non-integer %s type as slicing operand", + type_storage_unparse(itype->storage)); + } expr->slice.end = lower_implicit_cast( &builtin_type_size, expr->slice.end); } else { @@ -1790,13 +2025,15 @@ check_expr_slice(struct context *ctx, expr->result = type_store_lookup_slice(ctx->store, atype->array.members); trleave(TR_CHECK, NULL); + return errors; } -static void +static struct errors * check_expr_struct(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trenter(TR_CHECK, "struct"); expr->type = EXPR_STRUCT; @@ -1805,13 +2042,19 @@ check_expr_struct(struct context *ctx, 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"); + if (!obj) { + return error(aexpr->loc, expr, errors, + "Unknown type alias"); + } + if (obj->otype != O_TYPE) { + return error(aexpr->loc, expr, errors, + "Name does not refer to a type"); + } stype = obj->type; - expect(&aexpr->loc, - type_dealias(stype)->storage == STORAGE_STRUCT, - "Object named is not a struct type"); + if (type_dealias(stype)->storage != STORAGE_STRUCT) { + return error(aexpr->loc, expr, errors, + "Object named is not a struct type"); + } } struct ast_type satype = { @@ -1823,8 +2066,10 @@ check_expr_struct(struct context *ctx, struct expr_struct_field *sexpr = &expr->_struct.fields; struct expr_struct_field **snext = &sexpr->next; expr->_struct.autofill = aexpr->_struct.autofill; - expect(&aexpr->loc, stype != NULL || !expr->_struct.autofill, - "Autofill is only permitted for named struct initializers"); + if (stype == NULL && expr->_struct.autofill) { + return error(aexpr->loc, expr, errors, + "Autofill is only permitted for named struct initializers"); + } struct ast_field_value *afield = aexpr->_struct.fields; while (afield) { @@ -1838,24 +2083,32 @@ check_expr_struct(struct context *ctx, 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? + if (!afield->field.name) { + return error(afield->field.initializer->loc, + expr, errors, + "Anonymous fields are not permitted for named struct type"); + // XXX: ^ Is that correct? + } sexpr->field = type_get_field(type_dealias(stype), afield->field.name); - expect(&afield->field.initializer->loc, sexpr->field, - "No field by this name exists for this type"); + if (!sexpr->field) { + return error(afield->field.initializer->loc, + expr, errors, + "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, ftype); + errors = check_expression(ctx, afield->field.initializer, + sexpr->value, ftype, errors); if (stype) { - expect(&afield->field.initializer->loc, - type_is_assignable(sexpr->field->type, sexpr->value->result), - "Initializer is not assignable to struct field"); + if (!type_is_assignable(sexpr->field->type, sexpr->value->result)) { + return error(afield->field.initializer->loc, + expr, errors, + "Initializer is not assignable to struct field"); + } sexpr->value = lower_implicit_cast( sexpr->field->type, sexpr->value); } @@ -1884,13 +2137,16 @@ check_expr_struct(struct context *ctx, 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); + if (!field) { + // TODO: Use more specific error location + return error(aexpr->loc, expr, errors, + "No field by this name exists for this type"); + } + if (!type_is_assignable(field->type, sexpr->value->result)) { + return error(aexpr->loc, expr, errors, + "Cannot initialize struct field '%s' from value of this type", + field->name); + } sexpr->field = field; sexpr->value = lower_implicit_cast(field->type, sexpr->value); @@ -1904,19 +2160,22 @@ check_expr_struct(struct context *ctx, } trleave(TR_CHECK, NULL); + return errors; } -static void +static struct errors * check_expr_switch(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trenter(TR_CHECK, "switch"); expr->type = EXPR_SWITCH; struct expression *value = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->_switch.value, value, NULL); + errors = check_expression(ctx, aexpr->_switch.value, value, NULL, + errors); const struct type *type = type_dealias(value->result); expr->_switch.value = value; @@ -1940,24 +2199,27 @@ check_expr_switch(struct context *ctx, struct expression *evaled = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aopt->value, value, type); - expect(&aopt->value->loc, - type_is_assignable( - type_dealias(type), - type_dealias(value->result)), - "Invalid type for switch case"); + errors = check_expression(ctx, aopt->value, value, type, + errors); + if (!type_is_assignable(type_dealias(type), + type_dealias(value->result))) { + return error(aopt->value->loc, expr, errors, + "Invalid type for switch case"); + } enum eval_result r = eval_expr(ctx, value, evaled); - expect(&aopt->value->loc, - r == EVAL_OK, - "Unable to evaluate case at compile time"); + if (r != EVAL_OK) { + return error(aopt->value->loc, expr, errors, + "Unable to evaluate case at compile time"); + } opt->value = evaled; next_opt = &opt->next; } _case->value = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, acase->value, _case->value, hint); + errors = check_expression(ctx, acase->value, _case->value, hint, + errors); if (_case->value->terminates) { continue; } @@ -1989,10 +2251,11 @@ check_expr_switch(struct context *ctx, struct switch_case *_case = expr->_switch.cases; struct ast_switch_case *acase = aexpr->_switch.cases; while (_case) { - expect(&acase->value->loc, - _case->value->terminates || - type_is_assignable(expr->result, _case->value->result), - "Switch case is not assignable to result type"); + if (!_case->value->terminates && !type_is_assignable( + expr->result, _case->value->result)) { + return error(acase->value->loc, expr, errors, + "Switch case is not assignable to result type"); + } _case->value = lower_implicit_cast( expr->result, _case->value); _case = _case->next; @@ -2006,13 +2269,15 @@ check_expr_switch(struct context *ctx, tu = next; } } + return errors; } -static void +static struct errors * check_expr_tuple(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trenter(TR_CHECK, "tuple"); expr->type = EXPR_TUPLE; @@ -2029,8 +2294,8 @@ check_expr_tuple(struct context *ctx, for (const struct ast_expression_tuple *atuple = &aexpr->tuple; atuple; atuple = atuple->next) { tuple->value = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, atuple->expr, tuple->value, - ttuple ? ttuple->type : NULL); + errors = check_expression(ctx, atuple->expr, tuple->value, + ttuple ? ttuple->type : NULL, errors); rtype->type = tuple->value->result; if (atuple->next) { @@ -2082,11 +2347,14 @@ check_expr_tuple(struct context *ctx, struct expression_tuple *etuple = &expr->tuple; const struct ast_expression_tuple *atuple = &aexpr->tuple; while (etuple) { - expect(&atuple->expr->loc, ttuple, - "Too many values for tuple type"); - expect(&atuple->expr->loc, - type_is_assignable(ttuple->type, etuple->value->result), - "Value is not assignable to tuple value type"); + if (!ttuple) { + return error(atuple->expr->loc, expr, errors, + "Too many values for tuple type"); + } + if (!type_is_assignable(ttuple->type, etuple->value->result)) { + return error(atuple->expr->loc, expr, errors, + "Value is not assignable to tuple value type"); + } etuple->value = lower_implicit_cast(ttuple->type, etuple->value); etuple = etuple->next; atuple = atuple->next; @@ -2094,46 +2362,54 @@ check_expr_tuple(struct context *ctx, } trleave(TR_CHECK, NULL); + return errors; } -static void +static struct errors * check_expr_unarithm(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trenter(TR_CHECK, "unarithm"); expr->type = EXPR_UNARITHM; struct expression *operand = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, aexpr->unarithm.operand, operand, NULL); + errors = check_expression(ctx, aexpr->unarithm.operand, operand, NULL, + errors); expr->unarithm.operand = operand; expr->unarithm.op = aexpr->unarithm.op; switch (expr->unarithm.op) { case UN_LNOT: - expect(&aexpr->unarithm.operand->loc, - operand->result->storage == STORAGE_BOOL, - "Cannot perform logical NOT (!) on non-boolean type"); + if (operand->result->storage != STORAGE_BOOL) { + return error(aexpr->unarithm.operand->loc, expr, errors, + "Cannot perform logical NOT (!) on non-boolean type"); + } expr->result = &builtin_type_bool; break; case UN_BNOT: - expect(&aexpr->unarithm.operand->loc, - type_is_integer(operand->result), - "Cannot perform binary NOT (~) on non-integer type"); - expect(&aexpr->unarithm.operand->loc, - !type_is_signed(operand->result), - "Cannot perform binary NOT (~) on signed type"); + if (!type_is_integer(operand->result)) { + return error(aexpr->unarithm.operand->loc, expr, errors, + "Cannot perform binary NOT (~) on non-integer type"); + } + if (type_is_signed(operand->result)) { + return error(aexpr->unarithm.operand->loc, expr, errors, + "Cannot perform binary NOT (~) on signed type"); + } expr->result = operand->result; break; case UN_MINUS: case UN_PLUS: - expect(&aexpr->unarithm.operand->loc, - type_is_numeric(operand->result), - "Cannot perform operation on non-numeric type"); - expect(&aexpr->unarithm.operand->loc, - type_is_signed(operand->result), - "Cannot perform operation on unsigned type"); + if (!type_is_numeric(operand->result)) { + return error(aexpr->unarithm.operand->loc, expr, errors, + "Cannot perform operation on non-numeric type"); + } + if (!type_is_signed(operand->result)) { + return error(aexpr->unarithm.operand->loc, expr, errors, + "Cannot perform operation on unsigned type"); + } expr->result = operand->result; break; case UN_ADDRESS: @@ -2141,112 +2417,117 @@ check_expr_unarithm(struct context *ctx, ctx->store, operand->result, 0); break; case UN_DEREF: - expect(&aexpr->unarithm.operand->loc, - operand->result->storage == STORAGE_POINTER, - "Cannot de-reference non-pointer type"); - expect(&aexpr->unarithm.operand->loc, - !(operand->result->pointer.flags & PTR_NULLABLE), - "Cannot dereference nullable pointer type"); + if (operand->result->storage != STORAGE_POINTER) { + return error(aexpr->unarithm.operand->loc, expr, errors, + "Cannot de-reference non-pointer type"); + } + if (operand->result->pointer.flags & PTR_NULLABLE) { + return error(aexpr->unarithm.operand->loc, expr, errors, + "Cannot dereference nullable pointer type"); + } expr->result = operand->result->pointer.referent; break; } trleave(TR_CHECK, NULL); + return errors; } -void +struct errors * check_expression(struct context *ctx, const struct ast_expression *aexpr, struct expression *expr, - const struct type *hint) + const struct type *hint, + struct errors *errors) { trenter(TR_CHECK, "expression"); expr->loc = aexpr->loc; switch (aexpr->type) { case EXPR_ACCESS: - check_expr_access(ctx, aexpr, expr, hint); + errors = check_expr_access(ctx, aexpr, expr, hint, errors); break; case EXPR_ALLOC: - check_expr_alloc(ctx, aexpr, expr, hint); + errors = check_expr_alloc(ctx, aexpr, expr, hint, errors); break; case EXPR_APPEND: - check_expr_append(ctx, aexpr, expr, hint); + errors = check_expr_append(ctx, aexpr, expr, hint, errors); break; case EXPR_ASSERT: - check_expr_assert(ctx, aexpr, expr, hint); + errors = check_expr_assert(ctx, aexpr, expr, hint, errors); break; case EXPR_ASSIGN: - check_expr_assign(ctx, aexpr, expr, hint); + errors = check_expr_assign(ctx, aexpr, expr, hint, errors); break; case EXPR_BINARITHM: - check_expr_binarithm(ctx, aexpr, expr, hint); + errors = check_expr_binarithm(ctx, aexpr, expr, hint, errors); break; case EXPR_BINDING: - check_expr_binding(ctx, aexpr, expr, hint); + errors = check_expr_binding(ctx, aexpr, expr, hint, errors); break; case EXPR_BREAK: case EXPR_CONTINUE: - check_expr_control(ctx, aexpr, expr, hint); + errors = check_expr_control(ctx, aexpr, expr, hint, errors); break; case EXPR_CALL: - check_expr_call(ctx, aexpr, expr, hint); + errors = check_expr_call(ctx, aexpr, expr, hint, errors); break; case EXPR_CAST: - check_expr_cast(ctx, aexpr, expr, hint); + errors = check_expr_cast(ctx, aexpr, expr, hint, errors); break; case EXPR_CONSTANT: - check_expr_constant(ctx, aexpr, expr, hint); + errors = check_expr_constant(ctx, aexpr, expr, hint, errors); break; case EXPR_DEFER: - check_expr_defer(ctx, aexpr, expr, hint); + errors = check_expr_defer(ctx, aexpr, expr, hint, errors); break; case EXPR_DELETE: - check_expr_delete(ctx, aexpr, expr, hint); + errors = check_expr_delete(ctx, aexpr, expr, hint, errors); break; case EXPR_FOR: - check_expr_for(ctx, aexpr, expr, hint); + errors = check_expr_for(ctx, aexpr, expr, hint, errors); break; case EXPR_FREE: - check_expr_free(ctx, aexpr, expr, hint); + errors = check_expr_free(ctx, aexpr, expr, hint, errors); break; case EXPR_IF: - check_expr_if(ctx, aexpr, expr, hint); + errors = check_expr_if(ctx, aexpr, expr, hint, errors); break; case EXPR_LIST: - check_expr_list(ctx, aexpr, expr, hint); + errors = check_expr_list(ctx, aexpr, expr, hint, errors); break; case EXPR_MATCH: - check_expr_match(ctx, aexpr, expr, hint); + errors = check_expr_match(ctx, aexpr, expr, hint, errors); break; case EXPR_MEASURE: - check_expr_measure(ctx, aexpr, expr, hint); + errors = check_expr_measure(ctx, aexpr, expr, hint, errors); break; case EXPR_PROPAGATE: - check_expr_propagate(ctx, aexpr, expr, hint); + errors = check_expr_propagate(ctx, aexpr, expr, hint, errors); break; case EXPR_RETURN: - check_expr_return(ctx, aexpr, expr, hint); + errors = check_expr_return(ctx, aexpr, expr, hint, errors); break; case EXPR_SLICE: - check_expr_slice(ctx, aexpr, expr, hint); + errors = check_expr_slice(ctx, aexpr, expr, hint, errors); break; case EXPR_STRUCT: - check_expr_struct(ctx, aexpr, expr, hint); + errors = check_expr_struct(ctx, aexpr, expr, hint, errors); break; case EXPR_SWITCH: - check_expr_switch(ctx, aexpr, expr, hint); + errors = check_expr_switch(ctx, aexpr, expr, hint, errors); break; case EXPR_TUPLE: - check_expr_tuple(ctx, aexpr, expr, hint); + errors = check_expr_tuple(ctx, aexpr, expr, hint, errors); break; case EXPR_UNARITHM: - check_expr_unarithm(ctx, aexpr, expr, hint); + errors = check_expr_unarithm(ctx, aexpr, expr, hint, errors); break; } trleave(TR_CHECK, NULL); assert(expr->result); + return errors; } static struct declaration * @@ -2324,7 +2605,10 @@ check_function(struct context *ctx, } struct expression *body = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, afndecl->body, body, fntype->func.result); + struct errors *errors = check_expression(ctx, afndecl->body, body, + fntype->func.result, NULL); + // TODO: Pass errors up and deal with them at the end of check + handle_errors(errors); expect(&afndecl->body->loc, body->terminates || type_is_assignable(fntype->func.result, body->result), @@ -2362,28 +2646,14 @@ check_global(struct context *ctx, const struct type *type = type_store_lookup_atype( ctx->store, agdecl->type); - bool context = agdecl->type->storage == STORAGE_ARRAY - && agdecl->type->array.contextual; // TODO: Free initialier struct expression *initializer = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, agdecl->init, initializer, type); - - if (context) { - expect(&agdecl->init->loc, - initializer->result->storage == STORAGE_ARRAY, - "Cannot infer array length from non-array type"); - expect(&agdecl->init->loc, - initializer->result->array.members == type->array.members, - "Initializer is not assignable to binding type"); - type = initializer->result; - - // Update type of object (drops const, hack!) - struct scope_object *obj = (struct scope_object *)scope_lookup( - ctx->scope, &agdecl->ident); - obj->type = type; - } + struct errors *errors = check_expression(ctx, agdecl->init, initializer, + type, NULL); + // TODO: Pass errors up and deal with them at the end of check + handle_errors(errors); expect(&agdecl->init->loc, type_is_assignable(type, initializer->result), @@ -2462,20 +2732,328 @@ check_declarations(struct context *ctx, return next; } -static void +static bool expr_is_specified(struct context *ctx, + const struct ast_expression *aexpr); + +static bool +type_is_specified(struct context *ctx, const struct ast_type *atype) +{ + if (!atype) { + return true; + } + + switch (atype->storage) { + case STORAGE_BOOL: + case STORAGE_CHAR: + case STORAGE_F32: + case STORAGE_F64: + case STORAGE_FCONST: + case STORAGE_I16: + case STORAGE_I32: + case STORAGE_I64: + case STORAGE_I8: + case STORAGE_ICONST: + case STORAGE_INT: + case STORAGE_NULL: + case STORAGE_RUNE: + case STORAGE_SIZE: + case STORAGE_STRING: + case STORAGE_U16: + case STORAGE_U32: + case STORAGE_U64: + case STORAGE_U8: + case STORAGE_UINT: + case STORAGE_UINTPTR: + case STORAGE_VOID: + return true; + case STORAGE_ALIAS: + return scope_lookup(ctx->scope, &atype->alias) != NULL; + case STORAGE_ARRAY: + case STORAGE_SLICE: + return type_is_specified(ctx, atype->array.members) + && expr_is_specified(ctx, atype->array.length); + case STORAGE_ENUM: + for (struct ast_enum_field *field = atype->_enum.values; + field; field = field->next) { + if (!expr_is_specified(ctx, field->value)) { + return false; + } + } + return true; + case STORAGE_FUNCTION: + for (struct ast_function_parameters *param = atype->func.params; + param; param = param->next) { + if (!type_is_specified(ctx, param->type)) { + return false; + } + } + return type_is_specified(ctx, atype->func.result); + case STORAGE_POINTER: + return true; + case STORAGE_STRUCT: + case STORAGE_UNION: + for (const struct ast_struct_union_type *stype = + &atype->struct_union; + stype; stype = stype->next) { + 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; + } + } + return true; + case STORAGE_TAGGED: + for (const struct ast_tagged_union_type *ttype = + &atype->tagged_union; + ttype; ttype = ttype->next) { + if (!type_is_specified(ctx, ttype->type)) { + return false; + } + } + return true; + case STORAGE_TUPLE: + for (const struct ast_tuple_type *ttype = &atype->tuple; + ttype; ttype = ttype->next) { + if (!type_is_specified(ctx, ttype->type)) { + return false; + } + } + return true; + } + assert(0); // Unreachable +} + +static bool +expr_is_specified(struct context *ctx, const struct ast_expression *aexpr) +{ + if (!aexpr) { + return true; + } + + switch (aexpr->type) { + case EXPR_ACCESS: + switch (aexpr->access.type) { + case ACCESS_IDENTIFIER: + // XXX: Is this right? + //return scope_lookup(ctx->scope, &aexpr->access.ident); + return true; + case ACCESS_INDEX: + return expr_is_specified(ctx, aexpr->access.array) + && expr_is_specified(ctx, aexpr->access.index); + case ACCESS_FIELD: + return expr_is_specified(ctx, aexpr->access._struct); + case ACCESS_TUPLE: + return expr_is_specified(ctx, aexpr->access.tuple) + && expr_is_specified(ctx, aexpr->access.value); + } + assert(0); + case EXPR_ALLOC: + return expr_is_specified(ctx, aexpr->alloc.expr) + && expr_is_specified(ctx, aexpr->alloc.cap); + case EXPR_APPEND: + for (const struct ast_append_values *value = + aexpr->append.values; + value; value = value->next) { + if (!expr_is_specified(ctx, value->expr)) { + return false; + } + } + return expr_is_specified(ctx, aexpr->append.expr) + && expr_is_specified(ctx, aexpr->append.variadic); + case EXPR_ASSERT: + return expr_is_specified(ctx, aexpr->assert.cond) + && expr_is_specified(ctx, aexpr->assert.message); + case EXPR_ASSIGN: + return expr_is_specified(ctx, aexpr->assign.object) + && expr_is_specified(ctx, aexpr->assign.value); + case EXPR_BINARITHM: + return expr_is_specified(ctx, aexpr->binarithm.lvalue) + && expr_is_specified(ctx, aexpr->binarithm.rvalue); + case EXPR_BINDING: + for (const struct ast_expression_binding *binding = + &aexpr->binding; + binding; binding = binding->next) { + if (!expr_is_specified(ctx, binding->initializer)) { + return false; + } + if (!type_is_specified(ctx, binding->type)) { + return false; + } + } + return true; + case EXPR_BREAK: + case EXPR_CONTINUE: + return true; + case EXPR_CALL: + for (struct ast_call_argument *arg = aexpr->call.args; arg; + arg = arg->next) { + if (!expr_is_specified(ctx, arg->value)) { + return false; + } + } + return expr_is_specified(ctx, aexpr->call.lvalue); + case EXPR_CAST: + return expr_is_specified(ctx, aexpr->cast.value) + && type_is_specified(ctx, aexpr->cast.type); + case EXPR_CONSTANT: + if (aexpr->constant.storage == STORAGE_ARRAY) { + for (struct ast_array_constant *aconst = + aexpr->constant.array; + aconst; aconst = aconst->next) { + if (!expr_is_specified(ctx, aconst->value)) { + return false; + } + } + } + return true; + case EXPR_DEFER: + return expr_is_specified(ctx, aexpr->defer.deferred); + case EXPR_DELETE: + return expr_is_specified(ctx, aexpr->delete.expr); + case EXPR_FOR: + return expr_is_specified(ctx, aexpr->_for.bindings) + && expr_is_specified(ctx, aexpr->_for.cond) + && expr_is_specified(ctx, aexpr->_for.afterthought) + && expr_is_specified(ctx, aexpr->_for.body); + case EXPR_FREE: + return expr_is_specified(ctx, aexpr->free.expr); + case EXPR_IF: + return expr_is_specified(ctx, aexpr->_if.cond) + && expr_is_specified(ctx, aexpr->_if.true_branch) + && expr_is_specified(ctx, aexpr->_if.false_branch); + case EXPR_LIST: + for (const struct ast_expression_list *list = &aexpr->list; + list; list = list->next) { + if (!expr_is_specified(ctx, list->expr)) { + return false; + } + } + return true; + case EXPR_MATCH: + for (struct ast_match_case *mcase = aexpr->match.cases; mcase; + mcase = mcase->next) { + if (!type_is_specified(ctx, mcase->type)) { + return false; + } + if (!expr_is_specified(ctx, mcase->value)) { + return false; + } + } + return expr_is_specified(ctx, aexpr->match.value); + case EXPR_MEASURE: + switch (aexpr->measure.op) { + case M_LEN: + return expr_is_specified(ctx, aexpr->measure.value); + case M_SIZE: + return type_is_specified(ctx, aexpr->measure.type); + case M_OFFSET: + assert(0); // TODO + } + assert(0); + case EXPR_PROPAGATE: + return expr_is_specified(ctx, aexpr->propagate.value); + case EXPR_RETURN: + return expr_is_specified(ctx, aexpr->_return.value); + case EXPR_SLICE: + return expr_is_specified(ctx, aexpr->slice.object) + && expr_is_specified(ctx, aexpr->slice.start) + && expr_is_specified(ctx, aexpr->slice.end); + case EXPR_STRUCT: + for (struct ast_field_value *field = aexpr->_struct.fields; + field; field = field->next) { + if (field->is_embedded) { + if (!expr_is_specified(ctx, field->embedded)) { + return false; + } + } else { + if (!type_is_specified(ctx, + field->field.type)) { + return false; + } + if (!expr_is_specified(ctx, + field->field.initializer)) { + return false; + } + } + } + if (aexpr->_struct.type.name) { + if (!scope_lookup(ctx->scope, &aexpr->_struct.type)) { + return false; + } + } + return true; + case EXPR_SWITCH: + for (struct ast_switch_case *scase = aexpr->_switch.cases; + scase; scase =scase->next) { + for (struct ast_case_option *opt = scase->options; + opt; opt = opt->next) { + if (!expr_is_specified(ctx, opt->value)) { + return false; + } + } + if (!expr_is_specified(ctx, scase->value)) { + return false; + } + } + return expr_is_specified(ctx, aexpr->_switch.value); + case EXPR_TUPLE: + for (const struct ast_expression_tuple *tuple = &aexpr->tuple; + tuple; tuple = tuple->next) { + if (!expr_is_specified(ctx, tuple->expr)) { + return false; + } + } + return true; + case EXPR_UNARITHM: + return expr_is_specified(ctx, aexpr->unarithm.operand); + } + assert(0); // Unreachable +} + +static bool scan_const(struct context *ctx, const struct ast_global_decl *decl) { + // TODO: Get rid of this once the type store bubbles up errors + for (const struct ast_global_decl *d = decl; d; d = d->next) { + if (!type_is_specified(ctx, d->type) + || !expr_is_specified(ctx, d->init)) { + return false; + } + } trenter(TR_SCAN, "constant"); assert(!decl->symbol); // Invariant const struct type *type = type_store_lookup_atype( ctx->store, decl->type); - // TODO: - // - Free the initializer - // - Defer if we can't evaluate it now (for forward references) + // TODO: Free the initializer struct expression *initializer = xcalloc(1, sizeof(struct expression)); - check_expression(ctx, decl->init, initializer, type); + struct errors *errors = check_expression(ctx, decl->init, initializer, + type, NULL); + if (errors != NULL) { + struct errors *next = errors; + while (next) { + struct errors *tmp = next->next; + free(next); + next = tmp; + } + return false; + } expect(&decl->init->loc, type_is_assignable(type, initializer->result), "Constant type is not assignable from initializer type"); @@ -2484,7 +3062,6 @@ scan_const(struct context *ctx, const struct ast_global_decl *decl) struct expression *value = xcalloc(1, sizeof(struct expression)); enum eval_result r = eval_expr(ctx, initializer, value); - // TODO: More forward reference issues: expect(&decl->init->loc, r == EVAL_OK, "Unable to evaluate constant initializer at compile time"); @@ -2493,23 +3070,29 @@ scan_const(struct context *ctx, const struct ast_global_decl *decl) scope_insert(ctx->unit, O_CONST, &ident, &decl->ident, type, value); trleave(TR_SCAN, NULL); + return true; } -static void +static bool scan_function(struct context *ctx, const struct ast_function_decl *decl) { if ((decl->flags & FN_TEST) && !tag_enabled(ctx->tags, "test")) { - return; + return true; } - trenter(TR_SCAN, "function"); const struct ast_type fn_atype = { .storage = STORAGE_FUNCTION, .flags = TYPE_CONST, .func = decl->prototype, }; + // TODO: Get rid of this once the type store bubbles up errors + // TODO: Do we want to do something on !expr_is_specified(ctx, decl->body)? + if (!type_is_specified(ctx, &fn_atype)) { + return false; + } + trenter(TR_SCAN, "function"); const struct type *fntype = type_store_lookup_atype( ctx->store, &fn_atype); - assert(fntype); // TODO: Forward references + assert(fntype); if (!(decl->flags & FN_TEST)) { struct identifier ident = {0}; @@ -2526,16 +3109,43 @@ scan_function(struct context *ctx, const struct ast_function_decl *decl) char buf[1024]; identifier_unparse_static(&decl->ident, buf, sizeof(buf)); trleave(TR_SCAN, "func %s", buf); + return true; } -static void +static bool scan_global(struct context *ctx, const struct ast_global_decl *decl) { + // TODO: Get rid of this once the type store bubbles up errors + for (const struct ast_global_decl *d = decl; d; d = d->next) { + if (!type_is_specified(ctx, d->type) + || !expr_is_specified(ctx, d->init)) { + return false; + } + } trenter(TR_SCAN, "global"); const struct type *type = type_store_lookup_atype( ctx->store, decl->type); - assert(type); // TODO: Forward references + assert(type); + + if (decl->type->storage == STORAGE_ARRAY + && decl->type->array.contextual) { + // TODO: Free initialier + struct expression *initializer = + xcalloc(1, sizeof(struct expression)); + struct errors *errors = check_expression(ctx, decl->init, + initializer, type, NULL); + if (errors != NULL) { + return false; + } + expect(&decl->init->loc, + initializer->result->storage == STORAGE_ARRAY, + "Cannot infer array length from non-array type"); + expect(&decl->init->loc, + initializer->result->array.members == type->array.members, + "Initializer is not assignable to binding type"); + type = initializer->result; + } struct identifier ident = {0}; if (decl->symbol) { @@ -2546,11 +3156,16 @@ scan_global(struct context *ctx, const struct ast_global_decl *decl) scope_insert(ctx->unit, O_DECL, &ident, &decl->ident, type, NULL); trleave(TR_SCAN, NULL); + return true; } -static void +static bool scan_type(struct context *ctx, const struct ast_type_decl *decl) { + // TODO: Get rid of this once the type store bubbles up errors + if (!type_is_specified(ctx, decl->type)) { + return false; + } trenter(TR_SCAN, "type"); const struct type *type = type_store_lookup_atype(ctx->store, decl->type); @@ -2601,31 +3216,59 @@ scan_type(struct context *ctx, const struct ast_type_decl *decl) } } trleave(TR_SCAN, NULL); + return true; } -static void +static bool +scan_declaration(struct context *ctx, const struct ast_decl *decl) +{ + bool ret = false; + switch (decl->decl_type) { + case AST_DECL_CONST: + ret = scan_const(ctx, &decl->constant); + break; + case AST_DECL_FUNC: + ret = scan_function(ctx, &decl->function); + break; + case AST_DECL_GLOBAL: + ret = scan_global(ctx, &decl->global); + break; + case AST_DECL_TYPE: + ret = scan_type(ctx, &decl->type); + break; + } + return ret; +} + +static struct ast_decls * scan_declarations(struct context *ctx, const struct ast_decls *decls) { trenter(TR_SCAN, "declarations"); - while (decls) { - const struct ast_decl *decl = &decls->decl; - switch (decl->decl_type) { - case AST_DECL_CONST: - scan_const(ctx, &decl->constant); - break; - case AST_DECL_FUNC: - scan_function(ctx, &decl->function); - break; - case AST_DECL_GLOBAL: - scan_global(ctx, &decl->global); - break; - case AST_DECL_TYPE: - scan_type(ctx, &decl->type); - break; + struct ast_decls *next = NULL; + bool found = false; + while (decls || next) { + if (!decls) { + if (!found) { + return next; + } + decls = next; + next = NULL; + found = false; + } + if (scan_declaration(ctx, &decls->decl)) { + found = true; + } else { + // TODO: Free this + struct ast_decls *cur = + xcalloc(sizeof(struct ast_decls), 1); + memcpy(cur, decls, sizeof(struct ast_decls)); + cur->next = next; + next = cur; } decls = decls->next; } trleave(TR_SCAN, NULL); + return next; } static void @@ -2749,7 +3392,14 @@ check_internal(struct type_store *ts, struct scopes **next = &subunit_scopes; struct imports **inext = &unit->imports; - // First pass populates the type graph + struct unresolveds { + struct scope *scope; + const struct ast_decls *unresolved; + struct unresolveds *next; + }; + struct unresolveds *cur = NULL, *unresolved = NULL; + + // First pass populates the imports for (const struct ast_subunit *su = &aunit->subunits; su; su = su->next) { scope_push(&ctx.scope, TR_SCAN); @@ -2774,19 +3424,72 @@ check_internal(struct type_store *ts, } } - ctx.store->check_context = &ctx; - scan_declarations(&ctx, &su->decls); + struct unresolveds *new = + xcalloc(sizeof(struct unresolveds), 1); + new->unresolved = &su->decls; + new->next = cur; + cur = new; *next = xcalloc(1, sizeof(struct scopes)); - (*next)->scope = scope_pop(&ctx.scope, TR_SCAN); + new->scope = (*next)->scope = scope_pop(&ctx.scope, TR_SCAN); next = &(*next)->next; + + + } + + // Second pass populates the type graph + bool found = false; + while (cur || unresolved) { + if (!cur) { + if (!found) { + const struct ast_decls *d = + unresolved->unresolved; + while (d->next) { + d = d->next; + } + expect(&d->decl.loc, false, + "Unresolvable identifier"); + } + cur = unresolved; + unresolved = NULL; + found = false; + } + ctx.scope = cur->scope; + ctx.store->check_context = &ctx; + struct ast_decls *left = + scan_declarations(&ctx, cur->unresolved); + if (left) { + struct unresolveds *new = + xcalloc(sizeof(struct unresolveds), 1); + new->scope = cur->scope; + new->unresolved = left; + new->next = unresolved; + unresolved = new; + } + + size_t old_len = 0, new_len = 0; + for (const struct ast_decls *old = cur->unresolved; old; + old = old->next) { + old_len++; + } + for (struct ast_decls *new = left; new; + new = new->next) { + new_len++; + } + if (new_len < old_len) { + found = true; + } + + struct unresolveds *tmp = cur; + cur = tmp->next; + free(tmp); } if (scan_only) { return ctx.unit; } - // Second pass populates the expression graph + // Third pass populates the expression graph struct scopes *scope = subunit_scopes; struct declarations **next_decl = &unit->declarations; for (const struct ast_subunit *su = &aunit->subunits; diff --git a/src/type_store.c b/src/type_store.c @@ -44,7 +44,9 @@ ast_array_len(struct type_store *store, const struct ast_type *atype) if (atype->array.length == NULL) { return SIZE_UNDEFINED; } - check_expression(store->check_context, atype->array.length, &in, NULL); + struct errors *errors = check_expression(store->check_context, + atype->array.length, &in, NULL, NULL); + assert(errors == NULL); // TODO: Handle this gracefully enum eval_result r = eval_expr(store->check_context, &in, &out); // TODO: Bubble up these errors: assert(r == EVAL_OK); @@ -165,7 +167,9 @@ struct_insert_field(struct type_store *store, struct struct_field **fields, if (atype->offset) { *ccompat = false; struct expression in, out; - check_expression(store->check_context, atype->offset, &in, NULL); + struct errors *errors = check_expression(store->check_context, + atype->offset, &in, NULL, NULL); + assert(errors == NULL); // TODO: Handle this gracefully enum eval_result r = eval_expr(store->check_context, &in, &out); // TODO: Bubble up assert(r == EVAL_OK); @@ -540,8 +544,11 @@ type_init_from_atype(struct type_store *store, value->name = strdup(avalue->name); if (avalue->value != NULL) { struct expression in, out; - check_expression(store->check_context, - avalue->value, &in, storage); + struct errors *errors = check_expression( + store->check_context, avalue->value, + &in, storage, NULL); + // TODO: Handle this more gracefully + assert(errors == NULL); enum eval_result r = eval_expr(store->check_context, &in, &out); // TODO: Bubble this up