Fixes to or/and with rethrow.

This commit is contained in:
Christoffer Lerno
2022-08-15 02:05:14 +02:00
parent 0a17857737
commit 9a69a13b04
21 changed files with 536 additions and 112 deletions

View File

@@ -447,7 +447,7 @@ BinaryOp binary_op[TOKEN_LAST + 1] = {
[TOKEN_SHR] = BINARYOP_SHR,
[TOKEN_AND] = BINARYOP_AND,
[TOKEN_OR] = BINARYOP_OR,
[TOKEN_QUESTQUEST] = BINARYOP_OR_ERR,
[TOKEN_QUESTQUEST] = BINARYOP_ELSE,
[TOKEN_AMP] = BINARYOP_BIT_AND,
[TOKEN_BIT_OR] = BINARYOP_BIT_OR,
[TOKEN_BIT_XOR] = BINARYOP_BIT_XOR,

View File

@@ -19,7 +19,7 @@ typedef enum
BINARYOP_BIT_AND,
BINARYOP_AND,
BINARYOP_OR,
BINARYOP_OR_ERR,
BINARYOP_ELSE,
// Don't change the ordering for GT to EQ or things will break
BINARYOP_GT,
BINARYOP_GE,

View File

@@ -5,7 +5,7 @@
#include "llvm_codegen_internal.h"
#include <math.h>
void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs_loaded, BinaryOp binary_op);
static void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs_loaded, BinaryOp binary_op);
static void llvm_emit_any_pointer(GenContext *c, BEValue *any, BEValue *pointer);
static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr);
static void llvm_emit_unary_expr(GenContext *c, BEValue *value, Expr *expr);
@@ -120,32 +120,9 @@ BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, LLVMValue
return value;
}
static inline LLVMValueRef llvm_emit_extract_value(GenContext *c, LLVMValueRef agg, unsigned index)
{
if (LLVMGetTypeKind(LLVMTypeOf(agg)) == LLVMVectorTypeKind)
{
return LLVMBuildExtractElement(c->builder, agg, llvm_const_int(c, type_usize, index), "");
}
return LLVMBuildExtractValue(c->builder, agg, index, "");
}
static inline LLVMValueRef llvm_zext_trunc(GenContext *c, LLVMValueRef data, LLVMTypeRef type)
{
LLVMTypeRef current_type = LLVMTypeOf(data);
if (current_type == type) return data;
assert(LLVMGetTypeKind(type) == LLVMIntegerTypeKind);
assert(LLVMGetTypeKind(current_type) == LLVMIntegerTypeKind);
if (llvm_bitsize(c, current_type) < llvm_bitsize(c, type))
{
return LLVMBuildZExt(c->builder, data, type, "zext");
}
assert(llvm_bitsize(c, current_type) > llvm_bitsize(c, type));
return LLVMBuildTrunc(c->builder, data, type, "ztrunc");
}
static void llvm_convert_vector_comparison(GenContext *c, BEValue *be_value, LLVMValueRef val, Type *vector_type)
{
@@ -270,15 +247,6 @@ void llvm_enter_struct_for_coerce(GenContext *c, LLVMValueRef *struct_ptr, LLVMT
}
}
LLVMValueRef llvm_int_resize(GenContext *c, LLVMValueRef value, LLVMTypeRef from, LLVMTypeRef to)
{
if (llvm_store_size(c, from) >= llvm_store_size(c, to))
{
return LLVMBuildTruncOrBitCast(c->builder, value, to, "trunc");
}
return LLVMBuildZExt(c->builder, value, to, "ext");
}
/**
* General functionality to convert int <-> int ptr <-> int
*/
@@ -352,7 +320,7 @@ LLVMValueRef llvm_emit_coerce(GenContext *c, LLVMTypeRef coerced, BEValue *value
&& LLVMGetTypeKind(coerced) == LLVMIntegerTypeKind
&& LLVMGetTypeKind(llvm_source_type) == LLVMIntegerTypeKind)
{
return llvm_int_resize(c, value->value, llvm_source_type, coerced);
return llvm_zext_trunc(c, value->value, coerced);
}
// 2. From now on we need th address.
@@ -2539,21 +2507,29 @@ static void llvm_emit_slice_assign(GenContext *c, BEValue *be_value, Expr *expr)
}
static void gencontext_emit_logical_and_or(GenContext *c, BEValue *be_value, Expr *expr, BinaryOp op)
static void llvm_emit_logical_and_or(GenContext *c, BEValue *be_value, Expr *expr, BinaryOp op)
{
// Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E)
// For vector implementation.
// Set up basic blocks, following Cone
LLVMBasicBlockRef start_block;
LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, op == BINARYOP_AND ? "and.phi" : "or.phi");
LLVMBasicBlockRef rhs_block = llvm_basic_block_new(c, op == BINARYOP_AND ? "and.rhs" : "or.rhs");
LLVMBasicBlockRef lhs_end_block;
// Generate left-hand condition and conditional branch
llvm_emit_expr(c, be_value, exprptr(expr->binary_expr.left));
llvm_value_rvalue(c, be_value);
start_block = c->current_block;
lhs_end_block = llvm_get_current_block_if_in_use(c);
LLVMValueRef result_on_skip = LLVMConstInt(c->bool_type, op == BINARYOP_AND ? 0 : 1, 0);
// We might end this with a jump, eg (foo()? || bar()) where foo() is a macro and guaranteed not to exit.
if (!lhs_end_block)
{
// Just set any value.
llvm_value_set_bool(be_value, result_on_skip);
return;
}
// Set up basic blocks, following Cone
LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, op == BINARYOP_AND ? "and.phi" : "or.phi");
LLVMBasicBlockRef rhs_block = llvm_basic_block_new(c, op == BINARYOP_AND ? "and.rhs" : "or.rhs");
if (op == BINARYOP_AND)
{
@@ -2569,24 +2545,25 @@ static void gencontext_emit_logical_and_or(GenContext *c, BEValue *be_value, Exp
llvm_emit_expr(c, &rhs_value, exprptr(expr->binary_expr.right));
llvm_value_rvalue(c, &rhs_value);
LLVMBasicBlockRef end_block = c->current_block;
llvm_emit_br(c, phi_block);
LLVMBasicBlockRef rhs_end_block = llvm_get_current_block_if_in_use(c);
if (rhs_end_block)
{
llvm_emit_br(c, phi_block);
}
// Generate phi
llvm_emit_block(c, phi_block);
// Simplify for LLVM by entering the constants we already know of.
LLVMValueRef result_on_skip = LLVMConstInt(c->bool_type, op == BINARYOP_AND ? 0 : 1, 0);
// One possibility here is that a return happens inside of the expression.
if (!end_block)
if (!rhs_end_block)
{
llvm_value_set_bool(be_value, result_on_skip);
return;
}
LLVMValueRef phi = LLVMBuildPhi(c->builder, c->bool_type, "val");
LLVMValueRef logic_values[2] = { result_on_skip, rhs_value.value };
LLVMBasicBlockRef blocks[2] = { start_block, end_block };
LLVMBasicBlockRef blocks[2] = { lhs_end_block, rhs_end_block };
LLVMAddIncoming(phi, logic_values, blocks, 2);
llvm_value_set_bool(be_value, phi);
@@ -3016,21 +2993,22 @@ void llvm_emit_comp(GenContext *c, BEValue *result, BEValue *lhs, BEValue *rhs,
TODO
}
static void gencontext_emit_or_error(GenContext *c, BEValue *be_value, Expr *expr)
static void llvm_emit_else(GenContext *c, BEValue *be_value, Expr *expr)
{
LLVMBasicBlockRef else_block = llvm_basic_block_new(c, "else_block");
LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, "phi_block");
// Store catch/error var
// Store catch/opt var
PUSH_OPT();
// Set the catch/error var
// Set the catch/opt var
c->opt_var = NULL;
c->catch_block = else_block;
BEValue normal_value;
llvm_emit_exprid(c, &normal_value, expr->binary_expr.left);
llvm_value_rvalue(c, &normal_value);
// Emit the real value, this will cause an implicit jump to the else block on failure.
BEValue real_value;
llvm_emit_exprid(c, &real_value, expr->binary_expr.left);
llvm_value_rvalue(c, &real_value);
// Restore.
POP_OPT();
@@ -3038,66 +3016,65 @@ static void gencontext_emit_or_error(GenContext *c, BEValue *be_value, Expr *exp
// Emit success and jump to phi.
LLVMBasicBlockRef success_end_block = llvm_get_current_block_if_in_use(c);
// Only jump to phi if we didn't have an immediate jump. That would
// for example happen on "{| defer foo(); return Foo.ERR! |} ?? 123"
if (success_end_block) llvm_emit_br(c, phi_block);
// Emit else
llvm_emit_block(c, else_block);
// Emit the value here
BEValue else_value;
llvm_emit_exprid(c, &else_value, expr->binary_expr.right);
llvm_value_rvalue(c, &else_value);
LLVMBasicBlockRef else_block_exit = llvm_get_current_block_if_in_use(c);
// While the value may not be an optional, we may get a jump
// from this construction: foo() ?? (bar()?)
// In this case the else block is empty.
if (else_block_exit) llvm_emit_br(c, phi_block);
llvm_emit_block(c, phi_block);
if (!else_block_exit)
{
*be_value = normal_value;
*be_value = real_value;
return;
}
if (!success_end_block)
{
*be_value = else_value;
return;
}
if (expr->type->type_kind == TYPE_BOOL)
{
}
LLVMValueRef logic_values[2] = { normal_value.value, else_value.value };
LLVMValueRef logic_values[2] = { real_value.value, else_value.value };
LLVMBasicBlockRef blocks[2] = { success_end_block, else_block_exit };
if (expr->type->type_kind == TYPE_BOOL)
// Special handling of bool, since we need the "set_bool" function.
if (real_value.type == type_bool)
{
LLVMValueRef phi = LLVMBuildPhi(c->builder, c->bool_type, "val");
LLVMAddIncoming(phi, logic_values, blocks, 2);
llvm_value_set_bool(be_value, phi);
return;
}
else
{
LLVMValueRef phi = LLVMBuildPhi(c->builder, llvm_get_type(c, expr->type), "val");
LLVMAddIncoming(phi, logic_values, blocks, 2);
llvm_value_set(be_value, phi, expr->type);
}
LLVMValueRef phi = LLVMBuildPhi(c->builder, llvm_get_type(c, expr->type), "val");
LLVMAddIncoming(phi, logic_values, blocks, 2);
llvm_value_set(be_value, phi, expr->type);
}
void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValue *lhs_loaded, BinaryOp binary_op)
{
if (binary_op == BINARYOP_OR_ERR)
if (binary_op == BINARYOP_ELSE)
{
gencontext_emit_or_error(c, be_value, expr);
llvm_emit_else(c, be_value, expr);
return;
}
if (binary_op == BINARYOP_AND || binary_op == BINARYOP_OR)
{
gencontext_emit_logical_and_or(c, be_value, expr, binary_op);
llvm_emit_logical_and_or(c, be_value, expr, binary_op);
return;
}
BEValue lhs;
@@ -3132,7 +3109,7 @@ void gencontext_emit_binary(GenContext *c, BEValue *be_value, Expr *expr, BEValu
switch (binary_op)
{
case BINARYOP_ERROR:
case BINARYOP_OR_ERR:
case BINARYOP_ELSE:
UNREACHABLE
case BINARYOP_MULT:
if (is_float)
@@ -4999,23 +4976,24 @@ static inline void gencontext_emit_expression_list_expr(GenContext *context, BEV
}
}
static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value, Type *type, AstId current, BlockExit **block_exit)
static inline void llvm_emit_return_block(GenContext *c, BEValue *be_value, Type *type, AstId current, BlockExit **block_exit)
{
Type *type_lowered = type_lowering(type);
// First case - an empty block
if (!current)
{
llvm_value_set(be_value, NULL, type_void);
llvm_value_set(be_value, llvm_get_undef(c, type_lowered), type_lowered);
return;
}
Type *type_lowered = type_lowering(type);
LLVMValueRef old_ret_out = context->return_out;
context->in_block++;
LLVMValueRef old_ret_out = c->return_out;
c->in_block++;
LLVMValueRef error_out = context->opt_var;
LLVMBasicBlockRef error_block = context->catch_block;
LLVMValueRef error_out = c->opt_var;
LLVMBasicBlockRef error_block = c->catch_block;
LLVMValueRef return_out = NULL;
LLVMBasicBlockRef expr_block = llvm_basic_block_new(context, "expr_block.exit");
LLVMBasicBlockRef expr_block = llvm_basic_block_new(c, "expr_block.exit");
BlockExit exit = {
.block_return_exit = expr_block,
@@ -5028,16 +5006,16 @@ static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value
if (type_no_optional(type_lowered) != type_void)
{
exit.block_return_out = llvm_emit_alloca_aligned(context, type_lowered, "blockret");
exit.block_return_out = llvm_emit_alloca_aligned(c, type_lowered, "blockret");
}
context->opt_var = NULL;
context->catch_block = NULL;
c->opt_var = NULL;
c->catch_block = NULL;
// Process all but the last statement.
Ast *value = ast_next(&current);
while (value->next)
{
llvm_emit_stmt(context, value);
llvm_emit_stmt(c, value);
value = ast_next(&current);
}
@@ -5068,7 +5046,7 @@ static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value
if (IS_OPTIONAL(ret_expr)) break;
// Optimization, emit directly to value
llvm_emit_expr(context, be_value, ret_expr);
llvm_emit_expr(c, be_value, ret_expr);
// And remove the alloca
LLVMInstructionEraseFromParent(exit.block_return_out);
goto DONE;
@@ -5076,20 +5054,20 @@ static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value
} while (0);
// Emit the last statement
llvm_emit_stmt(context, value);
llvm_emit_stmt(c, value);
// In the case of a void with no return, then this may be true.
if (llvm_basic_block_is_unused(expr_block))
{
// Skip the expr block.
llvm_value_set(be_value, NULL, type_void);
llvm_value_set(be_value, llvm_get_undef(c, type_lowered), type_lowered);
goto DONE;
}
llvm_emit_br(context, expr_block);
llvm_emit_br(c, expr_block);
// Emit the exit block.
llvm_emit_block(context, expr_block);
llvm_emit_block(c, expr_block);
if (exit.block_return_out)
{
@@ -5101,10 +5079,10 @@ static inline void llvm_emit_return_block(GenContext *context, BEValue *be_value
}
DONE:
context->return_out = old_ret_out;
context->catch_block = error_block;
context->opt_var = error_out;
context->in_block--;
c->return_out = old_ret_out;
c->catch_block = error_block;
c->opt_var = error_out;
c->in_block--;
}

View File

@@ -300,6 +300,7 @@ INLINE AlignSize llvm_type_or_alloca_align(GenContext *c, LLVMValueRef dest, Typ
// -- Bitcast --
static inline LLVMValueRef llvm_emit_bitcast(GenContext *c, LLVMValueRef value, Type *type);
INLINE LLVMValueRef llvm_emit_bitcast_ptr(GenContext *c, LLVMValueRef value, Type *type);
INLINE LLVMValueRef llvm_zext_trunc(GenContext *c, LLVMValueRef data, LLVMTypeRef type);
// -- Constants --
void llvm_emit_typeid(GenContext *c, BEValue *be_value, Type *type);
@@ -365,7 +366,7 @@ void llvm_store_to_ptr_zero(GenContext *context, LLVMValueRef pointer, Type *typ
TypeSize llvm_store_size(GenContext *c, LLVMTypeRef type);
/// -- Aggregates --
static inline LLVMValueRef llvm_emit_insert_value(GenContext *c, LLVMValueRef agg, LLVMValueRef new_value, ArraySize index);
INLINE LLVMValueRef llvm_emit_insert_value(GenContext *c, LLVMValueRef agg, LLVMValueRef new_value, ArraySize index);
LLVMValueRef llvm_emit_aggregate_two(GenContext *c, Type *type, LLVMValueRef value1, LLVMValueRef value2);
LLVMValueRef llvm_emit_struct_gep_raw(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index,
unsigned struct_alignment, AlignSize *alignment);
@@ -375,6 +376,8 @@ LLVMValueRef llvm_emit_pointer_gep_raw(GenContext *c, LLVMTypeRef pointee_type,
LLVMValueRef llvm_emit_pointer_inbounds_gep_raw(GenContext *c, LLVMTypeRef pointee_type, LLVMValueRef ptr, LLVMValueRef offset);
void llvm_emit_ptr_from_array(GenContext *c, BEValue *value);
void llvm_emit_struct_member_ref(GenContext *c, BEValue *struct_ref, BEValue *member_ref, unsigned member_id);
INLINE LLVMValueRef llvm_emit_extract_value(GenContext *c, LLVMValueRef agg, unsigned index);
// -- Int operations ---
LLVMValueRef llvm_emit_shl_fixed(GenContext *c, LLVMValueRef data, int shift);

View File

@@ -1,4 +1,4 @@
static inline LLVMValueRef llvm_emit_insert_value(GenContext *c, LLVMValueRef agg, LLVMValueRef new_value, ArraySize index)
INLINE LLVMValueRef llvm_emit_insert_value(GenContext *c, LLVMValueRef agg, LLVMValueRef new_value, ArraySize index)
{
if (LLVMGetTypeKind(LLVMTypeOf(agg)) == LLVMVectorTypeKind)
{
@@ -8,6 +8,22 @@ static inline LLVMValueRef llvm_emit_insert_value(GenContext *c, LLVMValueRef ag
return LLVMBuildInsertValue(c->builder, agg, new_value, index, "");
}
INLINE LLVMValueRef llvm_zext_trunc(GenContext *c, LLVMValueRef data, LLVMTypeRef type)
{
LLVMTypeRef current_type = LLVMTypeOf(data);
if (current_type == type) return data;
assert(LLVMGetTypeKind(type) == LLVMIntegerTypeKind);
assert(LLVMGetTypeKind(current_type) == LLVMIntegerTypeKind);
if (llvm_bitsize(c, current_type) < llvm_bitsize(c, type))
{
return LLVMBuildZExt(c->builder, data, type, "zext");
}
assert(llvm_bitsize(c, current_type) > llvm_bitsize(c, type));
return LLVMBuildTrunc(c->builder, data, type, "ztrunc");
}
INLINE LLVMValueRef llvm_store_decl(GenContext *c, Decl *decl, BEValue *value)
{
BEValue ref;
@@ -94,6 +110,15 @@ INLINE LLVMValueRef llvm_emit_trunc(GenContext *c, LLVMValueRef value, Type *typ
return LLVMBuildTrunc(c->builder, value, llvm_get_type(c, type), "");
}
INLINE LLVMValueRef llvm_emit_extract_value(GenContext *c, LLVMValueRef agg, unsigned index)
{
if (LLVMGetTypeKind(LLVMTypeOf(agg)) == LLVMVectorTypeKind)
{
return LLVMBuildExtractElement(c->builder, agg, llvm_const_int(c, type_usize, index), "");
}
return LLVMBuildExtractValue(c->builder, agg, index, "");
}
INLINE bool llvm_use_debug(GenContext *context) { return context->debug.builder != NULL; }
INLINE bool llvm_basic_block_is_unused(LLVMBasicBlockRef block)

View File

@@ -739,7 +739,7 @@ Expr *recursive_may_narrow_float(Expr *expr, Type *type)
case BINARYOP_ADD:
case BINARYOP_DIV:
case BINARYOP_MOD:
case BINARYOP_OR_ERR:
case BINARYOP_ELSE:
{
Expr *res = recursive_may_narrow_float(exprptr(expr->binary_expr.left), type);
if (res) return res;
@@ -898,7 +898,7 @@ Expr *recursive_may_narrow_int(Expr *expr, Type *type)
case BINARYOP_BIT_OR:
case BINARYOP_BIT_XOR:
case BINARYOP_BIT_AND:
case BINARYOP_OR_ERR:
case BINARYOP_ELSE:
{
Expr *res = recursive_may_narrow_int(exprptr(expr->binary_expr.left), type);
if (res) return res;

View File

@@ -6657,11 +6657,16 @@ static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr)
type = type_is_optional_any(type) ? else_type : type->failable;
if (else_type->type_kind == TYPE_FAILABLE)
if (type_is_optional(else_type))
{
SEMA_ERROR(rhs, "The default value may not be a failable.");
SEMA_ERROR(rhs, "The default value may not be an optional.");
return false;
}
if (lhs->expr_kind == EXPR_FAILABLE)
{
expr_replace(expr, rhs);
return true;
}
Type *common = type_find_max_type(type, else_type);
if (!common)
{
@@ -6671,18 +6676,13 @@ static inline bool sema_expr_analyse_or_error(SemaContext *context, Expr *expr)
}
if (!cast_implicit(lhs, common)) return false;
if (!cast_implicit(rhs, common)) return false;
if (IS_OPTIONAL(rhs))
{
SEMA_ERROR(rhs, "The expression must be a non-failable.");
return false;
}
expr->type = common;
return true;
}
static inline bool sema_expr_analyse_binary(SemaContext *context, Expr *expr)
{
if (expr->binary_expr.operator == BINARYOP_OR_ERR) return sema_expr_analyse_or_error(context, expr);
if (expr->binary_expr.operator == BINARYOP_ELSE) return sema_expr_analyse_or_error(context, expr);
assert(expr->resolve_status == RESOLVE_RUNNING);
Expr *left = exprptr(expr->binary_expr.left);
Expr *right = exprptr(expr->binary_expr.right);
@@ -6694,7 +6694,7 @@ static inline bool sema_expr_analyse_binary(SemaContext *context, Expr *expr)
}
switch (expr->binary_expr.operator)
{
case BINARYOP_OR_ERR:
case BINARYOP_ELSE:
UNREACHABLE // Handled previously
case BINARYOP_ASSIGN:
return sema_expr_analyse_assign(context, expr, left, right);
@@ -8069,7 +8069,7 @@ void insert_widening_type(Expr *expr, Type *infer_type)
case BINARYOP_BIT_OR:
case BINARYOP_BIT_XOR:
case BINARYOP_BIT_AND:
case BINARYOP_OR_ERR:
case BINARYOP_ELSE:
if (!expr_is_simple(exprptr(expr->binary_expr.left)) || !expr_is_simple(exprptr(expr->binary_expr.right))) return;
expr->type = infer_type;
expr->binary_expr.widen = true;

View File

@@ -0,0 +1,13 @@
module foo;
fault Foo { ABC }
fn void test()
{
int x = Foo.ABC! ?? 123;
}
fn void test2()
{
int! y = Foo.ABC! ?? Foo.ABC!; // #error: The default value may not be an optional
}

View File

@@ -0,0 +1,200 @@
// #target: macos-x64
module foo;
import std::io;
fault Foo { ABC }
fn void blurb() { io::println("Blurb");}
macro int! tester()
{
defer blurb();
return Foo.ABC!;
}
fn void! test(int x)
{
io::printfln("test(%d)", x);
if (x || (tester()?)) io::println("Ok1");
io::println("Test next");
if (tester()? || x) io::println("Ok?");
io::println("Test ok");
}
fn void! test2(int x)
{
io::printfln("test2(%d)", x);
if (x && (tester()?)) io::println("Ok1");
io::println("Test next");
if ((tester()?) && x) io::println("Ok?");
io::println("Test ok");
}
fn void main()
{
anyerr a = test(0);
anyerr b = test(1);
anyerr c = test2(0);
anyerr d = test2(1);
}
/* #expect: foo.ll
define i64 @foo_test(i32 %0) #0 {
entry:
%retparam = alloca i64, align 8
%taddr = alloca %"char[]", align 8
%vararg = alloca %"variant[]", align 8
%varargslots = alloca [1 x %variant], align 16
%taddr1 = alloca i32, align 4
%error_var = alloca i64, align 8
%blockret = alloca i32, align 4
%error_var4 = alloca i64, align 8
%blockret5 = alloca i32, align 4
%reterr = alloca i64, align 8
store %"char[]" { i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.1, i32 0, i32 0), i64 8 }, %"char[]"* %taddr, align 8
%1 = bitcast %"char[]"* %taddr to { i8*, i64 }*
%2 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %1, i32 0, i32 0
%lo = load i8*, i8** %2, align 8
%3 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %1, i32 0, i32 1
%hi = load i64, i64* %3, align 8
store i32 %0, i32* %taddr1, align 4
%4 = bitcast i32* %taddr1 to i8*
%5 = insertvalue %variant undef, i8* %4, 0
%6 = insertvalue %variant %5, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
%7 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots, i64 0, i64 0
store %variant %6, %variant* %7, align 16
%8 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 1
store i64 1, i64* %8, align 8
%9 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 0
%10 = bitcast [1 x %variant]* %varargslots to %variant*
store %variant* %10, %variant** %9, align 8
%11 = bitcast %"variant[]"* %vararg to { i8*, i64 }*
%12 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %11, i32 0, i32 0
%lo2 = load i8*, i8** %12, align 8
%13 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %11, i32 0, i32 1
%hi3 = load i64, i64* %13, align 8
%14 = call i64 @std_io_printfln(i64* %retparam, i8* %lo, i64 %hi, i8* %lo2, i64 %hi3)
%not_err = icmp eq i64 %14, 0
br i1 %not_err, label %after_check, label %voiderr
after_check: ; preds = %entry
br label %voiderr
voiderr: ; preds = %after_check, %entry
%intbool = icmp ne i32 %0, 0
br i1 %intbool, label %or.phi, label %or.rhs
or.rhs: ; preds = %voiderr
store i64 ptrtoint (%.fault* @"foo_Foo$ABC" to i64), i64* %error_var, align 8
br label %opt_block_cleanup
opt_block_cleanup: ; preds = %or.rhs
call void @foo_blurb()
br label %guard_block
guard_block: ; preds = %opt_block_cleanup
%15 = load i64, i64* %error_var, align 8
ret i64 %15
or.phi: ; preds = %voiderr
br label %if.then
if.then: ; preds = %or.phi
%16 = call i32 @std_io_println(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i32 0, i32 0)) #1
br label %if.exit
if.exit: ; preds = %if.then
%17 = call i32 @std_io_println(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.3, i32 0, i32 0)) #1
store i64 ptrtoint (%.fault* @"foo_Foo$ABC" to i64), i64* %error_var4, align 8
br label %opt_block_cleanup6
opt_block_cleanup6: ; preds = %if.exit
call void @foo_blurb()
br label %guard_block7
guard_block7: ; preds = %opt_block_cleanup6
%18 = load i64, i64* %error_var4, align 8
ret i64 %18
if.exit9: ; No predecessors!
%19 = call i32 @std_io_println(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.5, i32 0, i32 0)) #1
ret i64 0
}
define i64 @foo_test2(i32 %0) #0 {
entry:
%retparam = alloca i64, align 8
%taddr = alloca %"char[]", align 8
%vararg = alloca %"variant[]", align 8
%varargslots = alloca [1 x %variant], align 16
%taddr1 = alloca i32, align 4
%error_var = alloca i64, align 8
%blockret = alloca i32, align 4
%error_var4 = alloca i64, align 8
%blockret5 = alloca i32, align 4
%reterr = alloca i64, align 8
store %"char[]" { i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.6, i32 0, i32 0), i64 9 }, %"char[]"* %taddr, align 8
%1 = bitcast %"char[]"* %taddr to { i8*, i64 }*
%2 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %1, i32 0, i32 0
%lo = load i8*, i8** %2, align 8
%3 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %1, i32 0, i32 1
%hi = load i64, i64* %3, align 8
store i32 %0, i32* %taddr1, align 4
%4 = bitcast i32* %taddr1 to i8*
%5 = insertvalue %variant undef, i8* %4, 0
%6 = insertvalue %variant %5, i64 ptrtoint (%.introspect* @"ct$int" to i64), 1
%7 = getelementptr inbounds [1 x %variant], [1 x %variant]* %varargslots, i64 0, i64 0
store %variant %6, %variant* %7, align 16
%8 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 1
store i64 1, i64* %8, align 8
%9 = getelementptr inbounds %"variant[]", %"variant[]"* %vararg, i32 0, i32 0
%10 = bitcast [1 x %variant]* %varargslots to %variant*
store %variant* %10, %variant** %9, align 8
%11 = bitcast %"variant[]"* %vararg to { i8*, i64 }*
%12 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %11, i32 0, i32 0
%lo2 = load i8*, i8** %12, align 8
%13 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %11, i32 0, i32 1
%hi3 = load i64, i64* %13, align 8
%14 = call i64 @std_io_printfln(i64* %retparam, i8* %lo, i64 %hi, i8* %lo2, i64 %hi3)
%not_err = icmp eq i64 %14, 0
br i1 %not_err, label %after_check, label %voiderr
after_check: ; preds = %entry
br label %voiderr
voiderr: ; preds = %after_check, %entry
%intbool = icmp ne i32 %0, 0
br i1 %intbool, label %and.rhs, label %and.phi
and.rhs: ; preds = %voiderr
store i64 ptrtoint (%.fault* @"foo_Foo$ABC" to i64), i64* %error_var, align 8
br label %opt_block_cleanup
opt_block_cleanup: ; preds = %and.rhs
call void @foo_blurb()
br label %guard_block
guard_block: ; preds = %opt_block_cleanup
%15 = load i64, i64* %error_var, align 8
ret i64 %15
and.phi: ; preds = %voiderr
br label %if.exit
if.exit: ; preds = %and.phi
%16 = call i32 @std_io_println(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.7, i32 0, i32 0)) #1
store i64 ptrtoint (%.fault* @"foo_Foo$ABC" to i64), i64* %error_var4, align 8
br label %opt_block_cleanup6
opt_block_cleanup6: ; preds = %if.exit
call void @foo_blurb()
br label %guard_block7
guard_block7: ; preds = %opt_block_cleanup6
%17 = load i64, i64* %error_var4, align 8
ret i64 %17
if.exit8: ; No predecessors!
%18 = call i32 @std_io_println(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.8, i32 0, i32 0)) #1
ret i64 0
}

View File

@@ -0,0 +1,13 @@
module foo;
fault Foo { ABC }
fn void test()
{
int x = Foo.ABC! ?? 123;
}
fn void test2()
{
int! y = Foo.ABC! ?? Foo.ABC!; // #error: The default value may not be an optional
}

View File

@@ -0,0 +1,192 @@
// #target: macos-x64
module foo;
import std::io;
fault Foo { ABC }
fn void blurb() { io::println("Blurb");}
macro int! tester()
{
defer blurb();
return Foo.ABC!;
}
fn void! test(int x)
{
io::printfln("test(%d)", x);
if (x || (tester()?)) io::println("Ok1");
io::println("Test next");
if (tester()? || x) io::println("Ok?");
io::println("Test ok");
}
fn void! test2(int x)
{
io::printfln("test2(%d)", x);
if (x && (tester()?)) io::println("Ok1");
io::println("Test next");
if ((tester()?) && x) io::println("Ok?");
io::println("Test ok");
}
fn void main()
{
anyerr a = test(0);
anyerr b = test(1);
anyerr c = test2(0);
anyerr d = test2(1);
}
/* #expect: foo.ll
define i64 @foo_test(i32 %0) #0 {
entry:
%retparam = alloca i64, align 8
%taddr = alloca %"char[]", align 8
%vararg = alloca %"variant[]", align 8
%varargslots = alloca [1 x %variant], align 16
%taddr1 = alloca i32, align 4
%error_var = alloca i64, align 8
%blockret = alloca i32, align 4
%error_var4 = alloca i64, align 8
%blockret5 = alloca i32, align 4
%reterr = alloca i64, align 8
store %"char[]" { ptr @.str.1, i64 8 }, ptr %taddr, align 8
%1 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 0
%lo = load ptr, ptr %1, align 8
%2 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 1
%hi = load i64, ptr %2, align 8
store i32 %0, ptr %taddr1, align 4
%3 = insertvalue %variant undef, ptr %taddr1, 0
%4 = insertvalue %variant %3, i64 ptrtoint (ptr @"ct$int" to i64), 1
%5 = getelementptr inbounds [1 x %variant], ptr %varargslots, i64 0, i64 0
store %variant %4, ptr %5, align 16
%6 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 1
store i64 1, ptr %6, align 8
%7 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 0
store ptr %varargslots, ptr %7, align 8
%8 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 0
%lo2 = load ptr, ptr %8, align 8
%9 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 1
%hi3 = load i64, ptr %9, align 8
%10 = call i64 @std_io_printfln(ptr %retparam, ptr %lo, i64 %hi, ptr %lo2, i64 %hi3)
%not_err = icmp eq i64 %10, 0
br i1 %not_err, label %after_check, label %voiderr
after_check: ; preds = %entry
br label %voiderr
voiderr: ; preds = %after_check, %entry
%intbool = icmp ne i32 %0, 0
br i1 %intbool, label %or.phi, label %or.rhs
or.rhs: ; preds = %voiderr
store i64 ptrtoint (ptr @"foo_Foo$ABC" to i64), ptr %error_var, align 8
br label %opt_block_cleanup
opt_block_cleanup: ; preds = %or.rhs
call void @foo_blurb()
br label %guard_block
guard_block: ; preds = %opt_block_cleanup
%11 = load i64, ptr %error_var, align 8
ret i64 %11
or.phi: ; preds = %voiderr
br label %if.then
if.then: ; preds = %or.phi
%12 = call i32 @std_io_println(ptr @.str.2) #1
br label %if.exit
if.exit: ; preds = %if.then
%13 = call i32 @std_io_println(ptr @.str.3) #1
store i64 ptrtoint (ptr @"foo_Foo$ABC" to i64), ptr %error_var4, align 8
br label %opt_block_cleanup6
opt_block_cleanup6: ; preds = %if.exit
call void @foo_blurb()
br label %guard_block7
guard_block7: ; preds = %opt_block_cleanup6
%14 = load i64, ptr %error_var4, align 8
ret i64 %14
if.exit9: ; No predecessors!
%15 = call i32 @std_io_println(ptr @.str.5) #1
ret i64 0
}
define i64 @foo_test2(i32 %0) #0 {
entry:
%retparam = alloca i64, align 8
%taddr = alloca %"char[]", align 8
%vararg = alloca %"variant[]", align 8
%varargslots = alloca [1 x %variant], align 16
%taddr1 = alloca i32, align 4
%error_var = alloca i64, align 8
%blockret = alloca i32, align 4
%error_var4 = alloca i64, align 8
%blockret5 = alloca i32, align 4
%reterr = alloca i64, align 8
store %"char[]" { ptr @.str.6, i64 9 }, ptr %taddr, align 8
%1 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 0
%lo = load ptr, ptr %1, align 8
%2 = getelementptr inbounds { ptr, i64 }, ptr %taddr, i32 0, i32 1
%hi = load i64, ptr %2, align 8
store i32 %0, ptr %taddr1, align 4
%3 = insertvalue %variant undef, ptr %taddr1, 0
%4 = insertvalue %variant %3, i64 ptrtoint (ptr @"ct$int" to i64), 1
%5 = getelementptr inbounds [1 x %variant], ptr %varargslots, i64 0, i64 0
store %variant %4, ptr %5, align 16
%6 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 1
store i64 1, ptr %6, align 8
%7 = getelementptr inbounds %"variant[]", ptr %vararg, i32 0, i32 0
store ptr %varargslots, ptr %7, align 8
%8 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 0
%lo2 = load ptr, ptr %8, align 8
%9 = getelementptr inbounds { ptr, i64 }, ptr %vararg, i32 0, i32 1
%hi3 = load i64, ptr %9, align 8
%10 = call i64 @std_io_printfln(ptr %retparam, ptr %lo, i64 %hi, ptr %lo2, i64 %hi3)
%not_err = icmp eq i64 %10, 0
br i1 %not_err, label %after_check, label %voiderr
after_check: ; preds = %entry
br label %voiderr
voiderr: ; preds = %after_check, %entry
%intbool = icmp ne i32 %0, 0
br i1 %intbool, label %and.rhs, label %and.phi
and.rhs: ; preds = %voiderr
store i64 ptrtoint (ptr @"foo_Foo$ABC" to i64), ptr %error_var, align 8
br label %opt_block_cleanup
opt_block_cleanup: ; preds = %and.rhs
call void @foo_blurb()
br label %guard_block
guard_block: ; preds = %opt_block_cleanup
%11 = load i64, ptr %error_var, align 8
ret i64 %11
and.phi: ; preds = %voiderr
br label %if.exit
if.exit: ; preds = %and.phi
%12 = call i32 @std_io_println(ptr @.str.7) #1
store i64 ptrtoint (ptr @"foo_Foo$ABC" to i64), ptr %error_var4, align 8
br label %opt_block_cleanup6
opt_block_cleanup6: ; preds = %if.exit
call void @foo_blurb()
br label %guard_block7
guard_block7: ; preds = %opt_block_cleanup6
%13 = load i64, ptr %error_var4, align 8
ret i64 %13
if.exit8: ; No predecessors!
%14 = call i32 @std_io_println(ptr @.str.8) #1
ret i64 0
}