commit c516ffadafac0c89d748c0be98ecc7a4765cfb5f
parent d199daf6edb87676240177d7f0dd574f12d993d4
Author: Drew DeVault <sir@cmpwn.com>
Date: Sat, 24 Apr 2021 08:29:30 -0400
all: add ! postfix operator to abort on error
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
7 files changed, 95 insertions(+), 23 deletions(-)
diff --git a/include/ast.h b/include/ast.h
@@ -277,6 +277,7 @@ struct ast_expression_measure {
struct ast_expression_propagate {
struct ast_expression *value;
+ bool abort;
};
struct ast_expression_return {
diff --git a/include/expr.h b/include/expr.h
@@ -257,6 +257,7 @@ struct expression_measure {
struct expression_propagate {
struct expression *value;
+ bool abort;
};
struct expression_return {
diff --git a/include/types.h b/include/types.h
@@ -168,6 +168,7 @@ bool type_is_signed(const struct type *type);
bool type_is_integer(const struct type *type);
bool type_is_numeric(const struct type *type);
bool type_is_float(const struct type *type);
+bool type_has_error(const struct type *type);
uint32_t type_hash(const struct type *type);
diff --git a/src/check.c b/src/check.c
@@ -1608,15 +1608,23 @@ check_expr_list(struct context *ctx,
alist->next ? NULL : hint, errors);
list->expr = lexpr;
- alist = alist->next;
- if (alist) {
+ if (alist->next) {
+ expect(&alist->expr->loc, !type_has_error(lexpr->result),
+ "Cannot ignore error here");
*next = xcalloc(1, sizeof(struct expressions));
list = *next;
next = &list->next;
} else {
+ // XXX: This is a bit of a hack
+ if (hint && !type_has_error(hint)) {
+ expect(&alist->expr->loc,
+ !type_has_error(lexpr->result),
+ "Cannot ignore error here");
+ }
expr->result = lexpr->result;
expr->terminates = lexpr->terminates;
}
+ alist = alist->next;
}
scope_pop(&ctx->scope);
@@ -1807,13 +1815,15 @@ check_expr_propagate(struct context *ctx,
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");
+ if (!aexpr->propagate.abort) {
+ 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};
@@ -1870,11 +1880,6 @@ check_expr_propagate(struct context *ctx,
result_type = result_tagged.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;
expr->match.value = lvalue;
@@ -1910,17 +1915,42 @@ check_expr_propagate(struct context *ctx,
case_err->type = return_type;
case_err->object = err_obj;
case_err->value = xcalloc(1, sizeof(struct expression));
- case_err->value->type = EXPR_RETURN;
+
+ if (aexpr->propagate.abort) {
+ case_err->value->type = EXPR_ASSERT;
+ case_err->value->assert.cond = NULL;
+ case_err->value->assert.is_static = false;
+
+ int n = snprintf(NULL, 0, "Assertion failed: error occured at %s:%d:%d",
+ aexpr->loc.path, aexpr->loc.lineno, aexpr->loc.colno);
+ char *s = xcalloc(1, n + 1);
+ snprintf(s, n, "Assertion failed: error occured at %s:%d:%d",
+ aexpr->loc.path, aexpr->loc.lineno, aexpr->loc.colno);
+
+ case_err->value->assert.message = xcalloc(1, sizeof(struct expression));
+ case_err->value->assert.message->type = EXPR_CONSTANT;
+ case_err->value->assert.message->result = &builtin_type_const_str;
+ case_err->value->assert.message->constant.string.value = s;
+ case_err->value->assert.message->constant.string.len = n - 1;
+ } else {
+ 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");
+ }
+
+ case_err->value->type = EXPR_RETURN;
+
+ struct expression *rval =
+ xcalloc(1, sizeof(struct expression));
+ rval->type = EXPR_ACCESS;
+ rval->access.type = ACCESS_IDENTIFIER;
+ rval->access.object = err_obj;
+ rval->result = return_type;
+ case_err->value->_return.value = lower_implicit_cast(
+ ctx->fntype->func.result, rval);
+ }
case_err->value->terminates = true;
case_err->value->result = &builtin_type_void;
- struct expression *rval =
- xcalloc(1, sizeof(struct expression));
- rval->type = EXPR_ACCESS;
- rval->access.type = ACCESS_IDENTIFIER;
- rval->access.object = err_obj;
- rval->result = return_type;
- case_err->value->_return.value = lower_implicit_cast(
- ctx->fntype->func.result, rval);
expr->match.cases = case_ok;
case_ok->next = case_err;
diff --git a/src/parse.c b/src/parse.c
@@ -1376,9 +1376,11 @@ parse_postfix_expression(struct lexer *lexer, struct ast_expression *lvalue)
lvalue = parse_index_slice_expression(lexer, lvalue);
break;
case T_QUESTION:
+ case T_LNOT:
exp = mkexpr(&lexer->loc);
exp->type = EXPR_PROPAGATE;
exp->propagate.value = lvalue;
+ exp->propagate.abort = tok.token == T_LNOT;
lvalue = exp;
break;
default:
diff --git a/src/types.c b/src/types.c
@@ -67,6 +67,26 @@ type_get_value(const struct type *type, uintmax_t index)
return NULL;
}
+// Returns true if this type is or contains an error type
+bool
+type_has_error(const struct type *type)
+{
+ if (type->flags & TYPE_ERROR) {
+ return true;
+ }
+ type = type_dealias(type);
+ if (type->storage != STORAGE_TAGGED) {
+ return false;
+ }
+ const struct type_tagged_union *tu = &type->tagged;
+ for (; tu; tu = tu->next) {
+ if (tu->type->flags & TYPE_ERROR) {
+ return true;
+ }
+ }
+ return false;
+}
+
const char *
type_storage_unparse(enum type_storage storage)
{
@@ -641,6 +661,10 @@ castable_from_tagged(const struct type *to, const struct type *from)
bool
type_is_castable(const struct type *to, const struct type *from)
{
+ if (to->storage == STORAGE_VOID) {
+ return true;
+ }
+
if (type_dealias(to)->storage == STORAGE_TAGGED) {
return tagged_select_subtype(to, from) != NULL
|| tagged_subset_compat(to, from);
diff --git a/tests/23-errors.ha b/tests/23-errors.ha
@@ -1,3 +1,5 @@
+use rt;
+
type err_int = int!;
fn assignability() void = {
@@ -26,7 +28,18 @@ fn propagate() void = {
assert(indirect(false) is error);
};
+fn cannotignore() void = {
+ assert(rt::compile("type error = void!;
+
+export fn main() int = {
+ error;
+ return 42;
+};") != 0);
+ err_if_false(true)!;
+};
+
export fn main() void = {
assignability();
propagate();
+ cannotignore();
};