| author | |
| committer | |
| log | f0deef1d79db272fa80ef0323b4382ee1936a3e4 |
| tree | f291eeed6b5d81c4de5aaa6f02030a3fab2bd2a0 |
| parent | d43ebf562d852cb7a7ee983d8084584a53d185ee |
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 | 3182 | assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == |
| 3183 | 3183 | Air.refToIndex(coerced_operand).?); |
| 3184 | 3184 | |
| 3185 | // Convert the br operand to a block. | |
| 3186 | const br_operand_ty_ref = try sema.addType(br_operand_ty); | |
| 3185 | // Convert the br instruction to a block instruction that has the coercion | |
| 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 | 3188 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + |
| 3188 | coerce_block.instructions.items.len); | |
| 3189 | try sema.air_instructions.ensureUnusedCapacity(gpa, 2); | |
| 3190 | const sub_block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len); | |
| 3191 | const sub_br_inst = sub_block_inst + 1; | |
| 3192 | sema.air_instructions.items(.data)[br].br.operand = Air.indexToRef(sub_block_inst); | |
| 3193 | sema.air_instructions.appendAssumeCapacity(.{ | |
| 3194 | .tag = .block, | |
| 3195 | .data = .{ .ty_pl = .{ | |
| 3196 | .ty = br_operand_ty_ref, | |
| 3197 | .payload = sema.addExtraAssumeCapacity(Air.Block{ | |
| 3198 | .body_len = @intCast(u32, coerce_block.instructions.items.len), | |
| 3199 | }), | |
| 3200 | } }, | |
| 3201 | }); | |
| 3189 | sub_block_len); | |
| 3190 | try sema.air_instructions.ensureUnusedCapacity(gpa, 1); | |
| 3191 | const sub_br_inst = @intCast(Air.Inst.Index, sema.air_instructions.len); | |
| 3192 | ||
| 3193 | sema.air_instructions.items(.tag)[br] = .block; | |
| 3194 | sema.air_instructions.items(.data)[br] = .{ .ty_pl = .{ | |
| 3195 | .ty = Air.Inst.Ref.noreturn_type, | |
| 3196 | .payload = sema.addExtraAssumeCapacity(Air.Block{ | |
| 3197 | .body_len = sub_block_len, | |
| 3198 | }), | |
| 3199 | } }; | |
| 3202 | 3200 | sema.air_extra.appendSliceAssumeCapacity(coerce_block.instructions.items); |
| 3203 | 3201 | sema.air_extra.appendAssumeCapacity(sub_br_inst); |
| 3202 | ||
| 3204 | 3203 | sema.air_instructions.appendAssumeCapacity(.{ |
| 3205 | 3204 | .tag = .br, |
| 3206 | 3205 | .data = .{ .br = .{ |
| 3207 | .block_inst = sub_block_inst, | |
| 3206 | .block_inst = merges.block_inst, | |
| 3208 | 3207 | .operand = coerced_operand, |
| 3209 | 3208 | } }, |
| 3210 | 3209 | }); |
src/codegen/llvm.zig+6-1| ... | ... | @@ -2063,8 +2063,14 @@ pub const FuncGen = struct { |
| 2063 | 2063 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2064 | 2064 | const extra = self.air.extraData(Air.Block, ty_pl.payload); |
| 2065 | 2065 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; |
| 2066 | const inst_ty = self.air.typeOfIndex(inst); | |
| 2066 | 2067 | const parent_bb = self.context.createBasicBlock("Block"); |
| 2067 | 2068 | |
| 2069 | if (inst_ty.isNoReturn()) { | |
| 2070 | try self.genBody(body); | |
| 2071 | return null; | |
| 2072 | } | |
| 2073 | ||
| 2068 | 2074 | var break_bbs: BreakBasicBlocks = .{}; |
| 2069 | 2075 | defer break_bbs.deinit(self.gpa); |
| 2070 | 2076 | |
| ... | ... | @@ -2084,7 +2090,6 @@ pub const FuncGen = struct { |
| 2084 | 2090 | self.builder.positionBuilderAtEnd(parent_bb); |
| 2085 | 2091 | |
| 2086 | 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 | 2093 | if (!inst_ty.hasCodeGenBits()) return null; |
| 2089 | 2094 | |
| 2090 | 2095 | const raw_llvm_ty = try self.dg.llvmType(inst_ty); |
test/behavior.zig+6-6| ... | ... | @@ -24,25 +24,25 @@ test { |
| 24 | 24 | _ = @import("behavior/defer.zig"); |
| 25 | 25 | _ = @import("behavior/enum.zig"); |
| 26 | 26 | _ = @import("behavior/error.zig"); |
| 27 | _ = @import("behavior/error.zig"); | |
| 28 | _ = @import("behavior/generics.zig"); | |
| 27 | 29 | _ = @import("behavior/hasdecl.zig"); |
| 28 | 30 | _ = @import("behavior/hasfield.zig"); |
| 29 | 31 | _ = @import("behavior/if.zig"); |
| 30 | 32 | _ = @import("behavior/int128.zig"); |
| 33 | _ = @import("behavior/member_func.zig"); | |
| 31 | 34 | _ = @import("behavior/null.zig"); |
| 35 | _ = @import("behavior/optional.zig"); | |
| 32 | 36 | _ = @import("behavior/pointers.zig"); |
| 33 | 37 | _ = @import("behavior/ptrcast.zig"); |
| 34 | 38 | _ = @import("behavior/pub_enum.zig"); |
| 35 | 39 | _ = @import("behavior/struct.zig"); |
| 40 | _ = @import("behavior/this.zig"); | |
| 41 | _ = @import("behavior/translate_c_macros.zig"); | |
| 36 | 42 | _ = @import("behavior/truncate.zig"); |
| 37 | 43 | _ = @import("behavior/underscore.zig"); |
| 38 | 44 | _ = @import("behavior/usingnamespace.zig"); |
| 39 | 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"); | |
| 46 | 46 | |
| 47 | 47 | if (builtin.object_format != .c) { |
| 48 | 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 | 136 | try S.entry(); |
| 137 | 137 | comptime try S.entry(); |
| 138 | 138 | } |
| 139 | ||
| 140 | test "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 | 3 | const expect = testing.expect; |
| 4 | 4 | const expectEqual = testing.expectEqual; |
| 5 | 5 | |
| 6 | test "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 | ||
| 29 | 6 | test "assigning to an unwrapped optional field in an inline loop" { |
| 30 | 7 | comptime var maybe_pos_arg: ?comptime_int = null; |
| 31 | 8 | inline for ("ab") |x| { |