Commit 7201e69454

Jacob Young <jacobly0@users.noreply.github.com>
2023-07-25 02:10:37
AstGen: fix missing deferred ref
Closes #16524
1 parent 1aacfa7
Changed files (2)
src
test
behavior
src/AstGen.zig
@@ -6487,8 +6487,7 @@ fn forExpr(
 
     {
         var capture_token = for_full.payload_token;
-        for (for_full.ast.inputs, 0..) |input, i_usize| {
-            const i = @as(u32, @intCast(i_usize));
+        for (for_full.ast.inputs, indexables, lens) |input, *indexable_ref, *len_ref| {
             const capture_is_ref = token_tags[capture_token] == .asterisk;
             const ident_tok = capture_token + @intFromBool(capture_is_ref);
             const is_discard = mem.eql(u8, tree.tokenSlice(ident_tok), "_");
@@ -6527,14 +6526,14 @@ fn forExpr(
                     });
 
                 any_len_checks = any_len_checks or range_len != .none;
-                indexables[i] = if (start_is_zero) .none else start_val;
-                lens[i] = range_len;
+                indexable_ref.* = if (start_is_zero) .none else start_val;
+                len_ref.* = range_len;
             } else {
                 const indexable = try expr(parent_gz, scope, .{ .rl = .none }, input);
 
                 any_len_checks = true;
-                indexables[i] = indexable;
-                lens[i] = indexable;
+                indexable_ref.* = indexable;
+                len_ref.* = indexable;
             }
         }
     }
@@ -6546,7 +6545,7 @@ fn forExpr(
     // We use a dedicated ZIR instruction to assert the lengths to assist with
     // nicer error reporting as well as fewer ZIR bytes emitted.
     const len: Zir.Inst.Ref = len: {
-        const lens_len = @as(u32, @intCast(lens.len));
+        const lens_len: u32 = @intCast(lens.len);
         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.MultiOp).Struct.fields.len + lens_len);
         const len = try parent_gz.addPlNode(.for_len, node, Zir.Inst.MultiOp{
             .operands_len = lens_len,
@@ -6565,7 +6564,10 @@ fn forExpr(
     defer loop_scope.unstack();
     defer loop_scope.labeled_breaks.deinit(gpa);
 
+    // We need to finish loop_scope later once we have the deferred refs from then_scope. However, the
+    // load must be removed from instructions in the meantime or it appears to be part of parent_gz.
     const index = try loop_scope.addUnNode(.load, index_ptr, node);
+    _ = loop_scope.instructions.pop();
 
     var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
     defer cond_scope.unstack();
@@ -6581,26 +6583,14 @@ fn forExpr(
     const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
     const cond_block = try loop_scope.makeBlockInst(block_tag, node);
     try cond_scope.setBlockBody(cond_block);
-    // cond_block unstacked now, can add new instructions to loop_scope
-    try loop_scope.instructions.append(gpa, cond_block);
 
-    // Increment the index variable.
-    const index_plus_one = try loop_scope.addPlNode(.add_unsafe, node, Zir.Inst.Bin{
-        .lhs = index,
-        .rhs = .one_usize,
-    });
-    _ = try loop_scope.addBin(.store, index_ptr, index_plus_one);
-    const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
-    _ = try loop_scope.addNode(repeat_tag, node);
-
-    try loop_scope.setBlockBody(loop_block);
     loop_scope.break_block = loop_block;
     loop_scope.continue_block = cond_block;
     if (for_full.label_token) |label_token| {
-        loop_scope.label = @as(?GenZir.Label, GenZir.Label{
+        loop_scope.label = .{
             .token = label_token,
             .block_inst = loop_block,
-        });
+        };
     }
 
     var then_node = for_full.ast.then_expr;
@@ -6615,8 +6605,7 @@ fn forExpr(
     const then_sub_scope = blk: {
         var capture_token = for_full.payload_token;
         var capture_sub_scope: *Scope = &then_scope.base;
-        for (for_full.ast.inputs, 0..) |input, i_usize| {
-            const i = @as(u32, @intCast(i_usize));
+        for (for_full.ast.inputs, indexables, capture_scopes) |input, indexable_ref, *capture_scope| {
             const capture_is_ref = token_tags[capture_token] == .asterisk;
             const ident_tok = capture_token + @intFromBool(capture_is_ref);
             const capture_name = tree.tokenSlice(ident_tok);
@@ -6631,7 +6620,7 @@ fn forExpr(
             const capture_inst = inst: {
                 const is_counter = node_tags[input] == .for_range;
 
-                if (indexables[i] == .none) {
+                if (indexable_ref == .none) {
                     // Special case: the main index can be used directly.
                     assert(is_counter);
                     assert(!capture_is_ref);
@@ -6650,12 +6639,12 @@ fn forExpr(
                     0b11 => unreachable, // compile error emitted already
                 };
                 break :inst try then_scope.addPlNode(tag, input, Zir.Inst.Bin{
-                    .lhs = indexables[i],
+                    .lhs = indexable_ref,
                     .rhs = index,
                 });
             };
 
-            capture_scopes[i] = .{
+            capture_scope.* = .{
                 .parent = capture_sub_scope,
                 .gen_zir = &then_scope,
                 .name = name_str_index,
@@ -6665,7 +6654,7 @@ fn forExpr(
             };
 
             try then_scope.addDbgVar(.dbg_var_val, name_str_index, capture_inst);
-            capture_sub_scope = &capture_scopes[i].base;
+            capture_sub_scope = &capture_scope.base;
         }
 
         break :blk capture_sub_scope;
@@ -6730,6 +6719,24 @@ fn forExpr(
         cond_block,
         break_tag,
     );
+
+    // then_block and else_block unstacked now, can resurrect loop_scope to finally finish it
+    {
+        loop_scope.instructions_top = loop_scope.instructions.items.len;
+        try loop_scope.instructions.appendSlice(gpa, &.{ Zir.refToIndex(index).?, cond_block });
+
+        // Increment the index variable.
+        const index_plus_one = try loop_scope.addPlNode(.add_unsafe, node, Zir.Inst.Bin{
+            .lhs = index,
+            .rhs = .one_usize,
+        });
+        _ = try loop_scope.addBin(.store, index_ptr, index_plus_one);
+        const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
+        _ = try loop_scope.addNode(repeat_tag, node);
+
+        try loop_scope.setBlockBody(loop_block);
+    }
+
     if (ri.rl.strategy(&loop_scope).tag == .break_void and loop_scope.break_count == 0) {
         _ = try rvalue(parent_gz, ri, .void_value, node);
     }
test/behavior/for.zig
@@ -479,3 +479,41 @@ test "inline for on tuple pointer" {
 
     try expectEqual(S{ 0, 1, 2 }, s);
 }
+
+test "ref counter that starts at zero" {
+    if (builtin.zig_backend == .stage2_sparc64) 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_spirv64) return error.SkipZigTest; // TODO
+
+    for ([_]usize{ 0, 1, 2 }, 0..) |i, j| {
+        try expectEqual(i, j);
+        try expectEqual((&i).*, (&j).*);
+    }
+    inline for (.{ 0, 1, 2 }, 0..) |i, j| {
+        try expectEqual(i, j);
+        try expectEqual((&i).*, (&j).*);
+    }
+}
+
+test "inferred alloc ptr of for loop" {
+    if (builtin.zig_backend == .stage2_sparc64) 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_spirv64) return error.SkipZigTest; // TODO
+
+    {
+        var cond = false;
+        var opt = for (0..1) |_| {
+            if (cond) break cond;
+        } else null;
+        try expectEqual(@as(?bool, null), opt);
+    }
+    {
+        var cond = true;
+        var opt = for (0..1) |_| {
+            if (cond) break cond;
+        } else null;
+        try expectEqual(@as(?bool, true), opt);
+    }
+}