Browse Source

Allow for `foo() or_else unreachable()` and other diverging procedures

gingerBill 3 years ago
parent
commit
a58e4d0359
3 changed files with 70 additions and 27 deletions
  1. 23 3
      src/check_expr.cpp
  2. 1 1
      src/llvm_backend_proc.cpp
  3. 46 23
      src/llvm_backend_utility.cpp

+ 23 - 3
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_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);
-	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) {
 	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);
 	}
 	}

+ 1 - 1
src/llvm_backend_proc.cpp

@@ -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 {};
 
 
 
 

+ 46 - 23
src/llvm_backend_utility.cpp

@@ -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_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<Ast *> const &return_results);
 void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results);