Commit 273da9efd9

Andrew Kelley <andrew@ziglang.org>
2022-03-11 01:09:23
AstGen: structInitExpr and arrayInitExpr avoid crash
when an inferred alloc is passed as the result pointer of a block.
1 parent a30d283
Changed files (4)
src
test
behavior
src/AstGen.zig
@@ -1360,6 +1360,12 @@ fn arrayInitExpr(
             }
         },
         .block_ptr => |block_gz| {
+            // This condition is here for the same reason as the above condition in `inferred_ptr`.
+            // See corresponding logic in structInitExpr.
+            if (types.array == .none and astgen.isInferred(block_gz.rl_ptr)) {
+                const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
+                return rvalue(gz, rl, result, node);
+            }
             return arrayInitExprRlPtr(gz, scope, rl, node, block_gz.rl_ptr, array_init.ast.elements, types.array);
         },
     }
@@ -1604,7 +1610,16 @@ fn structInitExpr(
                 return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst);
             }
         },
-        .block_ptr => |block_gz| return structInitExprRlPtr(gz, scope, rl, node, struct_init, block_gz.rl_ptr),
+        .block_ptr => |block_gz| {
+            // This condition is here for the same reason as the above condition in `inferred_ptr`.
+            // See corresponding logic in arrayInitExpr.
+            if (struct_init.ast.type_expr == 0 and astgen.isInferred(block_gz.rl_ptr)) {
+                const result = try structInitExprRlNone(gz, scope, node, struct_init, .struct_init_anon);
+                return rvalue(gz, rl, result, node);
+            }
+
+            return structInitExprRlPtr(gz, scope, rl, node, struct_init, block_gz.rl_ptr);
+        },
     }
 }
 
@@ -10938,3 +10953,17 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.
     }
     return decl_count;
 }
+
+fn isInferred(astgen: *AstGen, ref: Zir.Inst.Ref) bool {
+    const inst = refToIndex(ref) orelse return false;
+    const zir_tags = astgen.instructions.items(.tag);
+    return switch (zir_tags[inst]) {
+        .alloc_inferred,
+        .alloc_inferred_mut,
+        .alloc_inferred_comptime,
+        .alloc_inferred_comptime_mut,
+        => true,
+
+        else => false,
+    };
+}
src/Zir.zig
@@ -516,7 +516,7 @@ pub const Inst = struct {
         /// Same as `store` except provides a source location.
         /// Uses the `pl_node` union field. Payload is `Bin`.
         store_node,
-        /// This instruction is not really supposed to be emitted from AstGen; nevetheless it
+        /// This instruction is not really supposed to be emitted from AstGen; nevertheless it
         /// is sometimes emitted due to deficiencies in AstGen. When Sema sees this instruction,
         /// it must clean up after AstGen's mess by looking at various context clues and
         /// then treating it as one of the following:
test/behavior/bugs/5474.zig
@@ -49,19 +49,17 @@ fn constant() !void {
 }
 
 test "pointer-to-array constness for zero-size elements, var" {
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
     try mutable();
     comptime try mutable();
 }
 
 test "pointer-to-array constness for zero-size elements, const" {
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
     try constant();
     comptime try constant();
 }
test/behavior/tuple.zig
@@ -165,3 +165,35 @@ test "array-like initializer for tuple types" {
     try S.doTheTest();
     comptime try S.doTheTest();
 }
+
+test "anon struct as the result from a labeled block" {
+    const S = struct {
+        fn doTheTest() !void {
+            const precomputed = comptime blk: {
+                var x: i32 = 1234;
+                break :blk .{
+                    .x = x,
+                };
+            };
+            try expect(precomputed.x == 1234);
+        }
+    };
+
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
+
+test "tuple as the result from a labeled block" {
+    const S = struct {
+        fn doTheTest() !void {
+            const precomputed = comptime blk: {
+                var x: i32 = 1234;
+                break :blk .{x};
+            };
+            try expect(precomputed[0] == 1234);
+        }
+    };
+
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}