| author | |
| committer | |
| log | 7c1061784b8b126633cfe84f46280f7bf72beffc |
| tree | eb99e5eb52d22b9f7d17cfd62dcfb19e3c26296a |
| parent | 210ee1067b06c14692432b7887077003e52b2137 |
`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 | 2084 | .param_anytype_comptime, |
| 2085 | 2085 | .alloc, |
| 2086 | 2086 | .alloc_mut, |
| 2087 | .alloc_comptime, | |
| 2087 | .alloc_comptime_mut, | |
| 2088 | 2088 | .alloc_inferred, |
| 2089 | 2089 | .alloc_inferred_mut, |
| 2090 | 2090 | .alloc_inferred_comptime, |
| 2091 | .alloc_inferred_comptime_mut, | |
| 2091 | 2092 | .array_cat, |
| 2092 | 2093 | .array_mul, |
| 2093 | 2094 | .array_type, |
| ... | ... | @@ -2613,7 +2614,7 @@ fn varDecl( |
| 2613 | 2614 | .type_inst = type_inst, |
| 2614 | 2615 | .align_inst = align_inst, |
| 2615 | 2616 | .is_const = true, |
| 2616 | .is_comptime = false, | |
| 2617 | .is_comptime = gz.force_comptime, | |
| 2617 | 2618 | }); |
| 2618 | 2619 | init_scope.instructions_top = gz.instructions.items.len; |
| 2619 | 2620 | } |
| ... | ... | @@ -2621,14 +2622,18 @@ fn varDecl( |
| 2621 | 2622 | } else { |
| 2622 | 2623 | const alloc = if (align_inst == .none) alloc: { |
| 2623 | 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 | 2630 | } else alloc: { |
| 2626 | 2631 | const ref = try gz.addAllocExtended(.{ |
| 2627 | 2632 | .node = node, |
| 2628 | 2633 | .type_inst = .none, |
| 2629 | 2634 | .align_inst = align_inst, |
| 2630 | 2635 | .is_const = true, |
| 2631 | .is_comptime = false, | |
| 2636 | .is_comptime = gz.force_comptime, | |
| 2632 | 2637 | }); |
| 2633 | 2638 | init_scope.instructions_top = gz.instructions.items.len; |
| 2634 | 2639 | break :alloc ref; |
| ... | ... | @@ -2716,7 +2721,10 @@ fn varDecl( |
| 2716 | 2721 | const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node); |
| 2717 | 2722 | const alloc = alloc: { |
| 2718 | 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 | 2728 | break :alloc try gz.addUnNode(tag, type_inst, node); |
| 2721 | 2729 | } else { |
| 2722 | 2730 | break :alloc try gz.addAllocExtended(.{ |
| ... | ... | @@ -2732,7 +2740,10 @@ fn varDecl( |
| 2732 | 2740 | } else a: { |
| 2733 | 2741 | const alloc = alloc: { |
| 2734 | 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 | 2747 | break :alloc try gz.addNode(tag, node); |
| 2737 | 2748 | } else { |
| 2738 | 2749 | break :alloc try gz.addAllocExtended(.{ |
| ... | ... | @@ -5441,7 +5452,7 @@ fn forExpr( |
| 5441 | 5452 | const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr); |
| 5442 | 5453 | |
| 5443 | 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 | 5456 | const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node); |
| 5446 | 5457 | // initialize to zero |
| 5447 | 5458 | _ = try parent_gz.addBin(.store, index_ptr, .zero_usize); |
src/Sema.zig+13-7| ... | ... | @@ -584,9 +584,10 @@ fn analyzeBodyInner( |
| 584 | 584 | .alloc => try sema.zirAlloc(block, inst), |
| 585 | 585 | .alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)), |
| 586 | 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 | 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 | 591 | .anyframe_type => try sema.zirAnyframeType(block, inst), |
| 591 | 592 | .array_cat => try sema.zirArrayCat(block, inst), |
| 592 | 593 | .array_mul => try sema.zirArrayMul(block, inst), |
| ... | ... | @@ -2368,12 +2369,16 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr |
| 2368 | 2369 | return sema.analyzeComptimeAlloc(block, var_ty, 0, ty_src); |
| 2369 | 2370 | } |
| 2370 | 2371 | |
| 2371 | fn zirAllocInferredComptime(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 2372 | fn zirAllocInferredComptime( | |
| 2373 | sema: *Sema, | |
| 2374 | inst: Zir.Inst.Index, | |
| 2375 | inferred_alloc_ty: Type, | |
| 2376 | ) CompileError!Air.Inst.Ref { | |
| 2372 | 2377 | const src_node = sema.code.instructions.items(.data)[inst].node; |
| 2373 | 2378 | const src: LazySrcLoc = .{ .node_offset = src_node }; |
| 2374 | 2379 | sema.src = src; |
| 2375 | 2380 | return sema.addConstant( |
| 2376 | Type.initTag(.inferred_alloc_mut), | |
| 2381 | inferred_alloc_ty, | |
| 2377 | 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 | 2485 | const final_elem_ty = try decl.ty.copy(sema.arena); |
| 2481 | 2486 | const final_ptr_ty = try Type.ptr(sema.arena, .{ |
| 2482 | 2487 | .pointee_type = final_elem_ty, |
| 2488 | .mutable = var_is_mut, | |
| 2483 | 2489 | .@"align" = iac.data.alignment, |
| 2484 | 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 | 2506 | const peer_inst_list = inferred_alloc.data.stored_inst_list.items; |
| 2501 | 2507 | const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_inst_list, .none); |
| 2502 | 2508 | |
| 2503 | try sema.requireRuntimeBlock(block, src); | |
| 2504 | try sema.resolveTypeLayout(block, ty_src, final_elem_ty); | |
| 2505 | ||
| 2506 | 2509 | const final_ptr_ty = try Type.ptr(sema.arena, .{ |
| 2507 | 2510 | .pointee_type = final_elem_ty, |
| 2508 | 2511 | .mutable = var_is_mut, |
| ... | ... | @@ -2564,6 +2567,9 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com |
| 2564 | 2567 | return; |
| 2565 | 2568 | } |
| 2566 | 2569 | |
| 2570 | try sema.requireRuntimeBlock(block, src); | |
| 2571 | try sema.resolveTypeLayout(block, ty_src, final_elem_ty); | |
| 2572 | ||
| 2567 | 2573 | // Change it to a normal alloc. |
| 2568 | 2574 | sema.air_instructions.set(ptr_inst, .{ |
| 2569 | 2575 | .tag = .alloc, |
src/Zir.zig+10-4| ... | ... | @@ -909,14 +909,18 @@ pub const Inst = struct { |
| 909 | 909 | /// Allocates comptime-mutable memory. |
| 910 | 910 | /// Uses the `un_node` union field. The operand is the type of the allocated object. |
| 911 | 911 | /// The node source location points to a var decl node. |
| 912 | alloc_comptime, | |
| 912 | alloc_comptime_mut, | |
| 913 | 913 | /// Same as `alloc` except the type is inferred. |
| 914 | 914 | /// Uses the `node` union field. |
| 915 | 915 | alloc_inferred, |
| 916 | 916 | /// Same as `alloc_inferred` except mutable. |
| 917 | 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 | 921 | alloc_inferred_comptime, |
| 922 | /// Same as `alloc_comptime_mut` except the type is inferred. | |
| 923 | alloc_inferred_comptime_mut, | |
| 920 | 924 | /// Each `store_to_inferred_ptr` puts the type of the stored value into a set, |
| 921 | 925 | /// and then `resolve_inferred_alloc` triggers peer type resolution on the set. |
| 922 | 926 | /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which |
| ... | ... | @@ -957,10 +961,11 @@ pub const Inst = struct { |
| 957 | 961 | .add_sat, |
| 958 | 962 | .alloc, |
| 959 | 963 | .alloc_mut, |
| 960 | .alloc_comptime, | |
| 964 | .alloc_comptime_mut, | |
| 961 | 965 | .alloc_inferred, |
| 962 | 966 | .alloc_inferred_mut, |
| 963 | 967 | .alloc_inferred_comptime, |
| 968 | .alloc_inferred_comptime_mut, | |
| 964 | 969 | .array_cat, |
| 965 | 970 | .array_mul, |
| 966 | 971 | .array_type, |
| ... | ... | @@ -1446,10 +1451,11 @@ pub const Inst = struct { |
| 1446 | 1451 | |
| 1447 | 1452 | .alloc = .un_node, |
| 1448 | 1453 | .alloc_mut = .un_node, |
| 1449 | .alloc_comptime = .un_node, | |
| 1454 | .alloc_comptime_mut = .un_node, | |
| 1450 | 1455 | .alloc_inferred = .node, |
| 1451 | 1456 | .alloc_inferred_mut = .node, |
| 1452 | 1457 | .alloc_inferred_comptime = .node, |
| 1458 | .alloc_inferred_comptime_mut = .node, | |
| 1453 | 1459 | .resolve_inferred_alloc = .un_node, |
| 1454 | 1460 | |
| 1455 | 1461 | .@"resume" = .un_node, |
src/print_zir.zig+2-1| ... | ... | @@ -155,7 +155,7 @@ const Writer = struct { |
| 155 | 155 | |
| 156 | 156 | .alloc, |
| 157 | 157 | .alloc_mut, |
| 158 | .alloc_comptime, | |
| 158 | .alloc_comptime_mut, | |
| 159 | 159 | .indexable_ptr_len, |
| 160 | 160 | .anyframe_type, |
| 161 | 161 | .bit_not, |
| ... | ... | @@ -401,6 +401,7 @@ const Writer = struct { |
| 401 | 401 | .alloc_inferred, |
| 402 | 402 | .alloc_inferred_mut, |
| 403 | 403 | .alloc_inferred_comptime, |
| 404 | .alloc_inferred_comptime_mut, | |
| 404 | 405 | => try self.writeNode(stream, inst), |
| 405 | 406 | |
| 406 | 407 | .error_value, |
test/behavior.zig-2| ... | ... | @@ -105,7 +105,6 @@ test { |
| 105 | 105 | _ = @import("behavior/math.zig"); |
| 106 | 106 | _ = @import("behavior/maximum_minimum.zig"); |
| 107 | 107 | _ = @import("behavior/merge_error_sets.zig"); |
| 108 | _ = @import("behavior/null_llvm.zig"); | |
| 109 | 108 | _ = @import("behavior/popcount.zig"); |
| 110 | 109 | _ = @import("behavior/saturating_arithmetic.zig"); |
| 111 | 110 | _ = @import("behavior/sizeof_and_typeof.zig"); |
| ... | ... | @@ -156,7 +155,6 @@ test { |
| 156 | 155 | _ = @import("behavior/ir_block_deps.zig"); |
| 157 | 156 | _ = @import("behavior/misc.zig"); |
| 158 | 157 | _ = @import("behavior/muladd.zig"); |
| 159 | _ = @import("behavior/null_stage1.zig"); | |
| 160 | 158 | _ = @import("behavior/optional_stage1.zig"); |
| 161 | 159 | _ = @import("behavior/popcount_stage1.zig"); |
| 162 | 160 | _ = @import("behavior/reflection.zig"); |
test/behavior/null.zig+48| ... | ... | @@ -1,3 +1,4 @@ |
| 1 | const builtin = @import("builtin"); | |
| 1 | 2 | const std = @import("std"); |
| 2 | 3 | const expect = std.testing.expect; |
| 3 | 4 | |
| ... | ... | @@ -140,3 +141,50 @@ const Particle = struct { |
| 140 | 141 | c: u64, |
| 141 | 142 | d: u64, |
| 142 | 143 | }; |
| 144 | ||
| 145 | test "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 | ||
| 153 | const SillyStruct = struct { | |
| 154 | context: ?i32, | |
| 155 | }; | |
| 156 | ||
| 157 | const here_is_a_null_literal = SillyStruct{ .context = null }; | |
| 158 | ||
| 159 | test "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 | } | |
| 175 | const StructWithOptional = struct { | |
| 176 | field: ?i32, | |
| 177 | }; | |
| 178 | ||
| 179 | var struct_with_optional: StructWithOptional = undefined; | |
| 180 | ||
| 181 | test "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 | ||
| 188 | const StructWithOptionalType = struct { | |
| 189 | t: ?type, | |
| 190 | }; |
test/behavior/null_llvm.zig deleted-36| ... | ... | @@ -1,36 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | ||
| 4 | test "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 | ||
| 12 | const SillyStruct = struct { | |
| 13 | context: ?i32, | |
| 14 | }; | |
| 15 | ||
| 16 | const here_is_a_null_literal = SillyStruct{ .context = null }; | |
| 17 | ||
| 18 | const StructWithOptional = struct { | |
| 19 | field: ?i32, | |
| 20 | }; | |
| 21 | ||
| 22 | var struct_with_optional: StructWithOptional = undefined; | |
| 23 | ||
| 24 | test "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 @@ |
| 1 | const expect = @import("std").testing.expect; | |
| 2 | ||
| 3 | test "if var maybe pointer" { | |
| 4 | try expect(shouldBeAPlus1(Particle{ | |
| 5 | .a = 14, | |
| 6 | .b = 1, | |
| 7 | .c = 1, | |
| 8 | .d = 1, | |
| 9 | }) == 15); | |
| 10 | } | |
| 11 | fn 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 | } | |
| 21 | const Particle = struct { | |
| 22 | a: u64, | |
| 23 | b: u64, | |
| 24 | c: u64, | |
| 25 | d: u64, | |
| 26 | }; | |
| 27 | ||
| 28 | test "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 | ||
| 35 | const StructWithOptionalType = struct { | |
| 36 | t: ?type, | |
| 37 | }; |