diff --git a/releasenotes.md b/releasenotes.md index 8a1bdd170..a94a89fe8 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -33,6 +33,7 @@ - Incorrect parsing of call attributes #2144. - Error when using named argument on trailing macro body expansion #2139. - Designated const initializers with `{}` would overwrite the parent field. +- Empty default case in @jump switch does not fallthrough #2147. ### Stdlib changes - Added `String.quick_ztr` and `String.is_zstr` diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 1ba70526d..359b399ba 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -825,17 +825,27 @@ static void llvm_emit_switch_jump_table(GenContext *c, Int max = { .type = TYPE_VOID }; int min_index = -1; int default_index = -1; + bool last_was_default = false; LLVMBasicBlockRef default_block = exit_block; for (unsigned i = 0; i < case_count; i++) { Ast *case_ast = cases[i]; if (case_ast->ast_kind == AST_DEFAULT_STMT) { - if (!case_ast->case_stmt.body) continue; - default_block = case_ast->case_stmt.backend_block; default_index = i; + if (!case_ast->case_stmt.body) + { + last_was_default = true; + continue; + } + default_block = case_ast->case_stmt.backend_block; continue; } + if (last_was_default && case_ast->case_stmt.body) + { + default_block = case_ast->case_stmt.backend_block; + last_was_default = false; + } Int value, to_value; llvm_set_jump_table_values(case_ast->case_stmt.expr, case_ast->case_stmt.to_expr, &value, &to_value); if (min.type == TYPE_VOID) diff --git a/test/test_suite/switch/jump_bug_default.c3t b/test/test_suite/switch/jump_bug_default.c3t new file mode 100644 index 000000000..8f05c4275 --- /dev/null +++ b/test/test_suite/switch/jump_bug_default.c3t @@ -0,0 +1,106 @@ +// #target: macos-aarch64 +module test; +import std; + +fn int main(String[] args) +{ + switch (1) @jump + { + default: + case 3: + io::printn("Hello"); + } + return 0; +} + +/* #expect: test.ll + +define i32 @test.main([2 x i64] %0) #0 { +entry: + %args = alloca %"char[][]", align 8 + %switch = alloca i32, align 4 + %len = alloca i64, align 8 + %error_var = alloca i64, align 8 + %retparam = alloca i64, align 8 + %taddr = alloca %"char[]", align 8 + %error_var2 = alloca i64, align 8 + %error_var8 = alloca i64, align 8 + store [2 x i64] %0, ptr %args, align 8 + store i32 1, ptr %switch, align 4 + br label %switch.entry + +switch.entry: ; preds = %entry + %1 = load i32, ptr %switch, align 4 + %2 = sub i32 %1, 3 + %3 = icmp ugt i32 %2, 0 + br i1 %3, label %switch.case, label %jumpblock + +jumpblock: ; preds = %switch.entry + %ptroffset = getelementptr inbounds [8 x i8], ptr @jumptable, i32 %2 + %target = load ptr, ptr %ptroffset, align 8 + indirectbr ptr %target, [label %switch.case] + +switch.case: ; preds = %jumpblock, %switch.entry + %4 = call ptr @std.io.stdout() + store %"char[]" { ptr @.str, i64 5 }, ptr %taddr, align 8 + %5 = load [2 x i64], ptr %taddr, align 8 + %6 = call i64 @std.io.File.write(ptr %retparam, ptr %4, [2 x i64] %5) + %not_err = icmp eq i64 %6, 0 + %7 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %7, label %after_check, label %assign_optional + +assign_optional: ; preds = %switch.case + store i64 %6, ptr %error_var, align 8 + br label %guard_block + +after_check: ; preds = %switch.case + br label %noerr_block + +guard_block: ; preds = %assign_optional + br label %voiderr + +noerr_block: ; preds = %after_check + %8 = load i64, ptr %retparam, align 8 + store i64 %8, ptr %len, align 8 + %9 = call i64 @std.io.File.write_byte(ptr %4, i8 10) + %not_err3 = icmp eq i64 %9, 0 + %10 = call i1 @llvm.expect.i1(i1 %not_err3, i1 true) + br i1 %10, label %after_check5, label %assign_optional4 + +assign_optional4: ; preds = %noerr_block + store i64 %9, ptr %error_var2, align 8 + br label %guard_block6 + +after_check5: ; preds = %noerr_block + br label %noerr_block7 + +guard_block6: ; preds = %assign_optional4 + br label %voiderr + +noerr_block7: ; preds = %after_check5 + %11 = call i64 @std.io.File.flush(ptr %4) + %not_err9 = icmp eq i64 %11, 0 + %12 = call i1 @llvm.expect.i1(i1 %not_err9, i1 true) + br i1 %12, label %after_check11, label %assign_optional10 + +assign_optional10: ; preds = %noerr_block7 + store i64 %11, ptr %error_var8, align 8 + br label %guard_block12 + +after_check11: ; preds = %noerr_block7 + br label %noerr_block13 + +guard_block12: ; preds = %assign_optional10 + br label %voiderr + +noerr_block13: ; preds = %after_check11 + %13 = load i64, ptr %len, align 8 + %add = add i64 %13, 1 + br label %voiderr + +voiderr: ; preds = %noerr_block13, %guard_block12, %guard_block6, %guard_block + br label %switch.exit + +switch.exit: ; preds = %voiderr + ret i32 0 +}