mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-02 12:48:14 +00:00
Allow for foo() or_else unreachable() and other diverging procedures
This commit is contained in:
+23
-3
@@ -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_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_);
|
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 = "") {
|
void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") {
|
||||||
auto results = did_you_mean_results(d);
|
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;
|
return Expr_Expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
check_multi_expr_with_type_hint(c, &y, default_value, x.type);
|
bool y_is_diverging = false;
|
||||||
error_operand_no_value(&y);
|
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) {
|
if (y.mode == Addressing_Invalid) {
|
||||||
o->mode = Addressing_Value;
|
o->mode = Addressing_Value;
|
||||||
o->type = t_invalid;
|
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);
|
add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value);
|
||||||
|
|
||||||
if (left_type != nullptr) {
|
if (left_type != nullptr) {
|
||||||
check_assignment(c, &y, left_type, name);
|
if (!y_is_diverging) {
|
||||||
|
check_assignment(c, &y, left_type, name);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
check_or_else_expr_no_value_error(c, name, x, type_hint);
|
check_or_else_expr_no_value_error(c, name, x, type_hint);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1852,7 +1852,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
|
|||||||
}
|
}
|
||||||
|
|
||||||
case BuiltinProc_unreachable:
|
case BuiltinProc_unreachable:
|
||||||
LLVMBuildUnreachable(p->builder);
|
lb_emit_unreachable(p);
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ bool lb_is_type_aggregate(Type *t) {
|
|||||||
return false;
|
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) {
|
lbValue lb_correct_endianness(lbProcedure *p, lbValue value) {
|
||||||
Type *src = core_type(value.type);
|
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 = {};
|
lbValue rhs = {};
|
||||||
lb_emit_try_lhs_rhs(p, arg, tv, &lhs, &rhs);
|
lb_emit_try_lhs_rhs(p, arg, tv, &lhs, &rhs);
|
||||||
|
|
||||||
LLVMValueRef incoming_values[2] = {};
|
|
||||||
LLVMBasicBlockRef incoming_blocks[2] = {};
|
|
||||||
|
|
||||||
GB_ASSERT(else_expr != nullptr);
|
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);
|
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_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_);
|
||||||
lb_start_block(p, 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, then);
|
||||||
lb_start_block(p, done);
|
return lb_emit_conv(p, lhs, type);
|
||||||
|
} else {
|
||||||
|
LLVMValueRef incoming_values[2] = {};
|
||||||
|
LLVMBasicBlockRef incoming_blocks[2] = {};
|
||||||
|
|
||||||
lbValue res = {};
|
lbBlock *then = lb_create_block(p, "or_else.then");
|
||||||
res.value = LLVMBuildPhi(p->builder, lb_type(p->module, type), "");
|
lbBlock *done = lb_create_block(p, "or_else.done"); // NOTE(bill): Append later
|
||||||
res.type = type;
|
lbBlock *else_ = lb_create_block(p, "or_else.else");
|
||||||
|
|
||||||
GB_ASSERT(p->curr_block->preds.count >= 2);
|
lb_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_);
|
||||||
incoming_blocks[0] = p->curr_block->preds[0]->block;
|
lb_start_block(p, then);
|
||||||
incoming_blocks[1] = p->curr_block->preds[1]->block;
|
|
||||||
|
|
||||||
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<Ast *> const &return_results);
|
void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results);
|
||||||
|
|||||||
Reference in New Issue
Block a user