Commit 7c1061784b

Andrew Kelley <andrew@ziglang.org>
2022-02-09 04:03:17
stage2: fix inferred comptime constant locals
`const` declarations inside comptime blocks were not getting properly evaluated at compile-time. To accomplish this there is a new ZIR instruction, `alloc_inferred_comptime`. Actually we already had one named that, but it got renamed to `alloc_inferred_comptime_mut` to match the naming convention with the other similar instructions.
1 parent 210ee10
src/AstGen.zig
@@ -2084,10 +2084,11 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
             .param_anytype_comptime,
             .alloc,
             .alloc_mut,
-            .alloc_comptime,
+            .alloc_comptime_mut,
             .alloc_inferred,
             .alloc_inferred_mut,
             .alloc_inferred_comptime,
+            .alloc_inferred_comptime_mut,
             .array_cat,
             .array_mul,
             .array_type,
@@ -2613,7 +2614,7 @@ fn varDecl(
                         .type_inst = type_inst,
                         .align_inst = align_inst,
                         .is_const = true,
-                        .is_comptime = false,
+                        .is_comptime = gz.force_comptime,
                     });
                     init_scope.instructions_top = gz.instructions.items.len;
                 }
@@ -2621,14 +2622,18 @@ fn varDecl(
             } else {
                 const alloc = if (align_inst == .none) alloc: {
                     init_scope.instructions_top = gz.instructions.items.len;
-                    break :alloc try init_scope.addNode(.alloc_inferred, node);
+                    const tag: Zir.Inst.Tag = if (gz.force_comptime)
+                        .alloc_inferred_comptime
+                    else
+                        .alloc_inferred;
+                    break :alloc try init_scope.addNode(tag, node);
                 } else alloc: {
                     const ref = try gz.addAllocExtended(.{
                         .node = node,
                         .type_inst = .none,
                         .align_inst = align_inst,
                         .is_const = true,
-                        .is_comptime = false,
+                        .is_comptime = gz.force_comptime,
                     });
                     init_scope.instructions_top = gz.instructions.items.len;
                     break :alloc ref;
@@ -2716,7 +2721,10 @@ fn varDecl(
                 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
                 const alloc = alloc: {
                     if (align_inst == .none) {
-                        const tag: Zir.Inst.Tag = if (is_comptime) .alloc_comptime else .alloc_mut;
+                        const tag: Zir.Inst.Tag = if (is_comptime)
+                            .alloc_comptime_mut
+                        else
+                            .alloc_mut;
                         break :alloc try gz.addUnNode(tag, type_inst, node);
                     } else {
                         break :alloc try gz.addAllocExtended(.{
@@ -2732,7 +2740,10 @@ fn varDecl(
             } else a: {
                 const alloc = alloc: {
                     if (align_inst == .none) {
-                        const tag: Zir.Inst.Tag = if (is_comptime) .alloc_inferred_comptime else .alloc_inferred_mut;
+                        const tag: Zir.Inst.Tag = if (is_comptime)
+                            .alloc_inferred_comptime_mut
+                        else
+                            .alloc_inferred_mut;
                         break :alloc try gz.addNode(tag, node);
                     } else {
                         break :alloc try gz.addAllocExtended(.{
@@ -5441,7 +5452,7 @@ fn forExpr(
     const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr);
 
     const index_ptr = blk: {
-        const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime else .alloc;
+        const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc;
         const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node);
         // initialize to zero
         _ = try parent_gz.addBin(.store, index_ptr, .zero_usize);
src/print_zir.zig
@@ -155,7 +155,7 @@ const Writer = struct {
 
             .alloc,
             .alloc_mut,
-            .alloc_comptime,
+            .alloc_comptime_mut,
             .indexable_ptr_len,
             .anyframe_type,
             .bit_not,
@@ -401,6 +401,7 @@ const Writer = struct {
             .alloc_inferred,
             .alloc_inferred_mut,
             .alloc_inferred_comptime,
+            .alloc_inferred_comptime_mut,
             => try self.writeNode(stream, inst),
 
             .error_value,
src/Sema.zig
@@ -584,9 +584,10 @@ fn analyzeBodyInner(
             .alloc                        => try sema.zirAlloc(block, inst),
             .alloc_inferred               => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)),
             .alloc_inferred_mut           => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)),
-            .alloc_inferred_comptime      => try sema.zirAllocInferredComptime(inst),
+            .alloc_inferred_comptime      => try sema.zirAllocInferredComptime(inst, Type.initTag(.inferred_alloc_const)),
+            .alloc_inferred_comptime_mut  => try sema.zirAllocInferredComptime(inst, Type.initTag(.inferred_alloc_mut)),
             .alloc_mut                    => try sema.zirAllocMut(block, inst),
-            .alloc_comptime               => try sema.zirAllocComptime(block, inst),
+            .alloc_comptime_mut           => try sema.zirAllocComptime(block, inst),
             .anyframe_type                => try sema.zirAnyframeType(block, inst),
             .array_cat                    => try sema.zirArrayCat(block, inst),
             .array_mul                    => try sema.zirArrayMul(block, inst),
@@ -2368,12 +2369,16 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
     return sema.analyzeComptimeAlloc(block, var_ty, 0, ty_src);
 }
 
-fn zirAllocInferredComptime(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+fn zirAllocInferredComptime(
+    sema: *Sema,
+    inst: Zir.Inst.Index,
+    inferred_alloc_ty: Type,
+) CompileError!Air.Inst.Ref {
     const src_node = sema.code.instructions.items(.data)[inst].node;
     const src: LazySrcLoc = .{ .node_offset = src_node };
     sema.src = src;
     return sema.addConstant(
-        Type.initTag(.inferred_alloc_mut),
+        inferred_alloc_ty,
         try Value.Tag.inferred_alloc_comptime.create(sema.arena, undefined),
     );
 }
@@ -2480,6 +2485,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
             const final_elem_ty = try decl.ty.copy(sema.arena);
             const final_ptr_ty = try Type.ptr(sema.arena, .{
                 .pointee_type = final_elem_ty,
+                .mutable = var_is_mut,
                 .@"align" = iac.data.alignment,
                 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
             });
@@ -2500,9 +2506,6 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
             const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
             const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);
 
-            try sema.requireRuntimeBlock(block, src);
-            try sema.resolveTypeLayout(block, ty_src, final_elem_ty);
-
             const final_ptr_ty = try Type.ptr(sema.arena, .{
                 .pointee_type = final_elem_ty,
                 .mutable = var_is_mut,
@@ -2564,6 +2567,9 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
                 return;
             }
 
+            try sema.requireRuntimeBlock(block, src);
+            try sema.resolveTypeLayout(block, ty_src, final_elem_ty);
+
             // Change it to a normal alloc.
             sema.air_instructions.set(ptr_inst, .{
                 .tag = .alloc,
src/Zir.zig
@@ -909,14 +909,18 @@ pub const Inst = struct {
         /// Allocates comptime-mutable memory.
         /// Uses the `un_node` union field. The operand is the type of the allocated object.
         /// The node source location points to a var decl node.
-        alloc_comptime,
+        alloc_comptime_mut,
         /// Same as `alloc` except the type is inferred.
         /// Uses the `node` union field.
         alloc_inferred,
         /// Same as `alloc_inferred` except mutable.
         alloc_inferred_mut,
-        /// Same as `alloc_comptime` except the type is inferred.
+        /// Allocates comptime const memory.
+        /// Uses the `node` union field. The type of the allocated object is inferred.
+        /// The node source location points to a var decl node.
         alloc_inferred_comptime,
+        /// Same as `alloc_comptime_mut` except the type is inferred.
+        alloc_inferred_comptime_mut,
         /// Each `store_to_inferred_ptr` puts the type of the stored value into a set,
         /// and then `resolve_inferred_alloc` triggers peer type resolution on the set.
         /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which
@@ -957,10 +961,11 @@ pub const Inst = struct {
                 .add_sat,
                 .alloc,
                 .alloc_mut,
-                .alloc_comptime,
+                .alloc_comptime_mut,
                 .alloc_inferred,
                 .alloc_inferred_mut,
                 .alloc_inferred_comptime,
+                .alloc_inferred_comptime_mut,
                 .array_cat,
                 .array_mul,
                 .array_type,
@@ -1446,10 +1451,11 @@ pub const Inst = struct {
 
                 .alloc = .un_node,
                 .alloc_mut = .un_node,
-                .alloc_comptime = .un_node,
+                .alloc_comptime_mut = .un_node,
                 .alloc_inferred = .node,
                 .alloc_inferred_mut = .node,
                 .alloc_inferred_comptime = .node,
+                .alloc_inferred_comptime_mut = .node,
                 .resolve_inferred_alloc = .un_node,
 
                 .@"resume" = .un_node,
test/behavior/null.zig
@@ -1,3 +1,4 @@
+const builtin = @import("builtin");
 const std = @import("std");
 const expect = std.testing.expect;
 
@@ -140,3 +141,50 @@ const Particle = struct {
     c: u64,
     d: u64,
 };
+
+test "null literal outside function" {
+    const is_null = here_is_a_null_literal.context == null;
+    try expect(is_null);
+
+    const is_non_null = here_is_a_null_literal.context != null;
+    try expect(!is_non_null);
+}
+
+const SillyStruct = struct {
+    context: ?i32,
+};
+
+const here_is_a_null_literal = SillyStruct{ .context = null };
+
+test "unwrap optional which is field of global var" {
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+
+    struct_with_optional.field = null;
+    if (struct_with_optional.field) |payload| {
+        _ = payload;
+        unreachable;
+    }
+    struct_with_optional.field = 1234;
+    if (struct_with_optional.field) |payload| {
+        try expect(payload == 1234);
+    } else {
+        unreachable;
+    }
+}
+const StructWithOptional = struct {
+    field: ?i32,
+};
+
+var struct_with_optional: StructWithOptional = undefined;
+
+test "optional types" {
+    comptime {
+        const opt_type_struct = StructWithOptionalType{ .t = u8 };
+        try expect(opt_type_struct.t != null and opt_type_struct.t.? == u8);
+    }
+}
+
+const StructWithOptionalType = struct {
+    t: ?type,
+};
test/behavior/null_llvm.zig
@@ -1,36 +0,0 @@
-const std = @import("std");
-const expect = std.testing.expect;
-
-test "null literal outside function" {
-    const is_null = here_is_a_null_literal.context == null;
-    try expect(is_null);
-
-    const is_non_null = here_is_a_null_literal.context != null;
-    try expect(!is_non_null);
-}
-
-const SillyStruct = struct {
-    context: ?i32,
-};
-
-const here_is_a_null_literal = SillyStruct{ .context = null };
-
-const StructWithOptional = struct {
-    field: ?i32,
-};
-
-var struct_with_optional: StructWithOptional = undefined;
-
-test "unwrap optional which is field of global var" {
-    struct_with_optional.field = null;
-    if (struct_with_optional.field) |payload| {
-        _ = payload;
-        unreachable;
-    }
-    struct_with_optional.field = 1234;
-    if (struct_with_optional.field) |payload| {
-        try expect(payload == 1234);
-    } else {
-        unreachable;
-    }
-}
test/behavior/null_stage1.zig
@@ -1,37 +0,0 @@
-const expect = @import("std").testing.expect;
-
-test "if var maybe pointer" {
-    try expect(shouldBeAPlus1(Particle{
-        .a = 14,
-        .b = 1,
-        .c = 1,
-        .d = 1,
-    }) == 15);
-}
-fn shouldBeAPlus1(p: Particle) u64 {
-    var maybe_particle: ?Particle = p;
-    if (maybe_particle) |*particle| {
-        particle.a += 1;
-    }
-    if (maybe_particle) |particle| {
-        return particle.a;
-    }
-    return 0;
-}
-const Particle = struct {
-    a: u64,
-    b: u64,
-    c: u64,
-    d: u64,
-};
-
-test "optional types" {
-    comptime {
-        const opt_type_struct = StructWithOptionalType{ .t = u8 };
-        try expect(opt_type_struct.t != null and opt_type_struct.t.? == u8);
-    }
-}
-
-const StructWithOptionalType = struct {
-    t: ?type,
-};
test/behavior.zig
@@ -105,7 +105,6 @@ test {
                 _ = @import("behavior/math.zig");
                 _ = @import("behavior/maximum_minimum.zig");
                 _ = @import("behavior/merge_error_sets.zig");
-                _ = @import("behavior/null_llvm.zig");
                 _ = @import("behavior/popcount.zig");
                 _ = @import("behavior/saturating_arithmetic.zig");
                 _ = @import("behavior/sizeof_and_typeof.zig");
@@ -156,7 +155,6 @@ test {
                     _ = @import("behavior/ir_block_deps.zig");
                     _ = @import("behavior/misc.zig");
                     _ = @import("behavior/muladd.zig");
-                    _ = @import("behavior/null_stage1.zig");
                     _ = @import("behavior/optional_stage1.zig");
                     _ = @import("behavior/popcount_stage1.zig");
                     _ = @import("behavior/reflection.zig");