Commit 1a4b0d9790

mlugg <mlugg@mlugg.co.uk>
2023-05-31 02:03:05
AstGen: handle ref_table for errdefer captures
Resolves: #15861
1 parent 77b40d6
Changed files (2)
src
test
behavior
src/AstGen.zig
@@ -2982,11 +2982,27 @@ fn deferStmt(
     if (have_err_code) try gz.addDbgBlockEnd();
     _ = try defer_gen.addBreak(.break_inline, 0, .void_value);
 
+    // We must handle ref_table for remapped_err_code manually.
     const body = defer_gen.instructionsSlice();
-    const body_len = gz.astgen.countBodyLenAfterFixups(body);
+    const body_len = blk: {
+        var refs: u32 = 0;
+        if (have_err_code) {
+            var cur_inst = remapped_err_code;
+            while (gz.astgen.ref_table.get(cur_inst)) |ref_inst| {
+                refs += 1;
+                cur_inst = ref_inst;
+            }
+        }
+        break :blk gz.astgen.countBodyLenAfterFixups(body) + refs;
+    };
 
     const index = @intCast(u32, gz.astgen.extra.items.len);
     try gz.astgen.extra.ensureUnusedCapacity(gz.astgen.gpa, body_len);
+    if (have_err_code) {
+        if (gz.astgen.ref_table.fetchRemove(remapped_err_code)) |kv| {
+            gz.astgen.appendPossiblyRefdBodyInst(&gz.astgen.extra, kv.value);
+        }
+    }
     gz.astgen.appendBodyWithFixups(body);
 
     const defer_scope = try block_arena.create(Scope.Defer);
test/behavior/defer.zig
@@ -134,6 +134,32 @@ test "errdefer with payload" {
     comptime try S.doTheTest();
 }
 
+test "reference to errdefer payload" {
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
+
+    const S = struct {
+        fn foo() !i32 {
+            errdefer |a| {
+                const ptr = &a;
+                const ptr2 = &ptr;
+                expectEqual(error.One, ptr2.*.*) catch @panic("test failure");
+                expectEqual(error.One, ptr.*) catch @panic("test failure");
+            }
+            return error.One;
+        }
+        fn doTheTest() !void {
+            try expectError(error.One, foo());
+        }
+    };
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
+
 test "simple else prong doesn't emit an error for unreachable else prong" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO