commit b01a5c5ab3965879b62fe5bb64a0811e35d78d04
parent 28e7b7036bca738f771a8676b4dcf7f9bc5c63d0
Author: Drew DeVault <sir@cmpwn.com>
Date: Tue, 23 Feb 2021 13:27:53 -0500
check: lower error propagation to match expression
Diffstat:
M | src/check.c | | | 66 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- |
M | src/gen.c | | | 2 | +- |
2 files changed, 56 insertions(+), 12 deletions(-)
diff --git a/src/check.c b/src/check.c
@@ -1564,20 +1564,13 @@ check_expr_propagate(struct context *ctx,
const struct type *hint)
{
trenter(TR_CHECK, "propagate");
- expr->type = EXPR_PROPAGATE;
-
struct expression *lvalue = xcalloc(1, sizeof(struct expression));
check_expression(ctx, aexpr->propagate.value, lvalue, hint);
- expr->propagate.value = lvalue;
const struct type *intype = lvalue->result;
- if (type_dealias(intype)->storage != STORAGE_TAGGED) {
- expect(&aexpr->loc,
- intype->flags & TYPE_ERROR,
- "No error can occur here, cannot propagate");
- expr->result = &builtin_type_void;
- return;
- }
+ expect(&aexpr->loc,
+ type_dealias(intype)->storage,
+ "Cannot use error propagation with non-tagged type");
struct type_tagged_union result_tagged = {0};
struct type_tagged_union *tagged = &result_tagged,
@@ -1631,10 +1624,61 @@ check_expr_propagate(struct context *ctx,
result_type = result_tagged.type;
}
- expr->result = result_type;
expect(&aexpr->loc,
type_is_assignable(ctx->fntype->func.result, return_type),
"Error type is not assignable to function result type");
+
+ // Lower to a match expression
+ expr->type = EXPR_MATCH;
+ expr->match.value = lvalue;
+
+ struct scope *scope = scope_push(&ctx->scope, TR_CHECK);
+ scope->type = EXPR_MATCH;
+ struct match_case *case_ok = xcalloc(1, sizeof(struct match_case));
+ struct match_case *case_err = xcalloc(1, sizeof(struct match_case));
+ struct identifier ok_name = {0}, err_name = {0};
+
+ int n = snprintf(NULL, 0, "ok.%d", ctx->id);
+ ok_name.name = xcalloc(n + 1, 1);
+ snprintf(ok_name.name, n + 1, "ok.%d", ctx->id);
+ ++ctx->id;
+ const struct scope_object *ok_obj = scope_insert(scope, O_BIND,
+ &ok_name, &ok_name, result_type, NULL);
+
+ n = snprintf(NULL, 0, "err.%d", ctx->id);
+ err_name.name = xcalloc(n + 1, 1);
+ snprintf(err_name.name, n + 1, "err.%d", ctx->id);
+ ++ctx->id;
+ const struct scope_object *err_obj = scope_insert(scope, O_BIND,
+ &err_name, &err_name, return_type, NULL);
+
+ case_ok->type = result_type;
+ case_ok->object = ok_obj;
+ case_ok->value = xcalloc(1, sizeof(struct expression));
+ case_ok->value->type = EXPR_ACCESS;
+ case_ok->value->access.type = ACCESS_IDENTIFIER;
+ case_ok->value->access.object = ok_obj;
+ case_ok->value->result = result_type;
+
+ case_err->type = return_type;
+ case_err->object = err_obj;
+ case_err->value = xcalloc(1, sizeof(struct expression));
+ case_err->value->type = EXPR_RETURN;
+ 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;
+
+ expr->result = result_type;
}
static void
diff --git a/src/gen.c b/src/gen.c
@@ -2571,7 +2571,7 @@ gen_expression(struct gen_context *ctx,
gen_expr_measure(ctx, expr, out);
break;
case EXPR_PROPAGATE:
- assert(0); // TODO
+ assert(0); // Lowered in check
case EXPR_RETURN:
gen_expr_return(ctx, expr, out);
break;