harec

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

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:
Minclude/ast.h | 1+
Minclude/expr.h | 1+
Minclude/types.h | 1+
Msrc/check.c | 76+++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/parse.c | 2++
Msrc/types.c | 24++++++++++++++++++++++++
Mtests/23-errors.ha | 13+++++++++++++
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(); };