From bb9e84d3293ac78de7a310046021af08725c14c5 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 31 Aug 2021 18:12:30 +0200 Subject: [PATCH] Make errors usable from other units. Order subarray elements correctly. Correct subarray -> pointer cast. Prevent unwrapping of catch in switch. --- resources/lib/std/io.c3 | 4 + src/compiler/llvm_codegen.c | 38 ++- src/compiler/llvm_codegen_c_abi_x64.c | 4 +- src/compiler/llvm_codegen_expr.c | 13 +- src/compiler/llvm_codegen_function.c | 3 + src/compiler/llvm_codegen_internal.h | 1 + src/compiler/sema_expr.c | 64 ++--- src/compiler/sema_stmts.c | 24 +- test/test_suite/from_docs/examples_defer.c3t | 89 +++++++ .../from_docs/examples_forswitch.c3t | 245 ++++++++++++++++++ .../from_docs/examples_functionpointer.c3t | 37 +++ .../from_docs/examples_if_catch.c3t | 176 +++++++++++++ test/test_suite/from_docs/examples_struct.c3 | 46 ++++ test/test_suite/functions/splat.c3t | 49 ++-- test/test_suite/functions/test_regression.c3t | 154 ++++++----- test/test_suite/statements/switch_errors.c3 | 24 ++ 16 files changed, 820 insertions(+), 151 deletions(-) create mode 100644 test/test_suite/from_docs/examples_defer.c3t create mode 100644 test/test_suite/from_docs/examples_forswitch.c3t create mode 100644 test/test_suite/from_docs/examples_functionpointer.c3t create mode 100644 test/test_suite/from_docs/examples_if_catch.c3t create mode 100644 test/test_suite/from_docs/examples_struct.c3 diff --git a/resources/lib/std/io.c3 b/resources/lib/std/io.c3 index da6579448..e92568d84 100644 --- a/resources/lib/std/io.c3 +++ b/resources/lib/std/io.c3 @@ -1,5 +1,9 @@ module std::io; +errtype IoError +{ + FILE_NOT_FOUND +} /* extern File *stdin @cname(__stdinp); extern File *stdout @cname(__stdoutp); diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 5090c7fd3..9d3bbf499 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -714,6 +714,29 @@ void llvm_codegen_setup() intrinsics_setup = true; } +static void llvm_set_linkage(GenContext *c, Decl *decl, LLVMValueRef value) +{ + if (decl->module != c->code_module) + { + LLVMSetLinkage(value, LLVMLinkOnceODRLinkage); + LLVMSetVisibility(value, LLVMDefaultVisibility); + return; + } + switch (decl->visibility) + { + case VISIBLE_MODULE: + case VISIBLE_PUBLIC: + LLVMSetLinkage(value, LLVMLinkOnceODRLinkage); + LLVMSetVisibility(value, LLVMDefaultVisibility); + break; + case VISIBLE_EXTERN: + case VISIBLE_LOCAL: + LLVMSetVisibility(value, LLVMHiddenVisibility); + LLVMSetLinkage(value, LLVMLinkerPrivateLinkage); + break; + } + +} void gencontext_emit_introspection_type(GenContext *c, Decl *decl) { llvm_get_type(c, decl->type); @@ -739,6 +762,7 @@ void gencontext_emit_introspection_type(GenContext *c, Decl *decl) scratch_buffer_append("$elements"); LLVMValueRef enum_elements = LLVMAddGlobal(c->module, elements_type, scratch_buffer_to_string()); LLVMSetGlobalConstant(enum_elements, 1); + llvm_set_linkage(c, decl, enum_elements); LLVMSetInitializer(enum_elements, LLVMConstNull(elements_type)); for (unsigned i = 0; i < elements; i++) { @@ -750,20 +774,8 @@ void gencontext_emit_introspection_type(GenContext *c, Decl *decl) LLVMSetGlobalConstant(global_name, 1); LLVMSetInitializer(global_name, LLVMConstInt(llvm_get_type(c, type_char), 1, false)); decl->type->backend_typeid = LLVMConstPointerCast(global_name, llvm_get_type(c, type_typeid)); + llvm_set_linkage(c, decl, global_name); - switch (decl->visibility) - { - case VISIBLE_MODULE: - case VISIBLE_PUBLIC: - LLVMSetLinkage(global_name, LLVMLinkOnceODRLinkage); - LLVMSetVisibility(global_name, LLVMDefaultVisibility); - break; - case VISIBLE_EXTERN: - case VISIBLE_LOCAL: - LLVMSetVisibility(global_name, LLVMHiddenVisibility); - LLVMSetLinkage(global_name, LLVMLinkerPrivateLinkage); - break; - } } diff --git a/src/compiler/llvm_codegen_c_abi_x64.c b/src/compiler/llvm_codegen_c_abi_x64.c index 84a2b9c6a..7698b9d54 100644 --- a/src/compiler/llvm_codegen_c_abi_x64.c +++ b/src/compiler/llvm_codegen_c_abi_x64.c @@ -578,8 +578,8 @@ AbiType *x64_get_int_type_at_offset(Type *type, unsigned offset, Type *source_ty if (offset < 16) return abi_type_new_plain(type_voidptr); break; case TYPE_SUBARRAY: - if (offset < 8) return abi_type_new_plain(type_ulong); - if (offset < 16) return abi_type_new_plain(type_voidptr); + if (offset < 8) return abi_type_new_plain(type_voidptr); + if (offset < 16) return abi_type_new_plain(type_ulong); break; case TYPE_ARRAY: { diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 0c4e0c09a..bd5b4ced1 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -578,12 +578,10 @@ void llvm_emit_cast(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_ llvm_emit_arr_to_subarray_cast(c, value, to_type); break; case CAST_SAPTR: + llvm_value_fold_failable(c, value); if (llvm_value_is_addr(value)) { - llvm_value_fold_failable(c, value); - value->value = llvm_emit_load_aligned(c, llvm_get_type(c, to_type), - LLVMBuildStructGEP(c->builder, value->value, 0, ""), - value->alignment, ""); + value->value = LLVMBuildStructGEP(c->builder, value->value, 0, ""); } else { @@ -2675,6 +2673,7 @@ static void llvm_emit_const_expr(GenContext *c, BEValue *be_value, Expr *expr) case CONST_ERR: { Decl *decl = expr->const_expr.err_val; + LLVMValueRef value; if (decl) { @@ -2796,9 +2795,9 @@ void llvm_emit_subarray_pointer(GenContext *c, BEValue *subarray, BEValue *point { llvm_value_addr(c, subarray); unsigned alignment = 0; - LLVMValueRef len_addr = llvm_emit_struct_gep_raw(c, subarray->value, llvm_get_type(c, subarray->type), 0, subarray->alignment, - 0, &alignment); - llvm_value_set_address_align(pointer, len_addr, type_get_ptr(subarray->type->array.base), alignment); + LLVMValueRef pointer_addr = llvm_emit_struct_gep_raw(c, subarray->value, llvm_get_type(c, subarray->type), 0, subarray->alignment, + 0, &alignment); + llvm_value_set_address_align(pointer, pointer_addr, type_get_ptr(subarray->type->array.base), alignment); } diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 7d65aa58f..0c3c9ad41 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -647,7 +647,10 @@ void llvm_emit_extern_decl(GenContext *context, Decl *decl) // TODO // Fix typeid break; case DECL_ENUM: + llvm_emit_methods(context, decl->methods); + break; case DECL_ERRTYPE: + gencontext_emit_introspection_type(context, decl); llvm_emit_methods(context, decl->methods); // TODO // Fix typeid return; diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 72c526fce..a8102066c 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -225,6 +225,7 @@ void llvm_emit_convert_value_from_coerced(GenContext *c, BEValue *result, LLVMTy void llvm_emit_coerce_store(GenContext *c, LLVMValueRef addr, AlignSize alignment, LLVMTypeRef coerced, LLVMValueRef value, LLVMTypeRef target_type); void llvm_emit_function_body(GenContext *context, Decl *decl); void llvm_emit_function_decl(GenContext *c, Decl *decl); +void gencontext_emit_introspection_type(GenContext *c, Decl *decl); LLVMValueRef llvm_emit_call_intrinsic(GenContext *c, unsigned intrinsic_id, LLVMTypeRef *types, unsigned type_count, LLVMValueRef *values, unsigned arg_count); void llvm_emit_cast(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_type, Type *from_type); void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index d267e8123..88fe7804b 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -2240,43 +2240,45 @@ static inline bool sema_expr_analyse_type_access(Context *context, Expr *expr, T } break; case DECL_ERRTYPE: - if (type == TOKEN_CONST_IDENT) + context_register_external_symbol(context, decl); + + if (type == TOKEN_CONST_IDENT) + { + if (!sema_expr_analyse_enum_constant(expr, identifier_token, decl)) { - if (!sema_expr_analyse_enum_constant(expr, identifier_token, decl)) - { - SEMA_ERROR(expr, "'%s' has no error value '%s'.", decl->name, name); - return false; - } + SEMA_ERROR(expr, "'%s' has no error value '%s'.", decl->name, name); + return false; + } + return true; + } + if (name == kw_elements) + { + expr_rewrite_to_int_const(expr, type_compint, vec_size(decl->enums.values)); + return true; + } + if (name == kw_max) + { + Expr *max = enum_minmax_value(decl, BINARYOP_GT); + if (!max) + { + expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0); return true; } - if (name == kw_elements) + expr_replace(expr, max); + return true; + } + if (name == kw_min) + { + Expr *min = enum_minmax_value(decl, BINARYOP_LT); + if (!min) { - expr_rewrite_to_int_const(expr, type_compint, vec_size(decl->enums.values)); + expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0); return true; } - if (name == kw_max) - { - Expr *max = enum_minmax_value(decl, BINARYOP_GT); - if (!max) - { - expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0); - return true; - } - expr_replace(expr, max); - return true; - } - if (name == kw_min) - { - Expr *min = enum_minmax_value(decl, BINARYOP_LT); - if (!min) - { - expr_rewrite_to_int_const(expr, decl->enums.type_info->type->canonical, 0); - return true; - } - expr_replace(expr, min); - return true; - } - break; + expr_replace(expr, min); + return true; + } + break; case DECL_UNION: case DECL_STRUCT: case DECL_DISTINCT: diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 420d31dfe..cd5ec7531 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -395,13 +395,23 @@ static void sema_remove_unwraps_from_try(Context *c, Expr *cond) } } -static inline bool sema_analyse_last_cond(Context *context, Expr *expr) +static inline bool sema_analyse_last_cond(Context *context, Expr *expr, bool may_unwrap) { switch (expr->expr_kind) { case EXPR_TRY_UNWRAP_CHAIN: + if (!may_unwrap) + { + SEMA_ERROR(expr, "Try unwrapping is only allowed inside of a 'while' or 'if' conditional."); + return false; + } return sema_analyse_try_unwrap_chain(context, expr); case EXPR_CATCH_UNWRAP: + if (!may_unwrap) + { + SEMA_ERROR(expr, "Catch unwrapping is only allowed inside of a 'while' or 'if' conditional, maybe catch(...) will do what you need?"); + return false; + } return sema_analyse_catch_unwrap(context, expr); default: return sema_analyse_expr(context, NULL, expr); @@ -435,7 +445,7 @@ static inline bool sema_analyse_cond_list(Context *context, Expr *expr, bool may if (!sema_analyse_expr(context, NULL, dexprs[i])) return false; } - if (!sema_analyse_last_cond(context, dexprs[entries - 1])) return false; + if (!sema_analyse_last_cond(context, dexprs[entries - 1], may_unwrap)) return false; expr_set_type(expr, dexprs[entries - 1]->type); expr->resolve_status = RESOLVE_DONE; @@ -454,12 +464,12 @@ static inline bool sema_analyse_cond_list(Context *context, Expr *expr, bool may * @param cast_to_bool if the result is to be cast to bool after * @return true if it passes analysis. */ -static inline bool sema_analyse_cond(Context *context, Expr *expr, bool cast_to_bool) +static inline bool sema_analyse_cond(Context *context, Expr *expr, bool cast_to_bool, bool may_unwrap) { assert(expr->expr_kind == EXPR_COND && "Conditional expressions should always be of type EXPR_DECL_LIST"); // 1. Analyse the declaration list. - if (!sema_analyse_cond_list(context, expr, true)) return false; + if (!sema_analyse_cond_list(context, expr, may_unwrap)) return false; // 2. If we get "void", either through a void call or an empty list, // signal that. @@ -537,7 +547,7 @@ static inline bool sema_analyse_while_stmt(Context *context, Ast *statement) SCOPE_START_WITH_LABEL(statement->while_stmt.flow.label) // 2. Analyze the condition - if (!sema_analyse_cond(context, cond, true)) + if (!sema_analyse_cond(context, cond, true, true)) { // 2a. In case of error, pop context and exit. return SCOPE_POP_ERROR(); @@ -1320,7 +1330,7 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) Expr *cond = statement->if_stmt.cond; SCOPE_OUTER_START bool cast_to_bool = statement->if_stmt.then_body->ast_kind != AST_IF_CATCH_SWITCH_STMT; - success = sema_analyse_cond(context, cond, cast_to_bool); + success = sema_analyse_cond(context, cond, cast_to_bool, true); Ast *then = statement->if_stmt.then_body; bool then_has_braces = then->ast_kind == AST_COMPOUND_STMT || then->ast_kind == AST_IF_CATCH_SWITCH_STMT; @@ -1966,7 +1976,7 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement) if (statement->ast_kind == AST_SWITCH_STMT) { - if (!sema_analyse_cond(context, cond, false)) return false; + if (!sema_analyse_cond(context, cond, false, false)) return false; switch_type = VECLAST(cond->cond_expr)->type->canonical; } else diff --git a/test/test_suite/from_docs/examples_defer.c3t b/test/test_suite/from_docs/examples_defer.c3t new file mode 100644 index 000000000..eb6c1838d --- /dev/null +++ b/test/test_suite/from_docs/examples_defer.c3t @@ -0,0 +1,89 @@ +// #target: x64-darwin +module defer1; +import std::io; + +func void test(int x) +{ + defer io::println(); + defer io::print("A"); + if (x == 1) return; + { + defer io::print("B"); + if (x == 0) return; + } + io::print("!"); +} + +func void main() +{ + test(1); // Prints "A" + test(0); // Prints "BA" + test(10); // Prints "B!A" +} + +// #expect: defer1.ll + +define void @defer1.test(i32 %0) #0 { +entry: + %x = alloca i32, align 4 + store i32 %0, i32* %x, align 4 + %1 = load i32, i32* %x, align 4 + %eq = icmp eq i32 %1, 1 + br i1 %eq, label %if.then, label %if.exit + +if.then: ; preds = %entry + %2 = call i32 @"std::io.print"(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str, i32 0, i32 0)) + br label %exit + +exit: ; preds = %if.then + %3 = call i32 @"std::io.println"(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str.1, i32 0, i32 0)) #1 + br label %exit1 + +exit1: ; preds = %exit + ret void + +if.exit: ; preds = %entry + %4 = load i32, i32* %x, align 4 + %eq2 = icmp eq i32 %4, 0 + br i1 %eq2, label %if.then3, label %if.exit7 + +if.then3: ; preds = %if.exit + %5 = call i32 @"std::io.print"(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.2, i32 0, i32 0)) + br label %exit4 + +exit4: ; preds = %if.then3 + %6 = call i32 @"std::io.print"(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.3, i32 0, i32 0)) + br label %exit5 + +exit5: ; preds = %exit4 + %7 = call i32 @"std::io.println"(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str.4, i32 0, i32 0)) #1 + br label %exit6 + +exit6: ; preds = %exit5 + ret void + +if.exit7: ; preds = %if.exit + %8 = call i32 @"std::io.print"(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.5, i32 0, i32 0)) + br label %exit8 + +exit8: ; preds = %if.exit7 + %9 = call i32 @"std::io.print"(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.6, i32 0, i32 0)) + %10 = call i32 @"std::io.print"(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.7, i32 0, i32 0)) + br label %exit9 + +exit9: ; preds = %exit8 + %11 = call i32 @"std::io.println"(i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str.8, i32 0, i32 0)) #1 + br label %exit10 + +exit10: ; preds = %exit9 + ret void +} + +; Function Attrs: nounwind +define void @main() #0 { +entry: + call void @defer1.test(i32 1) + call void @defer1.test(i32 0) + call void @defer1.test(i32 10) + ret void +} \ No newline at end of file diff --git a/test/test_suite/from_docs/examples_forswitch.c3t b/test/test_suite/from_docs/examples_forswitch.c3t new file mode 100644 index 000000000..dba521893 --- /dev/null +++ b/test/test_suite/from_docs/examples_forswitch.c3t @@ -0,0 +1,245 @@ +// #target: x64-darwin +module examples; + +import std::io; + +func void example_for() +{ + // the for-loop is the same as C99. + for (int i = 0; i < 10; i++) + { + io::printf("%d\n", i); + } + + // also equal + for (;;) + { + // .. + } +} + +enum Height : uint +{ + LOW = 0, + MEDIUM, + HIGH, +} + +func void demo_enum(Height h) +{ + switch (h) + { + case LOW: + case MEDIUM: + io::println("Not high"); + // Implicit break. + case HIGH: + io::println("High"); + } + + // This also works + switch (h) + { + case LOW: + case MEDIUM: + io::println("Not high"); + // Implicit break. + case Height.HIGH: + io::println("High"); + } + + // Completely empty cases are not allowed. + switch (h) + { + case LOW: + break; // Explicit break required, since switches can't be empty. + case MEDIUM: + io::println("Medium"); + case HIGH: + break; + } + + // special checking of switching on enum types + switch (h) + { + case LOW: + case MEDIUM: + case HIGH: + break; + default: // warning: default label in switch which covers all enumeration value + break; + } + + // Using "nextcase" will fallthrough to the next case statement, + // and each case statement starts its own scope. + switch (h) + { + case LOW: + int a = 1; + io::println("A"); + nextcase; + case MEDIUM: + int a = 2; + io::println("B"); + nextcase; + case HIGH: + // a is not defined here + io::println("C"); + } +} +// #expect: examples.ll + +define void @examples.example_for() #0 { +entry: + %i = alloca i32, align 4 + store i32 0, i32* %i, align 4 + br label %for.cond + +for.cond: ; preds = %for.inc, %entry + %0 = load i32, i32* %i, align 4 + %lt = icmp slt i32 %0, 10 + br i1 %lt, label %for.body, label %for.exit + +for.body: ; preds = %for.cond + %1 = load i32, i32* %i, align 4 + %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %1) + br label %for.inc + +for.inc: ; preds = %for.body + %3 = load i32, i32* %i, align 4 + %add = add i32 %3, 1 + store i32 %add, i32* %i, align 4 + br label %for.cond + +for.exit: ; preds = %for.cond + br label %infiniteloop + +infiniteloop: ; preds = %infiniteloop, %for.exit + br label %infiniteloop +} + +; Function Attrs: nounwind +define void @examples.demo_enum(i32 %0) #0 { +entry: + %h = alloca i32, align 4 + %switch = alloca i32, align 4 + %switch2 = alloca i32, align 4 + %switch7 = alloca i32, align 4 + %switch13 = alloca i32, align 4 + %switch17 = alloca i32, align 4 + %a = alloca i32, align 4 + %a21 = alloca i32, align 4 + store i32 %0, i32* %h, align 4 + %1 = load i32, i32* %h, align 4 + store i32 %1, i32* %switch, align 4 + br label %switch.entry + +switch.entry: ; preds = %entry + %2 = load i32, i32* %switch, align 4 + switch i32 %2, label %switch.exit [ + i32 0, label %switch.case + i32 1, label %switch.case + i32 2, label %switch.case1 + ] + +switch.case: ; preds = %switch.entry, %switch.entry + %3 = call i32 @"std::io.println"(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.1, i32 0, i32 0)) #1 + br label %switch.exit + +switch.case1: ; preds = %switch.entry + %4 = call i32 @"std::io.println"(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.2, i32 0, i32 0)) #1 + br label %switch.exit + +switch.exit: ; preds = %switch.case1, %switch.case, %switch.entry + %5 = load i32, i32* %h, align 4 + store i32 %5, i32* %switch2, align 4 + br label %switch.entry3 + +switch.entry3: ; preds = %switch.exit + %6 = load i32, i32* %switch2, align 4 + switch i32 %6, label %switch.exit6 [ + i32 0, label %switch.case4 + i32 1, label %switch.case4 + i32 2, label %switch.case5 + ] + +switch.case4: ; preds = %switch.entry3, %switch.entry3 + %7 = call i32 @"std::io.println"(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.3, i32 0, i32 0)) #1 + br label %switch.exit6 + +switch.case5: ; preds = %switch.entry3 + %8 = call i32 @"std::io.println"(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.4, i32 0, i32 0)) #1 + br label %switch.exit6 + +switch.exit6: ; preds = %switch.case5, %switch.case4, %switch.entry3 + %9 = load i32, i32* %h, align 4 + store i32 %9, i32* %switch7, align 4 + br label %switch.entry8 + +switch.entry8: ; preds = %switch.exit6 + %10 = load i32, i32* %switch7, align 4 + switch i32 %10, label %switch.exit12 [ + i32 0, label %switch.case9 + i32 1, label %switch.case10 + i32 2, label %switch.case11 + ] + +switch.case9: ; preds = %switch.entry8 + br label %switch.exit12 + +switch.case10: ; preds = %switch.entry8 + %11 = call i32 @"std::io.println"(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.5, i32 0, i32 0)) #1 + br label %switch.exit12 + +switch.case11: ; preds = %switch.entry8 + br label %switch.exit12 + +switch.exit12: ; preds = %switch.case11, %switch.case10, %switch.case9, %switch.entry8 + %12 = load i32, i32* %h, align 4 + store i32 %12, i32* %switch13, align 4 + br label %switch.entry14 + +switch.entry14: ; preds = %switch.exit12 + %13 = load i32, i32* %switch13, align 4 + switch i32 %13, label %switch.default [ + i32 0, label %switch.case15 + i32 1, label %switch.case15 + i32 2, label %switch.case15 + ] + +switch.case15: ; preds = %switch.entry14, %switch.entry14, %switch.entry14 + br label %switch.exit16 + +switch.default: ; preds = %switch.entry14 + br label %switch.exit16 + +switch.exit16: ; preds = %switch.default, %switch.case15 + %14 = load i32, i32* %h, align 4 + store i32 %14, i32* %switch17, align 4 + br label %switch.entry18 + +switch.entry18: ; preds = %switch.exit16 + %15 = load i32, i32* %switch17, align 4 + switch i32 %15, label %switch.exit23 [ + i32 0, label %switch.case19 + i32 1, label %switch.case20 + i32 2, label %switch.case22 + ] + +switch.case19: ; preds = %switch.entry18 + store i32 1, i32* %a, align 4 + %16 = call i32 @"std::io.println"(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.6, i32 0, i32 0)) #1 + br label %switch.case20 + +switch.case20: ; preds = %switch.entry18, %switch.case19 + store i32 2, i32* %a21, align 4 + %17 = call i32 @"std::io.println"(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.7, i32 0, i32 0)) #1 + br label %switch.case22 + +switch.case22: ; preds = %switch.entry18, %switch.case20 + %18 = call i32 @"std::io.println"(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.8, i32 0, i32 0)) #1 + br label %switch.exit23 + +switch.exit23: ; preds = %switch.case22, %switch.entry18 + ret void +} \ No newline at end of file diff --git a/test/test_suite/from_docs/examples_functionpointer.c3t b/test/test_suite/from_docs/examples_functionpointer.c3t new file mode 100644 index 000000000..a24594013 --- /dev/null +++ b/test/test_suite/from_docs/examples_functionpointer.c3t @@ -0,0 +1,37 @@ +// #target: x64-darwin + +module demo; +define Callback = func int(char* text, int value); + +func int my_callback(char* text, int value) +{ + return 0; +} + +Callback cb = &my_callback; + +func void main() +{ + int result = cb("demo", 123); + // .. +} + +// #expect: demo.ll + +define i32 @demo.my_callback(i8* %0, i32 %1) #0 { +entry: + %text = alloca i8*, align 8 + %value = alloca i32, align 4 + store i8* %0, i8** %text, align 8 + store i32 %1, i32* %value, align 4 + ret i32 0 +} + +define void @main() #0 { +entry: + %result = alloca i32, align 4 + %0 = load i32 (i8*, i32)*, i32 (i8*, i32)** @demo.cb, align 8 + %1 = call i32 %0(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i32 0, i32 0), i32 123) + store i32 %1, i32* %result, align 4 + ret void +} \ No newline at end of file diff --git a/test/test_suite/from_docs/examples_if_catch.c3t b/test/test_suite/from_docs/examples_if_catch.c3t new file mode 100644 index 000000000..32d992701 --- /dev/null +++ b/test/test_suite/from_docs/examples_if_catch.c3t @@ -0,0 +1,176 @@ +// #target: x64-darwin +module demo; +import std::io; + +errtype MathError +{ + DIVISION_BY_ZERO +} + +func int foo() { return 123; } +func int bar() { return 0; } + +func double! divide(int a, int b) +{ + if (b == 0) return MathError.DIVISION_BY_ZERO!; + return (double)(a) / (double)(b); + +} + +// Rethrowing an error uses "!!" suffix +func void! testMayError() +{ + divide(foo(), bar())!!; +} + +func void main() +{ + // ratio has a failable type. + double! ratio = divide(foo(), bar()); + + // Handle the error + if (catch err = ratio) + { + case MathError.DIVISION_BY_ZERO: + io::printf("Division by zero\n"); + return; + default: + io::printf("Unexpected error!"); + return; + } + // Flow typing makes "ratio" + // have the type double here. + io::printf("Ratio was %f\n", ratio); +} + +// #expect: demo.ll + +define i64 @demo.divide(double* %0, i32 %1, i32 %2) #0 { +entry: + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %reterr = alloca i64, align 8 + %reterr1 = alloca i64, align 8 + store i32 %1, i32* %a, align 4 + store i32 %2, i32* %b, align 4 + %3 = load i32, i32* %b, align 4 + %eq = icmp eq i32 %3, 0 + br i1 %eq, label %if.then, label %if.exit + +if.then: ; preds = %entry + store i64 ptrtoint ([1 x i8*]* @"demo.MathError$elements" to i64), i64* %reterr, align 8 + br label %err_retblock + +postfailed: ; No predecessors! + store double undef, double* %0, align 8 + ret i64 0 + +err_retblock: ; preds = %if.then + %4 = load i64, i64* %reterr, align 8 + ret i64 %4 + +if.exit: ; preds = %entry + %5 = load i32, i32* %a, align 4 + %sifp = sitofp i32 %5 to double + %6 = load i32, i32* %b, align 4 + %sifp2 = sitofp i32 %6 to double + %fdiv = fdiv double %sifp, %sifp2 + store double %fdiv, double* %0, align 8 + ret i64 0 +} + +define i64 @demo.testMayError() #0 { +entry: + %error_var = alloca i64, align 8 + %retparam = alloca double, align 8 + %0 = call i32 @demo.foo() + %1 = call i32 @demo.bar() + %2 = call i64 @demo.divide(double* %retparam, i32 %0, i32 %1) + %not_err = icmp eq i64 %2, 0 + br i1 %not_err, label %after.errcheck, label %error + +error: ; preds = %entry + store i64 %2, i64* %error_var, align 8 + br label %guard_block + +after.errcheck: ; preds = %entry + %3 = load double, double* %retparam, align 8 + br label %noerr_block + +guard_block: ; preds = %error + %4 = load i64, i64* %error_var, align 8 + ret i64 %4 + +noerr_block: ; preds = %after.errcheck + ret i64 0 +} + +define void @main() #0 { +entry: + %ratio = alloca double, align 8 + %ratio.f = alloca i64, align 8 + %retparam = alloca double, align 8 + %err = alloca i64, align 8 + %switch = alloca i64, align 8 + %0 = call i32 @demo.foo() + %1 = call i32 @demo.bar() + %2 = call i64 @demo.divide(double* %retparam, i32 %0, i32 %1) + %not_err = icmp eq i64 %2, 0 + br i1 %not_err, label %after.errcheck, label %error + +error: ; preds = %entry + store i64 %2, i64* %ratio.f, align 8 + br label %after_assign + +after.errcheck: ; preds = %entry + %3 = load double, double* %retparam, align 8 + store double %3, double* %ratio, align 8 + store i64 0, i64* %ratio.f, align 8 + br label %after_assign + +after_assign: ; preds = %after.errcheck, %error + br label %testblock + +testblock: ; preds = %after_assign + %4 = load i64, i64* %ratio.f, align 8 + %not_err1 = icmp eq i64 %4, 0 + br i1 %not_err1, label %after_check, label %error2 + +error2: ; preds = %testblock + store i64 %4, i64* %err, align 8 + br label %end_block + +after_check: ; preds = %testblock + store i64 0, i64* %err, align 8 + br label %end_block + +end_block: ; preds = %after_check, %error2 + %5 = load i64, i64* %err, align 8 + %neq = icmp ne i64 %5, 0 + br i1 %neq, label %if.then, label %if.exit + +if.then: ; preds = %end_block + store i64 %5, i64* %switch, align 8 + br label %switch.entry + +switch.entry: ; preds = %if.then + %6 = load i64, i64* %switch, align 8 + %eq = icmp eq i64 ptrtoint ([1 x i8*]* @"demo.MathError$elements" to i64), %6 + br i1 %eq, label %switch.case, label %next_if + +switch.case: ; preds = %switch.entry + %7 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @.str, i32 0, i32 0)) + ret void + +next_if: ; preds = %switch.entry + br label %switch.default + +switch.default: ; preds = %next_if + %8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([18 x i8], [18 x i8]* @.str.1, i32 0, i32 0)) + ret void + +if.exit: ; preds = %end_block + %9 = load double, double* %ratio, align 8 + %10 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.2, i32 0, i32 0), double %9) + ret void +} \ No newline at end of file diff --git a/test/test_suite/from_docs/examples_struct.c3 b/test/test_suite/from_docs/examples_struct.c3 new file mode 100644 index 000000000..2c7d8d6e0 --- /dev/null +++ b/test/test_suite/from_docs/examples_struct.c3 @@ -0,0 +1,46 @@ +define Callback = func int(char c); +struct Person {} +struct Company {} +enum Status : int +{ + IDLE, + BUSY, + DONE, +} + +struct MyData +{ + char* name; + Callback open; + Callback close; + //Status status; + + // named sub-structs (x.other.value) + struct other + { + int value; + int status; // ok, no name clash with other status + } + + // anonymous sub-structs (x.value) + struct + { + int value; + int status; // error, name clash with other status in MyData + } + + // anonymous union (x.person) + union + { + Person* person; + Company* company; + } + + // named sub-unions (x.either.this) + union either + { + int this; + bool or; + char* that; + } +} \ No newline at end of file diff --git a/test/test_suite/functions/splat.c3t b/test/test_suite/functions/splat.c3t index 39477675b..acc3e6502 100644 --- a/test/test_suite/functions/splat.c3t +++ b/test/test_suite/functions/splat.c3t @@ -33,12 +33,12 @@ func void test() %4 = getelementptr inbounds %"int[]", %"int[]"* %vararg, i32 0, i32 0 %5 = bitcast [3 x i32]* %varargslots to i32* store i32* %5, i32** %4, align 8 - %casttemp = bitcast %"int[]"* %vararg to { i64, i8* }* - %lo = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp, i32 0, i32 0 - %lo1 = load i64, i64* %lo, align 8 - %hi = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp, i32 0, i32 1 - %hi2 = load i8*, i8** %hi, align 8 - %6 = call i32 @sum_us(i64 %lo1, i8* %hi2) + %casttemp = bitcast %"int[]"* %vararg to { i8*, i64 }* + %lo = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp, i32 0, i32 0 + %lo1 = load i8*, i8** %lo, align 8 + %hi = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp, i32 0, i32 1 + %hi2 = load i64, i64* %hi, align 8 + %6 = call i32 @sum_us(i8* %lo1, i64 %hi2) %7 = bitcast [3 x i32]* %x to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %7, i8* align 4 bitcast ([3 x i32]* @.__const to i8*), i32 12, i1 false) %8 = bitcast [3 x i32]* %x to i32* @@ -50,25 +50,26 @@ func void test() store i64 3, i64* %11, align 8 %13 = bitcast [3 x i32]* %x to i32* store i32* %13, i32** %12, align 8 - %casttemp4 = bitcast %"int[]"* %vararg3 to { i64, i8* }* - %lo5 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp4, i32 0, i32 0 - %lo6 = load i64, i64* %lo5, align 8 - %hi7 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp4, i32 0, i32 1 - %hi8 = load i8*, i8** %hi7, align 8 - %14 = call i32 @sum_us(i64 %lo6, i8* %hi8) + %casttemp4 = bitcast %"int[]"* %vararg3 to { i8*, i64 }* + %lo5 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp4, i32 0, i32 0 + %lo6 = load i8*, i8** %lo5, align 8 + %hi7 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp4, i32 0, i32 1 + %hi8 = load i64, i64* %hi7, align 8 + %14 = call i32 @sum_us(i8* %lo6, i64 %hi8) %15 = getelementptr inbounds %"int[]", %"int[]"* %vararg9, i32 0, i32 1 %16 = getelementptr inbounds %"int[]", %"int[]"* %vararg9, i32 0, i32 0 - %casttemp10 = bitcast %"int[]"* %z to { i64, i8* }* - %lo11 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp10, i32 0, i32 0 - %lo12 = load i64, i64* %lo11, align 8 - %hi13 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp10, i32 0, i32 1 - %hi14 = load i8*, i8** %hi13, align 8 - %17 = call i32 @sum_us(i64 %lo12, i8* %hi14) + %casttemp10 = bitcast %"int[]"* %z to { i8*, i64 }* + %lo11 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp10, i32 0, i32 0 + %lo12 = load i8*, i8** %lo11, align 8 + %hi13 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp10, i32 0, i32 1 + %hi14 = load i64, i64* %hi13, align 8 + %17 = call i32 @sum_us(i8* %lo12, i64 %hi14) %18 = getelementptr inbounds %"int[]", %"int[]"* %vararg15, i32 0, i32 1 store i64 0, i64* %18, align 8 - %casttemp16 = bitcast %"int[]"* %vararg15 to { i64, i8* }* - %lo17 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp16, i32 0, i32 0 - %lo18 = load i64, i64* %lo17, align 8 - %hi19 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp16, i32 0, i32 1 - %hi20 = load i8*, i8** %hi19, align 8 - %19 = call i32 @sum_us(i64 %lo18, i8* %hi20) + %casttemp16 = bitcast %"int[]"* %vararg15 to { i8*, i64 }* + %lo17 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp16, i32 0, i32 0 + %lo18 = load i8*, i8** %lo17, align 8 + %hi19 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp16, i32 0, i32 1 + %hi20 = load i64, i64* %hi19, align 8 + %19 = call i32 @sum_us(i8* %lo18, i64 %hi20) + ret void diff --git a/test/test_suite/functions/test_regression.c3t b/test/test_suite/functions/test_regression.c3t index 5410a9ed3..04a100508 100644 --- a/test/test_suite/functions/test_regression.c3t +++ b/test/test_suite/functions/test_regression.c3t @@ -257,7 +257,7 @@ func Type getValue(Blob blob) %"int[]" = type { i32*, i64 } %Foo = type { i32, i32 } -define void @test.Foo2__printme(%Foo2* %0) +define void @test.Foo2__printme(%Foo2* %0) #0 { entry: %foo = alloca %Foo2*, align 8 store %Foo2* %0, %Foo2** %foo, align 8 @@ -268,7 +268,7 @@ entry: ret void } -define i32 @test.Foo2__mutate(%Foo2* %0) +define i32 @test.Foo2__mutate(%Foo2* %0) #0 { entry: %foo = alloca %Foo2*, align 8 store %Foo2* %0, %Foo2** %foo, align 8 @@ -281,15 +281,15 @@ entry: ret i32 %add } -declare i32 @printf(i8*, ...) +declare i32 @printf(i8*, ...) #0 -define void @test.helloWorld() +define void @test.helloWorld() #0 { entry: %0 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str, i32 0, i32 0)) ret void } -define i32 @test.test_static() +define i32 @test.test_static() #0 { entry: %0 = load i32, i32* @test_static.x, align 4 %add = add i32 %0, 1 @@ -300,7 +300,8 @@ entry: ret i32 %3 } -define i32 @test.helo(double %0, %Bobo* byval align 8 %1) +define i32 @test.helo(double %0, %Bobo* byval align 8 %1) #0 { +entry: %d = alloca double, align 8 %b = alloca %Bobo, align 4 %de = alloca [3 x i32], align 4 @@ -322,7 +323,8 @@ define i32 @test.helo(double %0, %Bobo* byval align 8 %1) ret i32 1 } -define i32 @test.test1(i32 %0, i32 %1) +define i32 @test.test1(i32 %0, i32 %1) #0 { +entry: %a = alloca i32, align 4 %b = alloca i32, align 4 store i32 %0, i32* %a, align 4 @@ -336,34 +338,39 @@ define i32 @test.test1(i32 %0, i32 %1) %gt = icmp sgt i32 %5, 128 br i1 %gt, label %if.then, label %if.exit +if.then: ; preds = %entry ret i32 -1 +if.exit: ; preds = %entry %6 = load i32, i32* %a, align 4 ret i32 %6 } -declare void @test_virtual3(i64, i8*) +declare void @test_virtual3(i64, i8*) #0 -declare void @test_virtual2(i8*, i8*) +declare void @test_virtual2(i8*, i8*) #0 -define i32 @test.sum_us(i64 %0, i8* %1) +define i32 @test.sum_us(i8* %0, i64 %1) #0 { +entry: %x = alloca %"int[]", align 8 %sum = alloca i32, align 4 %vararg = alloca %"int[]", align 8 %tempaddr = alloca %"int[]", align 8 - %pair = bitcast %"int[]"* %x to { i64, i8* }* - %lo = getelementptr inbounds { i64, i8* }, { i64, i8* }* %pair, i32 0, i32 0 - store i64 %0, i64* %lo, align 8 - %hi = getelementptr inbounds { i64, i8* }, { i64, i8* }* %pair, i32 0, i32 1 - store i8* %1, i8** %hi, align 8 + %pair = bitcast %"int[]"* %x to { i8*, i64 }* + %lo = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 0 + store i8* %0, i8** %lo, align 8 + %hi = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 1 + store i64 %1, i64* %hi, align 8 store i32 0, i32* %sum, align 4 %len = getelementptr inbounds %"int[]", %"int[]"* %x, i32 0, i32 1 %2 = load i64, i64* %len, align 8 %eq = icmp eq i64 %2, 0 br i1 %eq, label %if.then, label %if.exit +if.then: ; preds = %entry ret i32 0 +if.exit: ; preds = %entry %3 = load i32, i32* %sum, align 4 %subarrayptr = getelementptr inbounds %"int[]", %"int[]"* %x, i32 0, i32 0 %saptr = load i32*, i32** %subarrayptr, align 8 @@ -381,12 +388,12 @@ define i32 @test.sum_us(i64 %0, i8* %1) %11 = getelementptr inbounds %"int[]", %"int[]"* %vararg, i32 0, i32 1 %12 = getelementptr inbounds %"int[]", %"int[]"* %vararg, i32 0, i32 0 store %"int[]" %10, %"int[]"* %tempaddr, align 8 - %casttemp = bitcast %"int[]"* %tempaddr to { i64, i8* }* - %lo1 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp, i32 0, i32 0 - %lo2 = load i64, i64* %lo1, align 8 - %hi3 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp, i32 0, i32 1 - %hi4 = load i8*, i8** %hi3, align 8 - %13 = call i32 @test.sum_us(i64 %lo2, i8* %hi4) + %casttemp = bitcast %"int[]"* %tempaddr to { i8*, i64 }* + %lo1 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp, i32 0, i32 0 + %lo2 = load i8*, i8** %lo1, align 8 + %hi3 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp, i32 0, i32 1 + %hi4 = load i64, i64* %hi3, align 8 + %13 = call i32 @test.sum_us(i8* %lo2, i64 %hi4) %add = add i32 %4, %13 %add5 = add i32 %3, %add store i32 %add5, i32* %sum, align 4 @@ -394,19 +401,21 @@ define i32 @test.sum_us(i64 %0, i8* %1) ret i32 %14 } -define i32 @test.sumd(i64 %0, i8* %1) +define i32 @test.sumd(i8* %0, i64 %1) #0 { +entry: %x = alloca %"int[]", align 8 %sum = alloca i32, align 4 %i = alloca i32, align 4 - %pair = bitcast %"int[]"* %x to { i64, i8* }* - %lo = getelementptr inbounds { i64, i8* }, { i64, i8* }* %pair, i32 0, i32 0 - store i64 %0, i64* %lo, align 8 - %hi = getelementptr inbounds { i64, i8* }, { i64, i8* }* %pair, i32 0, i32 1 - store i8* %1, i8** %hi, align 8 + %pair = bitcast %"int[]"* %x to { i8*, i64 }* + %lo = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 0 + store i8* %0, i8** %lo, align 8 + %hi = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %pair, i32 0, i32 1 + store i64 %1, i64* %hi, align 8 store i32 0, i32* %sum, align 4 store i32 0, i32* %i, align 4 br label %for.cond +for.cond: ; preds = %for.inc, %entry %2 = load i32, i32* %i, align 4 %sisiext = sext i32 %2 to i64 %len = getelementptr inbounds %"int[]", %"int[]"* %x, i32 0, i32 1 @@ -416,6 +425,7 @@ define i32 @test.sumd(i64 %0, i8* %1) %siui-lt = or i1 %check, %lt br i1 %siui-lt, label %for.body, label %for.exit +for.body: ; preds = %for.cond %4 = load i32, i32* %sum, align 4 %subarrayptr = getelementptr inbounds %"int[]", %"int[]"* %x, i32 0, i32 0 %saptr = load i32*, i32** %subarrayptr, align 8 @@ -427,15 +437,18 @@ define i32 @test.sumd(i64 %0, i8* %1) store i32 %add, i32* %sum, align 4 br label %for.inc +for.inc: ; preds = %for.body %7 = load i32, i32* %i, align 4 %add2 = add i32 %7, 1 store i32 %add2, i32* %i, align 4 br label %for.cond +for.exit: ; preds = %for.cond %8 = load i32, i32* %sum, align 4 ret i32 %8 +} -define void @main() +define void @main() #0 { entry: %list = alloca %LinkedList, align 8 %i = alloca i32, align 4 @@ -474,12 +487,14 @@ entry: store i32 0, i32* %i, align 4 br label %for.cond +for.cond: ; preds = %for.inc, %entry %4 = load i32, i32* %i, align 4 - %5 = call i64 @"std::array::linkedlist.int.LinkedList__len"(%LinkedList* %list) + %5 = call i64 @"std::array::linkedlist.int.LinkedList__len"(%LinkedList* %list) #3 %uisitrunc = trunc i64 %5 to i32 %lt = icmp slt i32 %4, %uisitrunc br i1 %lt, label %for.body, label %for.exit +for.body: ; preds = %for.cond %6 = load i32, i32* %i, align 4 %7 = load i32, i32* %i, align 4 %siuiext = zext i32 %7 to i64 @@ -487,11 +502,13 @@ entry: %9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* @.str.2, i32 0, i32 0), i32 %6, i32 %8) br label %for.inc +for.inc: ; preds = %for.body %10 = load i32, i32* %i, align 4 %add = add i32 %10, 1 store i32 %add, i32* %i, align 4 br label %for.cond +for.exit: ; preds = %for.cond call void @"std::array::linkedlist.int.LinkedList__free"(%LinkedList* %list) %11 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([28 x i8], [28 x i8]* @.str.3, i32 0, i32 0), i32 -5, i32 14, i32 3) store i32 14, i32* %max, align 4 @@ -503,17 +520,19 @@ entry: call void @"std::array::list.int.List__append"(%List* %array, i32 100) call void @"std::array::list.int.List__append"(%List* %array, i32 200) call void @"std::array::list.int.List__append"(%List* %array, i32 400) - call void @"std::array::list.int.List__push"(%List* %array, i32 600) + call void @"std::array::list.int.List__push"(%List* %array, i32 600) #3 call void @"std::array::list.int.List__insertAt"(%List* %array, i64 2, i32 300) store i32 0, i32* %i1, align 4 br label %for.cond2 +for.cond2: ; preds = %for.inc7, %for.exit %14 = load i32, i32* %i1, align 4 %15 = call i64 @"std::array::list.int.List__len"(%List* %array) %uisitrunc3 = trunc i64 %15 to i32 %lt4 = icmp slt i32 %14, %uisitrunc3 br i1 %lt4, label %for.body5, label %for.exit9 +for.body5: ; preds = %for.cond2 %16 = load i32, i32* %i1, align 4 %17 = load i32, i32* %i1, align 4 %siuiext6 = zext i32 %17 to i64 @@ -521,11 +540,13 @@ entry: %19 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([17 x i8], [17 x i8]* @.str.5, i32 0, i32 0), i32 %16, i32 %18) br label %for.inc7 +for.inc7: ; preds = %for.body5 %20 = load i32, i32* %i1, align 4 %add8 = add i32 %20, 1 store i32 %add8, i32* %i1, align 4 br label %for.cond2 +for.exit9: ; preds = %for.cond2 call void @"std::array::list.int.List__free"(%List* %array) %21 = bitcast %Blob* %a to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %21, i8* align 4 bitcast (%Blob* @.__const.6 to i8*), i32 4, i1 false) @@ -558,12 +579,12 @@ entry: store i64 4, i64* %38, align 8 %40 = bitcast [4 x i32]* %x to i32* store i32* %40, i32** %39, align 8 - %casttemp = bitcast %"int[]"* %vararg to { i64, i8* }* - %lo = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp, i32 0, i32 0 - %lo11 = load i64, i64* %lo, align 8 - %hi = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp, i32 0, i32 1 - %hi12 = load i8*, i8** %hi, align 8 - %41 = call i32 @test.sum_us(i64 %lo11, i8* %hi12) + %casttemp = bitcast %"int[]"* %vararg to { i8*, i64 }* + %lo = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp, i32 0, i32 0 + %lo11 = load i8*, i8** %lo, align 8 + %hi = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp, i32 0, i32 1 + %hi12 = load i64, i64* %hi, align 8 + %41 = call i32 @test.sum_us(i8* %lo11, i64 %hi12) %42 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([22 x i8], [22 x i8]* @.str.13, i32 0, i32 0), i32 %41) %add13 = add i32 %37, %42 store i32 %add13, i32* %fro, align 4 @@ -580,21 +601,21 @@ entry: store i64 4, i64* %49, align 8 %51 = bitcast [4 x i32]* %x to i32* store i32* %51, i32** %50, align 8 - %casttemp15 = bitcast %"int[]"* %vararg14 to { i64, i8* }* - %lo16 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp15, i32 0, i32 0 - %lo17 = load i64, i64* %lo16, align 8 - %hi18 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp15, i32 0, i32 1 - %hi19 = load i8*, i8** %hi18, align 8 - %52 = call i32 @test.sum_us(i64 %lo17, i8* %hi19) + %casttemp15 = bitcast %"int[]"* %vararg14 to { i8*, i64 }* + %lo16 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp15, i32 0, i32 0 + %lo17 = load i8*, i8** %lo16, align 8 + %hi18 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp15, i32 0, i32 1 + %hi19 = load i64, i64* %hi18, align 8 + %52 = call i32 @test.sum_us(i8* %lo17, i64 %hi19) %53 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* @.str.16, i32 0, i32 0), i32 %52) %54 = getelementptr inbounds %"int[]", %"int[]"* %vararg20, i32 0, i32 1 %55 = getelementptr inbounds %"int[]", %"int[]"* %vararg20, i32 0, i32 0 - %casttemp21 = bitcast %"int[]"* %z to { i64, i8* }* - %lo22 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp21, i32 0, i32 0 - %lo23 = load i64, i64* %lo22, align 8 - %hi24 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp21, i32 0, i32 1 - %hi25 = load i8*, i8** %hi24, align 8 - %56 = call i32 @test.sum_us(i64 %lo23, i8* %hi25) + %casttemp21 = bitcast %"int[]"* %z to { i8*, i64 }* + %lo22 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp21, i32 0, i32 0 + %lo23 = load i8*, i8** %lo22, align 8 + %hi24 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp21, i32 0, i32 1 + %hi25 = load i64, i64* %hi24, align 8 + %56 = call i32 @test.sum_us(i8* %lo23, i64 %hi25) %57 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([21 x i8], [21 x i8]* @.str.17, i32 0, i32 0), i32 %56) %58 = getelementptr inbounds [4 x i32], [4 x i32]* %varargslots, i64 0, i64 0 store i32 1, i32* %58, align 4 @@ -609,12 +630,12 @@ entry: %63 = getelementptr inbounds %"int[]", %"int[]"* %vararg26, i32 0, i32 0 %64 = bitcast [4 x i32]* %varargslots to i32* store i32* %64, i32** %63, align 8 - %casttemp27 = bitcast %"int[]"* %vararg26 to { i64, i8* }* - %lo28 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp27, i32 0, i32 0 - %lo29 = load i64, i64* %lo28, align 8 - %hi30 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp27, i32 0, i32 1 - %hi31 = load i8*, i8** %hi30, align 8 - %65 = call i32 @test.sum_us(i64 %lo29, i8* %hi31) + %casttemp27 = bitcast %"int[]"* %vararg26 to { i8*, i64 }* + %lo28 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp27, i32 0, i32 0 + %lo29 = load i8*, i8** %lo28, align 8 + %hi30 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp27, i32 0, i32 1 + %hi31 = load i64, i64* %hi30, align 8 + %65 = call i32 @test.sum_us(i8* %lo29, i64 %hi31) %66 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.18, i32 0, i32 0), i32 %65) %67 = getelementptr inbounds [1 x i32], [1 x i32]* %varargslots33, i64 0, i64 0 store i32 1, i32* %67, align 4 @@ -623,28 +644,27 @@ entry: %69 = getelementptr inbounds %"int[]", %"int[]"* %vararg32, i32 0, i32 0 %70 = bitcast [1 x i32]* %varargslots33 to i32* store i32* %70, i32** %69, align 8 - %casttemp34 = bitcast %"int[]"* %vararg32 to { i64, i8* }* - %lo35 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp34, i32 0, i32 0 - %lo36 = load i64, i64* %lo35, align 8 - %hi37 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp34, i32 0, i32 1 - %hi38 = load i8*, i8** %hi37, align 8 - %71 = call i32 @test.sum_us(i64 %lo36, i8* %hi38) + %casttemp34 = bitcast %"int[]"* %vararg32 to { i8*, i64 }* + %lo35 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp34, i32 0, i32 0 + %lo36 = load i8*, i8** %lo35, align 8 + %hi37 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp34, i32 0, i32 1 + %hi38 = load i64, i64* %hi37, align 8 + %71 = call i32 @test.sum_us(i8* %lo36, i64 %hi38) %72 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.19, i32 0, i32 0), i32 %71) %73 = getelementptr inbounds %"int[]", %"int[]"* %vararg39, i32 0, i32 1 store i64 0, i64* %73, align 8 - %casttemp40 = bitcast %"int[]"* %vararg39 to { i64, i8* }* - %lo41 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp40, i32 0, i32 0 - %lo42 = load i64, i64* %lo41, align 8 - %hi43 = getelementptr inbounds { i64, i8* }, { i64, i8* }* %casttemp40, i32 0, i32 1 - %hi44 = load i8*, i8** %hi43, align 8 - %74 = call i32 @test.sum_us(i64 %lo42, i8* %hi44) + %casttemp40 = bitcast %"int[]"* %vararg39 to { i8*, i64 }* + %lo41 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp40, i32 0, i32 0 + %lo42 = load i8*, i8** %lo41, align 8 + %hi43 = getelementptr inbounds { i8*, i64 }, { i8*, i64 }* %casttemp40, i32 0, i32 1 + %hi44 = load i64, i64* %hi43, align 8 + %74 = call i32 @test.sum_us(i8* %lo42, i64 %hi44) %75 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.20, i32 0, i32 0), i32 %74) store i32 (double, %Bobo*)* null, i32 (double, %Bobo*)** %a1, align 8 store i32 (double, %Bobo*)* null, i32 (double, %Bobo*)** %b2, align 8 ret void } - // #expect: hello_world.ll define void @hello_world.hello() diff --git a/test/test_suite/statements/switch_errors.c3 b/test/test_suite/statements/switch_errors.c3 index 9b6414d0a..79ab20fc2 100644 --- a/test/test_suite/statements/switch_errors.c3 +++ b/test/test_suite/statements/switch_errors.c3 @@ -133,4 +133,28 @@ func void test_missing_no_cases(Baz x) default: break; } +} + +errtype MathError +{ + DIVISION_BY_ZERO +} + +// Rethrowing an error uses "!!" suffix +func void! testMayError() +{ +} + +func void main() +{ + // Handle the error + switch (catch err = testMayError()) // #error: Catch unwrapping is only allowed + { + case MathError.DIVISION_BY_ZERO: + io::printf("Division by zero\n"); + return; + default: + io::printf("Unexpected error!"); + return; + } } \ No newline at end of file