authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-26 23:17:01-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-26 23:17:01-07:00
logf0deef1d79db272fa80ef0323b4382ee1936a3e4
treef291eeed6b5d81c4de5aaa6f02030a3fab2bd2a0
parentd43ebf562d852cb7a7ee983d8084584a53d185ee

Sema: fix analyzeBlockBody logic

Previously, when a coercion needed to be inserted into a break instruction, the `br` AIR instruction would be rewritten so that the block operand was a sub-block that did the coercion. The problem is that the sub-block itself was never added to the parent block, resulting in the `br` instruction operand being a bad reference. Now, the `br` AIR instruction that needs to have coercion instructions added is replaced with the sub-block itself with type `noreturn`, and then the sub-block has the coercion instructions and a new `br` instruction that breaks from the original block. LLVM backend needed to be fixed to lower `noreturn` blocks without emitting an unused LLVM basic block.

5 files changed, 51 insertions(+), 47 deletions(-)

src/Sema.zig+16-17
...@@ -3182,29 +3182,28 @@ fn analyzeBlockBody(...@@ -3182,29 +3182,28 @@ fn analyzeBlockBody(
3182 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] ==3182 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] ==
3183 Air.refToIndex(coerced_operand).?);3183 Air.refToIndex(coerced_operand).?);
31843184
3185 // Convert the br operand to a block.3185 // Convert the br instruction to a block instruction that has the coercion
3186 const br_operand_ty_ref = try sema.addType(br_operand_ty);3186 // and then a new br inside that returns the coerced instruction.
3187 const sub_block_len = @intCast(u32, coerce_block.instructions.items.len + 1);
3187 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +3188 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
3188 coerce_block.instructions.items.len);3189 sub_block_len);
3189 try sema.air_instructions.ensureUnusedCapacity(gpa, 2);3190 try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
3190 const sub_block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);3191 const sub_br_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
3191 const sub_br_inst = sub_block_inst + 1;3192
3192 sema.air_instructions.items(.data)[br].br.operand = Air.indexToRef(sub_block_inst);3193 sema.air_instructions.items(.tag)[br] = .block;
3193 sema.air_instructions.appendAssumeCapacity(.{3194 sema.air_instructions.items(.data)[br] = .{ .ty_pl = .{
3194 .tag = .block,3195 .ty = Air.Inst.Ref.noreturn_type,
3195 .data = .{ .ty_pl = .{3196 .payload = sema.addExtraAssumeCapacity(Air.Block{
3196 .ty = br_operand_ty_ref,3197 .body_len = sub_block_len,
3197 .payload = sema.addExtraAssumeCapacity(Air.Block{3198 }),
3198 .body_len = @intCast(u32, coerce_block.instructions.items.len),3199 } };
3199 }),
3200 } },
3201 });
3202 sema.air_extra.appendSliceAssumeCapacity(coerce_block.instructions.items);3200 sema.air_extra.appendSliceAssumeCapacity(coerce_block.instructions.items);
3203 sema.air_extra.appendAssumeCapacity(sub_br_inst);3201 sema.air_extra.appendAssumeCapacity(sub_br_inst);
3202
3204 sema.air_instructions.appendAssumeCapacity(.{3203 sema.air_instructions.appendAssumeCapacity(.{
3205 .tag = .br,3204 .tag = .br,
3206 .data = .{ .br = .{3205 .data = .{ .br = .{
3207 .block_inst = sub_block_inst,3206 .block_inst = merges.block_inst,
3208 .operand = coerced_operand,3207 .operand = coerced_operand,
3209 } },3208 } },
3210 });3209 });
src/codegen/llvm.zig+6-1
...@@ -2063,8 +2063,14 @@ pub const FuncGen = struct {...@@ -2063,8 +2063,14 @@ pub const FuncGen = struct {
2063 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2063 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2064 const extra = self.air.extraData(Air.Block, ty_pl.payload);2064 const extra = self.air.extraData(Air.Block, ty_pl.payload);
2065 const body = self.air.extra[extra.end..][0..extra.data.body_len];2065 const body = self.air.extra[extra.end..][0..extra.data.body_len];
2066 const inst_ty = self.air.typeOfIndex(inst);
2066 const parent_bb = self.context.createBasicBlock("Block");2067 const parent_bb = self.context.createBasicBlock("Block");
20672068
2069 if (inst_ty.isNoReturn()) {
2070 try self.genBody(body);
2071 return null;
2072 }
2073
2068 var break_bbs: BreakBasicBlocks = .{};2074 var break_bbs: BreakBasicBlocks = .{};
2069 defer break_bbs.deinit(self.gpa);2075 defer break_bbs.deinit(self.gpa);
20702076
...@@ -2084,7 +2090,6 @@ pub const FuncGen = struct {...@@ -2084,7 +2090,6 @@ pub const FuncGen = struct {
2084 self.builder.positionBuilderAtEnd(parent_bb);2090 self.builder.positionBuilderAtEnd(parent_bb);
20852091
2086 // If the block does not return a value, we dont have to create a phi node.2092 // If the block does not return a value, we dont have to create a phi node.
2087 const inst_ty = self.air.typeOfIndex(inst);
2088 if (!inst_ty.hasCodeGenBits()) return null;2093 if (!inst_ty.hasCodeGenBits()) return null;
20892094
2090 const raw_llvm_ty = try self.dg.llvmType(inst_ty);2095 const raw_llvm_ty = try self.dg.llvmType(inst_ty);
test/behavior.zig+6-6
...@@ -24,25 +24,25 @@ test {...@@ -24,25 +24,25 @@ test {
24 _ = @import("behavior/defer.zig");24 _ = @import("behavior/defer.zig");
25 _ = @import("behavior/enum.zig");25 _ = @import("behavior/enum.zig");
26 _ = @import("behavior/error.zig");26 _ = @import("behavior/error.zig");
27 _ = @import("behavior/error.zig");
28 _ = @import("behavior/generics.zig");
27 _ = @import("behavior/hasdecl.zig");29 _ = @import("behavior/hasdecl.zig");
28 _ = @import("behavior/hasfield.zig");30 _ = @import("behavior/hasfield.zig");
29 _ = @import("behavior/if.zig");31 _ = @import("behavior/if.zig");
30 _ = @import("behavior/int128.zig");32 _ = @import("behavior/int128.zig");
33 _ = @import("behavior/member_func.zig");
31 _ = @import("behavior/null.zig");34 _ = @import("behavior/null.zig");
35 _ = @import("behavior/optional.zig");
32 _ = @import("behavior/pointers.zig");36 _ = @import("behavior/pointers.zig");
33 _ = @import("behavior/ptrcast.zig");37 _ = @import("behavior/ptrcast.zig");
34 _ = @import("behavior/pub_enum.zig");38 _ = @import("behavior/pub_enum.zig");
35 _ = @import("behavior/struct.zig");39 _ = @import("behavior/struct.zig");
40 _ = @import("behavior/this.zig");
41 _ = @import("behavior/translate_c_macros.zig");
36 _ = @import("behavior/truncate.zig");42 _ = @import("behavior/truncate.zig");
37 _ = @import("behavior/underscore.zig");43 _ = @import("behavior/underscore.zig");
38 _ = @import("behavior/usingnamespace.zig");44 _ = @import("behavior/usingnamespace.zig");
39 _ = @import("behavior/while.zig");45 _ = @import("behavior/while.zig");
40 _ = @import("behavior/this.zig");
41 _ = @import("behavior/member_func.zig");
42 _ = @import("behavior/translate_c_macros.zig");
43 _ = @import("behavior/generics.zig");
44 _ = @import("behavior/error.zig");
45 _ = @import("behavior/optional.zig");
4646
47 if (builtin.object_format != .c) {47 if (builtin.object_format != .c) {
48 // Tests that pass for stage1 and stage2 but not the C backend.48 // Tests that pass for stage1 and stage2 but not the C backend.
test/behavior/optional.zig+23
...@@ -136,3 +136,26 @@ test "unwrap function call with optional pointer return value" {...@@ -136,3 +136,26 @@ test "unwrap function call with optional pointer return value" {
136 try S.entry();136 try S.entry();
137 comptime try S.entry();137 comptime try S.entry();
138}138}
139
140test "nested orelse" {
141 const S = struct {
142 fn entry() !void {
143 try expect(func() == null);
144 }
145 fn maybe() ?Foo {
146 return null;
147 }
148 fn func() ?Foo {
149 const x = maybe() orelse
150 maybe() orelse
151 return null;
152 _ = x;
153 unreachable;
154 }
155 const Foo = struct {
156 field: i32,
157 };
158 };
159 try S.entry();
160 comptime try S.entry();
161}
test/behavior/optional_stage1.zig-23
...@@ -3,29 +3,6 @@ const testing = std.testing;...@@ -3,29 +3,6 @@ const testing = std.testing;
3const expect = testing.expect;3const expect = testing.expect;
4const expectEqual = testing.expectEqual;4const expectEqual = testing.expectEqual;
55
6test "nested orelse" {
7 const S = struct {
8 fn entry() !void {
9 try expect(func() == null);
10 }
11 fn maybe() ?Foo {
12 return null;
13 }
14 fn func() ?Foo {
15 const x = maybe() orelse
16 maybe() orelse
17 return null;
18 _ = x;
19 unreachable;
20 }
21 const Foo = struct {
22 field: i32,
23 };
24 };
25 try S.entry();
26 comptime try S.entry();
27}
28
29test "assigning to an unwrapped optional field in an inline loop" {6test "assigning to an unwrapped optional field in an inline loop" {
30 comptime var maybe_pos_arg: ?comptime_int = null;7 comptime var maybe_pos_arg: ?comptime_int = null;
31 inline for ("ab") |x| {8 inline for ("ab") |x| {