From 7c1061784b8b126633cfe84f46280f7bf72beffc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Feb 2022 20:03:17 -0700 Subject: [PATCH] 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. --- src/AstGen.zig | 25 +++++++++++++----- src/Sema.zig | 20 ++++++++++----- src/Zir.zig | 14 +++++++--- src/print_zir.zig | 3 ++- test/behavior.zig | 2 -- test/behavior/null.zig | 48 +++++++++++++++++++++++++++++++++++ test/behavior/null_llvm.zig | 36 -------------------------- test/behavior/null_stage1.zig | 37 --------------------------- 8 files changed, 91 insertions(+), 94 deletions(-) delete mode 100644 test/behavior/null_llvm.zig delete mode 100644 test/behavior/null_stage1.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index 5f6d05b7f5..9bc10f25e8 100644 --- a/src/AstGen.zig +++ b/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); diff --git a/src/Sema.zig b/src/Sema.zig index 72dfb4420b..69b9adc54b 100644 --- a/src/Sema.zig +++ b/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, diff --git a/src/Zir.zig b/src/Zir.zig index dbdaa0f5fb..b7e3e60916 100644 --- a/src/Zir.zig +++ b/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, diff --git a/src/print_zir.zig b/src/print_zir.zig index 1954772e37..9c79ad1a37 100644 --- a/src/print_zir.zig +++ b/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, diff --git a/test/behavior.zig b/test/behavior.zig index c15de44597..c177dd8634 100644 --- a/test/behavior.zig +++ b/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"); diff --git a/test/behavior/null.zig b/test/behavior/null.zig index 861921d39c..35ecafff80 100644 --- a/test/behavior/null.zig +++ b/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, +}; diff --git a/test/behavior/null_llvm.zig b/test/behavior/null_llvm.zig deleted file mode 100644 index 25d3c0ca1c..0000000000 --- a/test/behavior/null_llvm.zig +++ /dev/null @@ -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; - } -} diff --git a/test/behavior/null_stage1.zig b/test/behavior/null_stage1.zig deleted file mode 100644 index 2b8feea242..0000000000 --- a/test/behavior/null_stage1.zig +++ /dev/null @@ -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, -};