From a58e4d035964275ade5e09fa6bcafb5d46cc46e2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 5 Aug 2022 12:19:57 +0100 Subject: [PATCH] Allow for `foo() or_else unreachable()` and other diverging procedures --- src/check_expr.cpp | 26 ++++++++++++-- src/llvm_backend_proc.cpp | 2 +- src/llvm_backend_utility.cpp | 69 ++++++++++++++++++++++++------------ 3 files changed, 70 insertions(+), 27 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index cf9f2f751..96adde013 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -119,6 +119,7 @@ void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint); void check_or_return_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_); +bool is_diverging_expr(Ast *expr); void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") { auto results = did_you_mean_results(d); @@ -7399,8 +7400,25 @@ ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type return Expr_Expr; } - check_multi_expr_with_type_hint(c, &y, default_value, x.type); - error_operand_no_value(&y); + bool y_is_diverging = false; + check_expr_base(c, &y, default_value, x.type); + switch (y.mode) { + case Addressing_NoValue: + if (is_diverging_expr(y.expr)) { + // Allow + y.mode = Addressing_Value; + y_is_diverging = true; + } else { + error_operand_no_value(&y); + y.mode = Addressing_Invalid; + } + break; + case Addressing_Type: + error_operand_not_expression(&y); + y.mode = Addressing_Invalid; + break; + } + if (y.mode == Addressing_Invalid) { o->mode = Addressing_Value; o->type = t_invalid; @@ -7414,7 +7432,9 @@ ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value); if (left_type != nullptr) { - check_assignment(c, &y, left_type, name); + if (!y_is_diverging) { + check_assignment(c, &y, left_type, name); + } } else { check_or_else_expr_no_value_error(c, name, x, type_hint); } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index a026356a2..67f008a35 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1852,7 +1852,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv, } case BuiltinProc_unreachable: - LLVMBuildUnreachable(p->builder); + lb_emit_unreachable(p); return {}; diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 88ec2f22c..bbacce7a2 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -39,6 +39,12 @@ bool lb_is_type_aggregate(Type *t) { return false; } +void lb_emit_unreachable(lbProcedure *p) { + LLVMValueRef instr = LLVMGetLastInstruction(p->curr_block->block); + if (instr == nullptr || !lb_is_instr_terminating(instr)) { + LLVMBuildUnreachable(p->builder); + } +} lbValue lb_correct_endianness(lbProcedure *p, lbValue value) { Type *src = core_type(value.type); @@ -350,40 +356,57 @@ lbValue lb_emit_or_else(lbProcedure *p, Ast *arg, Ast *else_expr, TypeAndValue c lbValue rhs = {}; lb_emit_try_lhs_rhs(p, arg, tv, &lhs, &rhs); - LLVMValueRef incoming_values[2] = {}; - LLVMBasicBlockRef incoming_blocks[2] = {}; - GB_ASSERT(else_expr != nullptr); - lbBlock *then = lb_create_block(p, "or_else.then"); - lbBlock *done = lb_create_block(p, "or_else.done"); // NOTE(bill): Append later - lbBlock *else_ = lb_create_block(p, "or_else.else"); - - lb_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_); - lb_start_block(p, then); Type *type = default_type(tv.type); - incoming_values[0] = lb_emit_conv(p, lhs, type).value; + if (is_diverging_expr(else_expr)) { + lbBlock *then = lb_create_block(p, "or_else.then"); + lbBlock *else_ = lb_create_block(p, "or_else.else"); - lb_emit_jump(p, done); - lb_start_block(p, else_); + lb_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_); + // NOTE(bill): else block needs to be straight afterwards to make sure that the actual value is used + // from the then block + lb_start_block(p, else_); - incoming_values[1] = lb_emit_conv(p, lb_build_expr(p, else_expr), type).value; + lb_build_expr(p, else_expr); + lb_emit_unreachable(p); // add just in case - lb_emit_jump(p, done); - lb_start_block(p, done); + lb_start_block(p, then); + return lb_emit_conv(p, lhs, type); + } else { + LLVMValueRef incoming_values[2] = {}; + LLVMBasicBlockRef incoming_blocks[2] = {}; - lbValue res = {}; - res.value = LLVMBuildPhi(p->builder, lb_type(p->module, type), ""); - res.type = type; + lbBlock *then = lb_create_block(p, "or_else.then"); + lbBlock *done = lb_create_block(p, "or_else.done"); // NOTE(bill): Append later + lbBlock *else_ = lb_create_block(p, "or_else.else"); - GB_ASSERT(p->curr_block->preds.count >= 2); - incoming_blocks[0] = p->curr_block->preds[0]->block; - incoming_blocks[1] = p->curr_block->preds[1]->block; + lb_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_); + lb_start_block(p, then); - LLVMAddIncoming(res.value, incoming_values, incoming_blocks, 2); + incoming_values[0] = lb_emit_conv(p, lhs, type).value; - return res; + lb_emit_jump(p, done); + lb_start_block(p, else_); + + incoming_values[1] = lb_emit_conv(p, lb_build_expr(p, else_expr), type).value; + + lb_emit_jump(p, done); + lb_start_block(p, done); + + lbValue res = {}; + res.value = LLVMBuildPhi(p->builder, lb_type(p->module, type), ""); + res.type = type; + + GB_ASSERT(p->curr_block->preds.count >= 2); + incoming_blocks[0] = p->curr_block->preds[0]->block; + incoming_blocks[1] = p->curr_block->preds[1]->block; + + LLVMAddIncoming(res.value, incoming_values, incoming_blocks, 2); + + return res; + } } void lb_build_return_stmt(lbProcedure *p, Slice const &return_results);