Fixes to optional rethrow in expressions.

This commit is contained in:
Christoffer Lerno
2026-01-26 04:10:38 +01:00
parent e901a3de55
commit 3e76b7ff1c
5 changed files with 317 additions and 14 deletions

View File

@@ -3317,6 +3317,7 @@ static void llvm_emit_ptr_comparison(GenContext *c, BEValue *result, BEValue *lh
{
llvm_value_rvalue(c, lhs);
llvm_value_rvalue(c, rhs);
RETURN_ON_EMPTY_BLOCK(result);
LLVMValueRef lhs_value = lhs->value;
LLVMValueRef rhs_value = rhs->value;
LLVMValueRef val;
@@ -3545,7 +3546,7 @@ static inline void llvm_emit_fp_vector_compare(GenContext *c, BEValue *be_value,
llvm_value_set(be_value, cmp, type_bool);
}
static inline void llvm_emit_bool_vector_compare(GenContext *c, BEValue *be_value, BEValue *lhs, BEValue *rhs, BinaryOp binary_op, unsigned len)
static inline void llvm_emit_bool_vector_compare(GenContext *c, BEValue *result, BEValue *lhs, BEValue *rhs, BinaryOp binary_op, unsigned len)
{
LLVMTypeRef bool_vec = LLVMVectorType(c->bool_type, len);
LLVMTypeRef load_vec = LLVMVectorType(c->byte_type, len);
@@ -3564,10 +3565,10 @@ static inline void llvm_emit_bool_vector_compare(GenContext *c, BEValue *be_valu
{
cmp = llvm_emit_call_intrinsic(c, intrinsic_id.vector_reduce_or, &bool_vec, 1, &cmp, 1);
}
llvm_value_set(be_value, cmp, type_bool);
llvm_value_set(result, cmp, type_bool);
}
static void llvm_emit_array_comp(GenContext *c, BEValue *be_value, BEValue *lhs, BEValue *rhs, BinaryOp binary_op)
static void llvm_emit_array_comp(GenContext *c, BEValue *result, BEValue *lhs, BEValue *rhs, BinaryOp binary_op)
{
Type *array_base = type_flatten(lhs->type->array.base);
switch (array_base->type_kind)
@@ -3585,8 +3586,9 @@ static void llvm_emit_array_comp(GenContext *c, BEValue *be_value, BEValue *lhs,
MEMCMP:
llvm_value_addr(c, lhs);
llvm_value_addr(c, rhs);
llvm_emit_memcmp(c, be_value, lhs->value, rhs->value, llvm_const_int(c, type_usz, type_size(lhs->type)));
llvm_emit_int_comp_zero(c, be_value, be_value, binary_op);
RETURN_ON_EMPTY_BLOCK(result);
llvm_emit_memcmp(c, result, lhs->value, rhs->value, llvm_const_int(c, type_usz, type_size(lhs->type)));
llvm_emit_int_comp_zero(c, result, result, binary_op);
return;
case VECTORS:
if (is_power_of_two(array_base->array.len) && !type_flat_is_floatlike(array_base->array.base)) goto MEMCMP;
@@ -3625,12 +3627,12 @@ MEMCMP:
{
if (array_base_type == type_bool)
{
llvm_emit_bool_vector_compare(c, be_value, lhs, rhs, binary_op, len);
llvm_emit_bool_vector_compare(c, result, lhs, rhs, binary_op, len);
return;
}
if (type_is_float(array_base_type))
{
llvm_emit_fp_vector_compare(c, be_value, lhs, rhs, binary_op, array_base_type, len);
llvm_emit_fp_vector_compare(c, result, lhs, rhs, binary_op, array_base_type, len);
return;
}
LLVMBasicBlockRef blocks[17];
@@ -3661,7 +3663,7 @@ MEMCMP:
blocks[len] = ok_block;
LLVMValueRef phi = LLVMBuildPhi(c->builder, c->bool_type, "array_cmp_phi");
LLVMAddIncoming(phi, value_block, blocks, len + 1);
llvm_value_set(be_value, phi, type_bool);
llvm_value_set(result, phi, type_bool);
return;
}
@@ -3695,13 +3697,14 @@ MEMCMP:
llvm_emit_block(c, exit);
LLVMValueRef success = LLVMConstInt(c->bool_type, want_match ? 1 : 0, false);
LLVMValueRef failure = LLVMConstInt(c->bool_type, want_match ? 0 : 1, false);
llvm_new_phi(c, be_value, "array_cmp_phi", type_bool, success, comparison_phi, failure, loop_begin_phi);
llvm_new_phi(c, result, "array_cmp_phi", type_bool, success, comparison_phi, failure, loop_begin_phi);
}
static void llvm_emit_float_comp(GenContext *c, BEValue *be_value, BEValue *lhs, BEValue *rhs, BinaryOp binary_op, Type *vector_type)
static void llvm_emit_float_comp(GenContext *c, BEValue *result, BEValue *lhs, BEValue *rhs, BinaryOp binary_op, Type *vector_type)
{
llvm_value_rvalue(c, lhs);
llvm_value_rvalue(c, rhs);
RETURN_ON_EMPTY_BLOCK(result);
LLVMValueRef lhs_value = lhs->value;
LLVMValueRef rhs_value = rhs->value;
LLVMValueRef val;
@@ -3732,10 +3735,10 @@ static void llvm_emit_float_comp(GenContext *c, BEValue *be_value, BEValue *lhs,
}
if (vector_type)
{
llvm_convert_vector_comparison(c, be_value, val, vector_type, BINARYOP_EQ == binary_op);
llvm_convert_vector_comparison(c, result, val, vector_type, BINARYOP_EQ == binary_op);
return;
}
llvm_value_set(be_value, val, type_bool);
llvm_value_set(result, val, type_bool);
}
void llvm_emit_lhs_is_subtype(GenContext *c, BEValue *result, BEValue *lhs, BEValue *rhs)
@@ -3779,6 +3782,7 @@ void llvm_emit_comp(GenContext *c, BEValue *result, BEValue *lhs, BEValue *rhs,
case ALL_INTS:
llvm_value_rvalue(c, lhs);
llvm_value_rvalue(c, rhs);
RETURN_ON_EMPTY_BLOCK(result);
llvm_emit_int_comp_raw(c, result, lhs->type, rhs->type, lhs->value, rhs->value, binary_op);
return;
case ALL_FLOATS:
@@ -4092,13 +4096,20 @@ void llvm_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs
}
llvm_emit_expr(c, &lhs, exprptr(expr->binary_expr.left));
}
RETURN_ON_EMPTY_BLOCK(be_value);
// We need the rvalue.
llvm_fold_for_compare(c, &lhs);
RETURN_ON_EMPTY_BLOCK(be_value);
// Evaluate rhs
BEValue rhs;
llvm_emit_expr(c, &rhs, exprptr(expr->binary_expr.right));
RETURN_ON_EMPTY_BLOCK(be_value);
llvm_fold_for_compare(c, &rhs);
RETURN_ON_EMPTY_BLOCK(be_value);
EMIT_EXPR_LOC(c, expr);
// Comparison <=>
if (binary_op >= BINARYOP_GT && binary_op <= BINARYOP_EQ)

View File

@@ -595,6 +595,7 @@ void llvm_emit_debug_local_var(GenContext *c, Decl *var);
#define PUSH_DEFER_ERROR(val__) LLVMValueRef def_err__ = c->defer_error_var; c->defer_error_var = val__
#define POP_DEFER_ERROR() c->defer_error_var = def_err__
#define RETURN_ON_EMPTY_BLOCK(value__) do { if (!c->current_block) { llvm_value_set_empty(value__); return; }} while(0)
#define RETURN_ON_EMPTY_BLOCK_VOID() do { if (!c->current_block) { return; }} while(0)
LLVMAtomicOrdering llvm_atomic_ordering(Atomicity atomicity);

View File

@@ -724,10 +724,11 @@ static void llvm_emit_switch_body_if_chain(GenContext *c,
else
{
llvm_emit_comp(c, &equals, &be_value, switch_value, BINARYOP_EQ);
RETURN_ON_EMPTY_BLOCK_VOID();
}
}
next = llvm_basic_block_new(c, "next_if");
llvm_emit_cond_br(c, &equals, block, next);
if (c->current_block) llvm_emit_cond_br(c, &equals, block, next);
if (case_stmt->case_stmt.body)
{
llvm_emit_block(c, block);
@@ -748,7 +749,6 @@ static void llvm_emit_switch_body_if_chain(GenContext *c,
llvm_emit_br(c, exit_block);
}
llvm_emit_block(c, exit_block);
return;
}
static LLVMValueRef llvm_emit_switch_jump_stmt(GenContext *c,
@@ -1069,6 +1069,7 @@ void llvm_emit_switch(GenContext *c, Ast *ast)
{
// Regular switch
llvm_emit_cond(c, &switch_value, expr, false);
RETURN_ON_EMPTY_BLOCK_VOID();
}
else
{

View File

@@ -0,0 +1,144 @@
// #target: macos-x64
module test;
faultdef FOO_FAULT;
macro bool? maybe_throw() => FOO_FAULT~;
macro int? maybe_throw_int() => FOO_FAULT~;
macro int[2]? maybe_throw_array() => FOO_FAULT~;
macro int[<2>]? maybe_throw_vec() => FOO_FAULT~;
macro int[]? maybe_throw_slice() => FOO_FAULT~;
macro float? maybe_throw_float() => FOO_FAULT~;
macro void*? maybe_throw_ptr() => FOO_FAULT~;
fn void? throwme1()
{
bool x = maybe_throw()! == true;
}
fn void? throwme2()
{
bool x = maybe_throw_int()! == 1;
}
fn void? throwme3()
{
bool x = maybe_throw_array()! == (int[2]){};
}
fn void? throwme4()
{
bool x = maybe_throw_slice()! == (int[]) {};
}
fn void? throwme5()
{
bool x = maybe_throw_vec()! == (int[<2>]){};
}
fn void? throwme6()
{
bool x = maybe_throw_float()! == 1.0;
}
fn void? throwme7()
{
bool x = maybe_throw_ptr()! == null;
}
fn int main()
{
(void)throwme1();
(void)throwme2();
(void)throwme3();
(void)throwme4();
(void)throwme5();
(void)throwme6();
(void)throwme7();
return 0;
}
/* #expect: test.ll
define i64 @test.throwme1() #0 {
entry:
%x = alloca i8, align 1
%error_var = alloca i64, align 8
store i64 ptrtoint (ptr @test.FOO_FAULT to i64), ptr %error_var, align 8
br label %guard_block
guard_block: ; preds = %entry
%0 = load i64, ptr %error_var, align 8
ret i64 %0
}
define i64 @test.throwme2() #0 {
entry:
%x = alloca i8, align 1
%error_var = alloca i64, align 8
store i64 ptrtoint (ptr @test.FOO_FAULT to i64), ptr %error_var, align 8
br label %guard_block
guard_block: ; preds = %entry
%0 = load i64, ptr %error_var, align 8
ret i64 %0
}
define i64 @test.throwme3() #0 {
entry:
%x = alloca i8, align 1
%error_var = alloca i64, align 8
store i64 ptrtoint (ptr @test.FOO_FAULT to i64), ptr %error_var, align 8
br label %guard_block
guard_block: ; preds = %entry
%0 = load i64, ptr %error_var, align 8
ret i64 %0
}
define i64 @test.throwme4() #0 {
entry:
%x = alloca i8, align 1
%error_var = alloca i64, align 8
store i64 ptrtoint (ptr @test.FOO_FAULT to i64), ptr %error_var, align 8
br label %guard_block
guard_block: ; preds = %entry
%0 = load i64, ptr %error_var, align 8
ret i64 %0
}
define i64 @test.throwme5() #0 {
entry:
%x = alloca i8, align 1
%error_var = alloca i64, align 8
store i64 ptrtoint (ptr @test.FOO_FAULT to i64), ptr %error_var, align 8
br label %guard_block
guard_block: ; preds = %entry
%0 = load i64, ptr %error_var, align 8
ret i64 %0
}
define i64 @test.throwme6() #0 {
entry:
%x = alloca i8, align 1
%error_var = alloca i64, align 8
store i64 ptrtoint (ptr @test.FOO_FAULT to i64), ptr %error_var, align 8
br label %guard_block
guard_block: ; preds = %entry
%0 = load i64, ptr %error_var, align 8
ret i64 %0
}
define i64 @test.throwme7() #0 {
entry:
%x = alloca i8, align 1
%error_var = alloca i64, align 8
store i64 ptrtoint (ptr @test.FOO_FAULT to i64), ptr %error_var, align 8
br label %guard_block
guard_block: ; preds = %entry
%0 = load i64, ptr %error_var, align 8
ret i64 %0
}

View File

@@ -0,0 +1,146 @@
// #target: macos-x64
module test;
faultdef FOO_FAULT;
macro bool? maybe_throw() => FOO_FAULT~;
macro int? maybe_throw_int() => FOO_FAULT~;
fn void? throwme1()
{
if (maybe_throw()!);
}
fn void? throwme2()
{
switch (maybe_throw_int()!) { case 0: break; };
}
fn void? throwme3()
{
switch { case maybe_throw()!: break; };
}
fn void? throwme4()
{
while (maybe_throw()!);
}
fn void? throwme5()
{
for (maybe_throw()!;;);
}
fn void? throwme6()
{
for (;maybe_throw()!, true;);
}
fn void? throwme7()
{
for (;;maybe_throw()!);
}
fn int main()
{
(void)throwme1();
(void)throwme2();
(void)throwme3();
(void)throwme4();
(void)throwme5();
(void)throwme6();
(void)throwme7();
return 0;
}
/* #expect: test.ll
define i64 @test.throwme1() #0 {
entry:
%error_var = alloca i64, align 8
store i64 ptrtoint (ptr @test.FOO_FAULT to i64), ptr %error_var, align 8
br label %guard_block
guard_block: ; preds = %entry
%0 = load i64, ptr %error_var, align 8
ret i64 %0
}
define i64 @test.throwme2() #0 {
entry:
%error_var = alloca i64, align 8
store i64 ptrtoint (ptr @test.FOO_FAULT to i64), ptr %error_var, align 8
br label %guard_block
guard_block: ; preds = %entry
%0 = load i64, ptr %error_var, align 8
ret i64 %0
}
define i64 @test.throwme3() #0 {
entry:
%switch = alloca i8, align 1
%error_var = alloca i64, align 8
store i8 1, ptr %switch, align 1
br label %switch.entry
switch.entry: ; preds = %entry
%0 = load i8, ptr %switch, align 1
%1 = trunc i8 %0 to i1
store i64 ptrtoint (ptr @test.FOO_FAULT to i64), ptr %error_var, align 8
br label %guard_block
guard_block: ; preds = %switch.entry
%2 = load i64, ptr %error_var, align 8
ret i64 %2
}
define i64 @test.throwme4() #0 {
entry:
%error_var = alloca i64, align 8
br label %loop.cond
loop.cond: ; preds = %entry
store i64 ptrtoint (ptr @test.FOO_FAULT to i64), ptr %error_var, align 8
br label %guard_block
guard_block: ; preds = %loop.cond
%0 = load i64, ptr %error_var, align 8
ret i64 %0
}
define i64 @test.throwme5() #0 {
entry:
%error_var = alloca i64, align 8
store i64 ptrtoint (ptr @test.FOO_FAULT to i64), ptr %error_var, align 8
br label %guard_block
guard_block: ; preds = %entry
%0 = load i64, ptr %error_var, align 8
ret i64 %0
}
define i64 @test.throwme6() #0 {
entry:
%error_var = alloca i64, align 8
br label %loop.cond
loop.cond: ; preds = %entry
store i64 ptrtoint (ptr @test.FOO_FAULT to i64), ptr %error_var, align 8
br label %guard_block
guard_block: ; preds = %loop.cond
%0 = load i64, ptr %error_var, align 8
ret i64 %0
}
define i64 @test.throwme7() #0 {
entry:
%error_var = alloca i64, align 8
br label %loop.body
loop.body: ; preds = %entry
store i64 ptrtoint (ptr @test.FOO_FAULT to i64), ptr %error_var, align 8
br label %guard_block
guard_block: ; preds = %loop.body
%0 = load i64, ptr %error_var, align 8
ret i64 %0
}