From 341a70bd5dbfd0573e4eaa8fd8cb64d805626359 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 20 Feb 2025 03:44:22 +0100 Subject: [PATCH] Implicitly unwrapped optional value in defer incorrectly copied #1982. --- releasenotes.md | 1 + src/compiler/copying.c | 15 ++++++ test/src/test_suite_runner.c3 | 2 +- .../test_suite/defer/defer_with_unwrapped.c3t | 53 +++++++++++++++++++ 4 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 test/test_suite/defer/defer_with_unwrapped.c3t diff --git a/releasenotes.md b/releasenotes.md index 9d9aa5f2e..227a40c49 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -14,6 +14,7 @@ - Test runner with tracking allocator assertion at failed test #1963 - Test runner with tracking allocator didn't properly handle teardown_fn - Correctly give an error if a character literal contains a line break. +- Implicitly unwrapped optional value in defer incorrectly copied #1982. ### Stdlib changes diff --git a/src/compiler/copying.c b/src/compiler/copying.c index d8a4e9c41..e07f6e2a5 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -399,8 +399,23 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr) MACRO_COPY_EXPR_LIST(expr->catch_expr.exprs); return expr; case EXPR_IDENTIFIER: + { + Decl *var = expr->ident_expr; + // In the case we have an unwrapped alias, we need to create it. + if (var->decl_kind == DECL_VAR) + { + switch (var->var.kind) + { + case VARDECL_UNWRAPPED: + case VARDECL_REWRAPPED: + expr->ident_expr = copy_decl(c, var); + return expr; + default: break; + } + } fixup_decl(c, &expr->ident_expr); return expr; + } case EXPR_UNRESOLVED_IDENTIFIER: case EXPR_CT_IDENT: case EXPR_HASH_IDENT: diff --git a/test/src/test_suite_runner.c3 b/test/src/test_suite_runner.c3 index 3aa70aa37..80db7501f 100644 --- a/test/src/test_suite_runner.c3 +++ b/test/src/test_suite_runner.c3 @@ -58,7 +58,7 @@ fn void main(String[] args) // Print the test result io::printfn("Found %d tests: %.1f%% (%d / %d) passed (%d skipped).", - test_count, 100 * success_count / math::max(1, test_count - skip_count), + test_count, (100.0 * success_count) / math::max(1, test_count - skip_count), success_count, test_count - skip_count, skip_count); libc::exit(success_count == test_count - skip_count ? 0 : 1); } diff --git a/test/test_suite/defer/defer_with_unwrapped.c3t b/test/test_suite/defer/defer_with_unwrapped.c3t new file mode 100644 index 000000000..365c86a9a --- /dev/null +++ b/test/test_suite/defer/defer_with_unwrapped.c3t @@ -0,0 +1,53 @@ +// #target: macos-x64 +module test; +import std::io; +// Issue 1982 +fn int main() +{ + defer + { + int! x = 1; + if (catch err = x) unreachable(); + int y = x; + } + return 0; +} +/* #expect: test.ll + +define i32 @main() #0 { +entry: + %x = alloca i32, align 4 + %x.f = alloca i64, align 8 + %err = alloca i64, align 8 + %y = alloca i32, align 4 + store i32 1, ptr %x, align 4 + store i64 0, ptr %x.f, align 8 + br label %testblock + +testblock: ; preds = %entry + %optval = load i64, ptr %x.f, align 8 + %not_err = icmp eq i64 %optval, 0 + %0 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %0, label %after_check, label %assign_optional + +assign_optional: ; preds = %testblock + store i64 %optval, ptr %err, align 8 + br label %end_block + +after_check: ; preds = %testblock + store i64 0, ptr %err, align 8 + br label %end_block + +end_block: ; preds = %after_check, %assign_optional + %1 = load i64, ptr %err, align 8 + %i2b = icmp ne i64 %1, 0 + br i1 %i2b, label %if.then, label %if.exit + +if.then: ; preds = %end_block + unreachable + +if.exit: ; preds = %end_block + %2 = load i32, ptr %x, align 4 + store i32 %2, ptr %y, align 4 + ret i32 0 +}