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:
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