diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 856aa6cc0..9dcbd07e9 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -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, diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 01b95d61b..26f9ebfb4 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -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, diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index a64179a3a..29bcebbb6 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -5,7 +5,7 @@ #include "llvm_codegen_internal.h" #include -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(¤t); while (value->next) { - llvm_emit_stmt(context, value); + llvm_emit_stmt(c, value); value = ast_next(¤t); } @@ -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--; } diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 198881649..664335d5e 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -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); diff --git a/src/compiler/llvm_codegen_internal_impl.h b/src/compiler/llvm_codegen_internal_impl.h index c25721f15..b9b61eece 100644 --- a/src/compiler/llvm_codegen_internal_impl.h +++ b/src/compiler/llvm_codegen_internal_impl.h @@ -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) diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 34cd568bf..3e8712e9a 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -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; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 7361b1c78..1f25f211b 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -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; diff --git a/test/test_suite/errors/more_optional_tests.c3 b/test/test_suite/errors/more_optional_tests.c3 new file mode 100644 index 000000000..7645851d1 --- /dev/null +++ b/test/test_suite/errors/more_optional_tests.c3 @@ -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 +} \ No newline at end of file diff --git a/test/test_suite/errors/failable_chained_init.c3t b/test/test_suite/errors/optional_chained_init.c3t similarity index 100% rename from test/test_suite/errors/failable_chained_init.c3t rename to test/test_suite/errors/optional_chained_init.c3t diff --git a/test/test_suite/errors/failable_inits.c3t b/test/test_suite/errors/optional_inits.c3t similarity index 100% rename from test/test_suite/errors/failable_inits.c3t rename to test/test_suite/errors/optional_inits.c3t diff --git a/test/test_suite/errors/failable_taddr_and_access.c3t b/test/test_suite/errors/optional_taddr_and_access.c3t similarity index 100% rename from test/test_suite/errors/failable_taddr_and_access.c3t rename to test/test_suite/errors/optional_taddr_and_access.c3t diff --git a/test/test_suite/errors/failable_untyped_list.c3 b/test/test_suite/errors/optional_untyped_list.c3 similarity index 100% rename from test/test_suite/errors/failable_untyped_list.c3 rename to test/test_suite/errors/optional_untyped_list.c3 diff --git a/test/test_suite/errors/or_and_rethrow.c3t b/test/test_suite/errors/or_and_rethrow.c3t new file mode 100644 index 000000000..46e933c45 --- /dev/null +++ b/test/test_suite/errors/or_and_rethrow.c3t @@ -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 +} diff --git a/test/test_suite2/failable_catch.c3t b/test/test_suite2/errors/failable_catch.c3t similarity index 100% rename from test/test_suite2/failable_catch.c3t rename to test/test_suite2/errors/failable_catch.c3t diff --git a/test/test_suite2/errors/illegal_use_of_failable.c3 b/test/test_suite2/errors/illegal_use_of_optional.c3 similarity index 100% rename from test/test_suite2/errors/illegal_use_of_failable.c3 rename to test/test_suite2/errors/illegal_use_of_optional.c3 diff --git a/test/test_suite2/errors/more_optional_tests.c3 b/test/test_suite2/errors/more_optional_tests.c3 new file mode 100644 index 000000000..7645851d1 --- /dev/null +++ b/test/test_suite2/errors/more_optional_tests.c3 @@ -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 +} \ No newline at end of file diff --git a/test/test_suite2/errors/failable_chained_init.c3t b/test/test_suite2/errors/optional_chained_init.c3t similarity index 100% rename from test/test_suite2/errors/failable_chained_init.c3t rename to test/test_suite2/errors/optional_chained_init.c3t diff --git a/test/test_suite2/errors/failable_inits.c3t b/test/test_suite2/errors/optional_inits.c3t similarity index 100% rename from test/test_suite2/errors/failable_inits.c3t rename to test/test_suite2/errors/optional_inits.c3t diff --git a/test/test_suite2/errors/failable_taddr_and_access.c3t b/test/test_suite2/errors/optional_taddr_and_access.c3t similarity index 100% rename from test/test_suite2/errors/failable_taddr_and_access.c3t rename to test/test_suite2/errors/optional_taddr_and_access.c3t diff --git a/test/test_suite2/errors/failable_untyped_list.c3 b/test/test_suite2/errors/optional_untyped_list.c3 similarity index 100% rename from test/test_suite2/errors/failable_untyped_list.c3 rename to test/test_suite2/errors/optional_untyped_list.c3 diff --git a/test/test_suite2/errors/or_and_rethrow.c3t b/test/test_suite2/errors/or_and_rethrow.c3t new file mode 100644 index 000000000..4c63392da --- /dev/null +++ b/test/test_suite2/errors/or_and_rethrow.c3t @@ -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 +}