authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-08 20:03:17-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-08 20:03:17-07:00
log7c1061784b8b126633cfe84f46280f7bf72beffc
treeeb99e5eb52d22b9f7d17cfd62dcfb19e3c26296a
parent210ee1067b06c14692432b7887077003e52b2137

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.

8 files changed, 91 insertions(+), 94 deletions(-)

src/AstGen.zig+18-7
...@@ -2084,10 +2084,11 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2084,10 +2084,11 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2084 .param_anytype_comptime,2084 .param_anytype_comptime,
2085 .alloc,2085 .alloc,
2086 .alloc_mut,2086 .alloc_mut,
2087 .alloc_comptime,2087 .alloc_comptime_mut,
2088 .alloc_inferred,2088 .alloc_inferred,
2089 .alloc_inferred_mut,2089 .alloc_inferred_mut,
2090 .alloc_inferred_comptime,2090 .alloc_inferred_comptime,
2091 .alloc_inferred_comptime_mut,
2091 .array_cat,2092 .array_cat,
2092 .array_mul,2093 .array_mul,
2093 .array_type,2094 .array_type,
...@@ -2613,7 +2614,7 @@ fn varDecl(...@@ -2613,7 +2614,7 @@ fn varDecl(
2613 .type_inst = type_inst,2614 .type_inst = type_inst,
2614 .align_inst = align_inst,2615 .align_inst = align_inst,
2615 .is_const = true,2616 .is_const = true,
2616 .is_comptime = false,2617 .is_comptime = gz.force_comptime,
2617 });2618 });
2618 init_scope.instructions_top = gz.instructions.items.len;2619 init_scope.instructions_top = gz.instructions.items.len;
2619 }2620 }
...@@ -2621,14 +2622,18 @@ fn varDecl(...@@ -2621,14 +2622,18 @@ fn varDecl(
2621 } else {2622 } else {
2622 const alloc = if (align_inst == .none) alloc: {2623 const alloc = if (align_inst == .none) alloc: {
2623 init_scope.instructions_top = gz.instructions.items.len;2624 init_scope.instructions_top = gz.instructions.items.len;
2624 break :alloc try init_scope.addNode(.alloc_inferred, node);2625 const tag: Zir.Inst.Tag = if (gz.force_comptime)
2626 .alloc_inferred_comptime
2627 else
2628 .alloc_inferred;
2629 break :alloc try init_scope.addNode(tag, node);
2625 } else alloc: {2630 } else alloc: {
2626 const ref = try gz.addAllocExtended(.{2631 const ref = try gz.addAllocExtended(.{
2627 .node = node,2632 .node = node,
2628 .type_inst = .none,2633 .type_inst = .none,
2629 .align_inst = align_inst,2634 .align_inst = align_inst,
2630 .is_const = true,2635 .is_const = true,
2631 .is_comptime = false,2636 .is_comptime = gz.force_comptime,
2632 });2637 });
2633 init_scope.instructions_top = gz.instructions.items.len;2638 init_scope.instructions_top = gz.instructions.items.len;
2634 break :alloc ref;2639 break :alloc ref;
...@@ -2716,7 +2721,10 @@ fn varDecl(...@@ -2716,7 +2721,10 @@ fn varDecl(
2716 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);2721 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
2717 const alloc = alloc: {2722 const alloc = alloc: {
2718 if (align_inst == .none) {2723 if (align_inst == .none) {
2719 const tag: Zir.Inst.Tag = if (is_comptime) .alloc_comptime else .alloc_mut;2724 const tag: Zir.Inst.Tag = if (is_comptime)
2725 .alloc_comptime_mut
2726 else
2727 .alloc_mut;
2720 break :alloc try gz.addUnNode(tag, type_inst, node);2728 break :alloc try gz.addUnNode(tag, type_inst, node);
2721 } else {2729 } else {
2722 break :alloc try gz.addAllocExtended(.{2730 break :alloc try gz.addAllocExtended(.{
...@@ -2732,7 +2740,10 @@ fn varDecl(...@@ -2732,7 +2740,10 @@ fn varDecl(
2732 } else a: {2740 } else a: {
2733 const alloc = alloc: {2741 const alloc = alloc: {
2734 if (align_inst == .none) {2742 if (align_inst == .none) {
2735 const tag: Zir.Inst.Tag = if (is_comptime) .alloc_inferred_comptime else .alloc_inferred_mut;2743 const tag: Zir.Inst.Tag = if (is_comptime)
2744 .alloc_inferred_comptime_mut
2745 else
2746 .alloc_inferred_mut;
2736 break :alloc try gz.addNode(tag, node);2747 break :alloc try gz.addNode(tag, node);
2737 } else {2748 } else {
2738 break :alloc try gz.addAllocExtended(.{2749 break :alloc try gz.addAllocExtended(.{
...@@ -5441,7 +5452,7 @@ fn forExpr(...@@ -5441,7 +5452,7 @@ fn forExpr(
5441 const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr);5452 const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr);
54425453
5443 const index_ptr = blk: {5454 const index_ptr = blk: {
5444 const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime else .alloc;5455 const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc;
5445 const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node);5456 const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node);
5446 // initialize to zero5457 // initialize to zero
5447 _ = try parent_gz.addBin(.store, index_ptr, .zero_usize);5458 _ = try parent_gz.addBin(.store, index_ptr, .zero_usize);
src/Sema.zig+13-7
...@@ -584,9 +584,10 @@ fn analyzeBodyInner(...@@ -584,9 +584,10 @@ fn analyzeBodyInner(
584 .alloc => try sema.zirAlloc(block, inst),584 .alloc => try sema.zirAlloc(block, inst),
585 .alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)),585 .alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)),
586 .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)),586 .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)),
587 .alloc_inferred_comptime => try sema.zirAllocInferredComptime(inst),587 .alloc_inferred_comptime => try sema.zirAllocInferredComptime(inst, Type.initTag(.inferred_alloc_const)),
588 .alloc_inferred_comptime_mut => try sema.zirAllocInferredComptime(inst, Type.initTag(.inferred_alloc_mut)),
588 .alloc_mut => try sema.zirAllocMut(block, inst),589 .alloc_mut => try sema.zirAllocMut(block, inst),
589 .alloc_comptime => try sema.zirAllocComptime(block, inst),590 .alloc_comptime_mut => try sema.zirAllocComptime(block, inst),
590 .anyframe_type => try sema.zirAnyframeType(block, inst),591 .anyframe_type => try sema.zirAnyframeType(block, inst),
591 .array_cat => try sema.zirArrayCat(block, inst),592 .array_cat => try sema.zirArrayCat(block, inst),
592 .array_mul => try sema.zirArrayMul(block, inst),593 .array_mul => try sema.zirArrayMul(block, inst),
...@@ -2368,12 +2369,16 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr...@@ -2368,12 +2369,16 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
2368 return sema.analyzeComptimeAlloc(block, var_ty, 0, ty_src);2369 return sema.analyzeComptimeAlloc(block, var_ty, 0, ty_src);
2369}2370}
23702371
2371fn zirAllocInferredComptime(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {2372fn zirAllocInferredComptime(
2373 sema: *Sema,
2374 inst: Zir.Inst.Index,
2375 inferred_alloc_ty: Type,
2376) CompileError!Air.Inst.Ref {
2372 const src_node = sema.code.instructions.items(.data)[inst].node;2377 const src_node = sema.code.instructions.items(.data)[inst].node;
2373 const src: LazySrcLoc = .{ .node_offset = src_node };2378 const src: LazySrcLoc = .{ .node_offset = src_node };
2374 sema.src = src;2379 sema.src = src;
2375 return sema.addConstant(2380 return sema.addConstant(
2376 Type.initTag(.inferred_alloc_mut),2381 inferred_alloc_ty,
2377 try Value.Tag.inferred_alloc_comptime.create(sema.arena, undefined),2382 try Value.Tag.inferred_alloc_comptime.create(sema.arena, undefined),
2378 );2383 );
2379}2384}
...@@ -2480,6 +2485,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -2480,6 +2485,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
2480 const final_elem_ty = try decl.ty.copy(sema.arena);2485 const final_elem_ty = try decl.ty.copy(sema.arena);
2481 const final_ptr_ty = try Type.ptr(sema.arena, .{2486 const final_ptr_ty = try Type.ptr(sema.arena, .{
2482 .pointee_type = final_elem_ty,2487 .pointee_type = final_elem_ty,
2488 .mutable = var_is_mut,
2483 .@"align" = iac.data.alignment,2489 .@"align" = iac.data.alignment,
2484 .@"addrspace" = target_util.defaultAddressSpace(target, .local),2490 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
2485 });2491 });
...@@ -2500,9 +2506,6 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -2500,9 +2506,6 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
2500 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;2506 const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
2501 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);2507 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none);
25022508
2503 try sema.requireRuntimeBlock(block, src);
2504 try sema.resolveTypeLayout(block, ty_src, final_elem_ty);
2505
2506 const final_ptr_ty = try Type.ptr(sema.arena, .{2509 const final_ptr_ty = try Type.ptr(sema.arena, .{
2507 .pointee_type = final_elem_ty,2510 .pointee_type = final_elem_ty,
2508 .mutable = var_is_mut,2511 .mutable = var_is_mut,
...@@ -2564,6 +2567,9 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -2564,6 +2567,9 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
2564 return;2567 return;
2565 }2568 }
25662569
2570 try sema.requireRuntimeBlock(block, src);
2571 try sema.resolveTypeLayout(block, ty_src, final_elem_ty);
2572
2567 // Change it to a normal alloc.2573 // Change it to a normal alloc.
2568 sema.air_instructions.set(ptr_inst, .{2574 sema.air_instructions.set(ptr_inst, .{
2569 .tag = .alloc,2575 .tag = .alloc,
src/Zir.zig+10-4
...@@ -909,14 +909,18 @@ pub const Inst = struct {...@@ -909,14 +909,18 @@ pub const Inst = struct {
909 /// Allocates comptime-mutable memory.909 /// Allocates comptime-mutable memory.
910 /// Uses the `un_node` union field. The operand is the type of the allocated object.910 /// Uses the `un_node` union field. The operand is the type of the allocated object.
911 /// The node source location points to a var decl node.911 /// The node source location points to a var decl node.
912 alloc_comptime,912 alloc_comptime_mut,
913 /// Same as `alloc` except the type is inferred.913 /// Same as `alloc` except the type is inferred.
914 /// Uses the `node` union field.914 /// Uses the `node` union field.
915 alloc_inferred,915 alloc_inferred,
916 /// Same as `alloc_inferred` except mutable.916 /// Same as `alloc_inferred` except mutable.
917 alloc_inferred_mut,917 alloc_inferred_mut,
918 /// Same as `alloc_comptime` except the type is inferred.918 /// Allocates comptime const memory.
919 /// Uses the `node` union field. The type of the allocated object is inferred.
920 /// The node source location points to a var decl node.
919 alloc_inferred_comptime,921 alloc_inferred_comptime,
922 /// Same as `alloc_comptime_mut` except the type is inferred.
923 alloc_inferred_comptime_mut,
920 /// Each `store_to_inferred_ptr` puts the type of the stored value into a set,924 /// Each `store_to_inferred_ptr` puts the type of the stored value into a set,
921 /// and then `resolve_inferred_alloc` triggers peer type resolution on the set.925 /// and then `resolve_inferred_alloc` triggers peer type resolution on the set.
922 /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which926 /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which
...@@ -957,10 +961,11 @@ pub const Inst = struct {...@@ -957,10 +961,11 @@ pub const Inst = struct {
957 .add_sat,961 .add_sat,
958 .alloc,962 .alloc,
959 .alloc_mut,963 .alloc_mut,
960 .alloc_comptime,964 .alloc_comptime_mut,
961 .alloc_inferred,965 .alloc_inferred,
962 .alloc_inferred_mut,966 .alloc_inferred_mut,
963 .alloc_inferred_comptime,967 .alloc_inferred_comptime,
968 .alloc_inferred_comptime_mut,
964 .array_cat,969 .array_cat,
965 .array_mul,970 .array_mul,
966 .array_type,971 .array_type,
...@@ -1446,10 +1451,11 @@ pub const Inst = struct {...@@ -1446,10 +1451,11 @@ pub const Inst = struct {
14461451
1447 .alloc = .un_node,1452 .alloc = .un_node,
1448 .alloc_mut = .un_node,1453 .alloc_mut = .un_node,
1449 .alloc_comptime = .un_node,1454 .alloc_comptime_mut = .un_node,
1450 .alloc_inferred = .node,1455 .alloc_inferred = .node,
1451 .alloc_inferred_mut = .node,1456 .alloc_inferred_mut = .node,
1452 .alloc_inferred_comptime = .node,1457 .alloc_inferred_comptime = .node,
1458 .alloc_inferred_comptime_mut = .node,
1453 .resolve_inferred_alloc = .un_node,1459 .resolve_inferred_alloc = .un_node,
14541460
1455 .@"resume" = .un_node,1461 .@"resume" = .un_node,
src/print_zir.zig+2-1
...@@ -155,7 +155,7 @@ const Writer = struct {...@@ -155,7 +155,7 @@ const Writer = struct {
155155
156 .alloc,156 .alloc,
157 .alloc_mut,157 .alloc_mut,
158 .alloc_comptime,158 .alloc_comptime_mut,
159 .indexable_ptr_len,159 .indexable_ptr_len,
160 .anyframe_type,160 .anyframe_type,
161 .bit_not,161 .bit_not,
...@@ -401,6 +401,7 @@ const Writer = struct {...@@ -401,6 +401,7 @@ const Writer = struct {
401 .alloc_inferred,401 .alloc_inferred,
402 .alloc_inferred_mut,402 .alloc_inferred_mut,
403 .alloc_inferred_comptime,403 .alloc_inferred_comptime,
404 .alloc_inferred_comptime_mut,
404 => try self.writeNode(stream, inst),405 => try self.writeNode(stream, inst),
405406
406 .error_value,407 .error_value,
test/behavior.zig-2
...@@ -105,7 +105,6 @@ test {...@@ -105,7 +105,6 @@ test {
105 _ = @import("behavior/math.zig");105 _ = @import("behavior/math.zig");
106 _ = @import("behavior/maximum_minimum.zig");106 _ = @import("behavior/maximum_minimum.zig");
107 _ = @import("behavior/merge_error_sets.zig");107 _ = @import("behavior/merge_error_sets.zig");
108 _ = @import("behavior/null_llvm.zig");
109 _ = @import("behavior/popcount.zig");108 _ = @import("behavior/popcount.zig");
110 _ = @import("behavior/saturating_arithmetic.zig");109 _ = @import("behavior/saturating_arithmetic.zig");
111 _ = @import("behavior/sizeof_and_typeof.zig");110 _ = @import("behavior/sizeof_and_typeof.zig");
...@@ -156,7 +155,6 @@ test {...@@ -156,7 +155,6 @@ test {
156 _ = @import("behavior/ir_block_deps.zig");155 _ = @import("behavior/ir_block_deps.zig");
157 _ = @import("behavior/misc.zig");156 _ = @import("behavior/misc.zig");
158 _ = @import("behavior/muladd.zig");157 _ = @import("behavior/muladd.zig");
159 _ = @import("behavior/null_stage1.zig");
160 _ = @import("behavior/optional_stage1.zig");158 _ = @import("behavior/optional_stage1.zig");
161 _ = @import("behavior/popcount_stage1.zig");159 _ = @import("behavior/popcount_stage1.zig");
162 _ = @import("behavior/reflection.zig");160 _ = @import("behavior/reflection.zig");
test/behavior/null.zig+48
...@@ -1,3 +1,4 @@...@@ -1,3 +1,4 @@
1const builtin = @import("builtin");
1const std = @import("std");2const std = @import("std");
2const expect = std.testing.expect;3const expect = std.testing.expect;
34
...@@ -140,3 +141,50 @@ const Particle = struct {...@@ -140,3 +141,50 @@ const Particle = struct {
140 c: u64,141 c: u64,
141 d: u64,142 d: u64,
142};143};
144
145test "null literal outside function" {
146 const is_null = here_is_a_null_literal.context == null;
147 try expect(is_null);
148
149 const is_non_null = here_is_a_null_literal.context != null;
150 try expect(!is_non_null);
151}
152
153const SillyStruct = struct {
154 context: ?i32,
155};
156
157const here_is_a_null_literal = SillyStruct{ .context = null };
158
159test "unwrap optional which is field of global var" {
160 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
161 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
162
163 struct_with_optional.field = null;
164 if (struct_with_optional.field) |payload| {
165 _ = payload;
166 unreachable;
167 }
168 struct_with_optional.field = 1234;
169 if (struct_with_optional.field) |payload| {
170 try expect(payload == 1234);
171 } else {
172 unreachable;
173 }
174}
175const StructWithOptional = struct {
176 field: ?i32,
177};
178
179var struct_with_optional: StructWithOptional = undefined;
180
181test "optional types" {
182 comptime {
183 const opt_type_struct = StructWithOptionalType{ .t = u8 };
184 try expect(opt_type_struct.t != null and opt_type_struct.t.? == u8);
185 }
186}
187
188const StructWithOptionalType = struct {
189 t: ?type,
190};
test/behavior/null_llvm.zig deleted-36
...@@ -1,36 +0,0 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "null literal outside function" {
5 const is_null = here_is_a_null_literal.context == null;
6 try expect(is_null);
7
8 const is_non_null = here_is_a_null_literal.context != null;
9 try expect(!is_non_null);
10}
11
12const SillyStruct = struct {
13 context: ?i32,
14};
15
16const here_is_a_null_literal = SillyStruct{ .context = null };
17
18const StructWithOptional = struct {
19 field: ?i32,
20};
21
22var struct_with_optional: StructWithOptional = undefined;
23
24test "unwrap optional which is field of global var" {
25 struct_with_optional.field = null;
26 if (struct_with_optional.field) |payload| {
27 _ = payload;
28 unreachable;
29 }
30 struct_with_optional.field = 1234;
31 if (struct_with_optional.field) |payload| {
32 try expect(payload == 1234);
33 } else {
34 unreachable;
35 }
36}
test/behavior/null_stage1.zig deleted-37
...@@ -1,37 +0,0 @@
1const expect = @import("std").testing.expect;
2
3test "if var maybe pointer" {
4 try expect(shouldBeAPlus1(Particle{
5 .a = 14,
6 .b = 1,
7 .c = 1,
8 .d = 1,
9 }) == 15);
10}
11fn shouldBeAPlus1(p: Particle) u64 {
12 var maybe_particle: ?Particle = p;
13 if (maybe_particle) |*particle| {
14 particle.a += 1;
15 }
16 if (maybe_particle) |particle| {
17 return particle.a;
18 }
19 return 0;
20}
21const Particle = struct {
22 a: u64,
23 b: u64,
24 c: u64,
25 d: u64,
26};
27
28test "optional types" {
29 comptime {
30 const opt_type_struct = StructWithOptionalType{ .t = u8 };
31 try expect(opt_type_struct.t != null and opt_type_struct.t.? == u8);
32 }
33}
34
35const StructWithOptionalType = struct {
36 t: ?type,
37};