authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-26 15:44:40+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-27 18:33:23+03:00
log5baaf90e3c10d197131eaf5908da4401b9a07e7b
treea84610fc44febd93350f2b9b3e7b0f6d842e2cbf
parentcccc4c38273eb3e937c3572952b5609b51010baa

Sema: implement non-special inline switch prongs


4 files changed, 212 insertions(+), 10 deletions(-)

src/Module.zig+8-8
......@@ -2445,8 +2445,8 @@ pub const SrcLoc = struct {
24452445 const case_nodes = tree.extra_data[extra.start..extra.end];
24462446 for (case_nodes) |case_node| {
24472447 const case = switch (node_tags[case_node]) {
2448 .switch_case_one => tree.switchCaseOne(case_node),
2449 .switch_case => tree.switchCase(case_node),
2448 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
2449 .switch_case, .switch_case_inline => tree.switchCase(case_node),
24502450 else => unreachable,
24512451 };
24522452 const is_special = (case.ast.values.len == 0) or
......@@ -2469,8 +2469,8 @@ pub const SrcLoc = struct {
24692469 const case_nodes = tree.extra_data[extra.start..extra.end];
24702470 for (case_nodes) |case_node| {
24712471 const case = switch (node_tags[case_node]) {
2472 .switch_case_one => tree.switchCaseOne(case_node),
2473 .switch_case => tree.switchCase(case_node),
2472 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
2473 .switch_case, .switch_case_inline => tree.switchCase(case_node),
24742474 else => unreachable,
24752475 };
24762476 const is_special = (case.ast.values.len == 0) or
......@@ -2491,8 +2491,8 @@ pub const SrcLoc = struct {
24912491 const case_node = src_loc.declRelativeToNodeIndex(node_off);
24922492 const node_tags = tree.nodes.items(.tag);
24932493 const case = switch (node_tags[case_node]) {
2494 .switch_case_one => tree.switchCaseOne(case_node),
2495 .switch_case => tree.switchCase(case_node),
2494 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
2495 .switch_case, .switch_case_inline => tree.switchCase(case_node),
24962496 else => unreachable,
24972497 };
24982498 const start_tok = case.payload_token.?;
......@@ -5937,8 +5937,8 @@ pub const SwitchProngSrc = union(enum) {
59375937 var scalar_i: u32 = 0;
59385938 for (case_nodes) |case_node| {
59395939 const case = switch (node_tags[case_node]) {
5940 .switch_case_one => tree.switchCaseOne(case_node),
5941 .switch_case => tree.switchCase(case_node),
5940 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
5941 .switch_case, .switch_case_inline => tree.switchCase(case_node),
59425942 else => unreachable,
59435943 };
59445944 if (case.ast.values.len == 0)
src/Sema.zig+146-2
......@@ -162,6 +162,9 @@ pub const Block = struct {
162162 /// type of `err` in `else => |err|`
163163 switch_else_err_ty: ?Type = null,
164164
165 /// Value for switch_capture in an inline case
166 inline_case_capture: Air.Inst.Ref = .none,
167
165168 const Param = struct {
166169 /// `noreturn` means `anytype`.
167170 ty: Type,
......@@ -9002,6 +9005,30 @@ fn zirSwitchCapture(
90029005 const operand_ptr_ty = sema.typeOf(operand_ptr);
90039006 const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
90049007
9008 if (block.inline_case_capture != .none) {
9009 const item_val = sema.resolveConstValue(block, .unneeded, block.inline_case_capture, undefined) catch unreachable;
9010 if (operand_ty.zigTypeTag() == .Union) {
9011 const field_index = @intCast(u32, operand_ty.unionTagFieldIndex(item_val, sema.mod).?);
9012 const union_obj = operand_ty.cast(Type.Payload.Union).?.data;
9013 const field_ty = union_obj.fields.values()[field_index].ty;
9014 if (is_ref) {
9015 const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, .{
9016 .pointee_type = field_ty,
9017 .mutable = operand_ptr_ty.ptrIsMutable(),
9018 .@"volatile" = operand_ptr_ty.isVolatilePtr(),
9019 .@"addrspace" = operand_ptr_ty.ptrAddressSpace(),
9020 });
9021 return block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty);
9022 } else {
9023 return block.addStructFieldVal(operand_ptr, field_index, field_ty);
9024 }
9025 } else if (is_ref) {
9026 return sema.addConstantMaybeRef(block, operand_src, operand_ty, item_val, true);
9027 } else {
9028 return block.inline_case_capture;
9029 }
9030 }
9031
90059032 const operand = if (operand_is_ref)
90069033 try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
90079034 else
......@@ -9234,14 +9261,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
92349261 } else 0;
92359262
92369263 const special_prong = extra.data.bits.specialProng();
9237 const special: struct { body: []const Zir.Inst.Index, end: usize } = switch (special_prong) {
9238 .none => .{ .body = &.{}, .end = header_extra_index },
9264 const special: struct { body: []const Zir.Inst.Index, end: usize, is_inline: bool } = switch (special_prong) {
9265 .none => .{ .body = &.{}, .end = header_extra_index, .is_inline = false },
92399266 .under, .@"else" => blk: {
92409267 const body_len = @truncate(u31, sema.code.extra[header_extra_index]);
92419268 const extra_body_start = header_extra_index + 1;
92429269 break :blk .{
92439270 .body = sema.code.extra[extra_body_start..][0..body_len],
92449271 .end = extra_body_start + body_len,
9272 .is_inline = sema.code.extra[header_extra_index] >> 31 != 0,
92459273 };
92469274 },
92479275 };
......@@ -9901,6 +9929,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
99019929 if (special_prong == .none) {
99029930 return sema.fail(block, src, "switch must handle all possibilities", .{});
99039931 }
9932 if (special.is_inline) {
9933 return sema.fail(block, src, "TODO special.is_inline", .{});
9934 }
99049935 if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand)) {
99059936 return Air.Inst.Ref.unreachable_value;
99069937 }
......@@ -9927,6 +9958,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
99279958 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
99289959 extra_index += 1;
99299960 const body_len = @truncate(u31, sema.code.extra[extra_index]);
9961 const is_inline = sema.code.extra[extra_index] >> 31 != 0;
99309962 extra_index += 1;
99319963 const body = sema.code.extra[extra_index..][0..body_len];
99329964 extra_index += body_len;
......@@ -9936,8 +9968,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
99369968
99379969 case_block.instructions.shrinkRetainingCapacity(0);
99389970 case_block.wip_capture_scope = wip_captures.scope;
9971 case_block.inline_case_capture = .none;
99399972
99409973 const item = try sema.resolveInst(item_ref);
9974 if (is_inline) case_block.inline_case_capture = item;
99419975 // `item` is already guaranteed to be constant known.
99429976
99439977 const analyze_body = if (union_originally) blk: {
......@@ -9989,12 +10023,118 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
998910023 const ranges_len = sema.code.extra[extra_index];
999010024 extra_index += 1;
999110025 const body_len = @truncate(u31, sema.code.extra[extra_index]);
10026 const is_inline = sema.code.extra[extra_index] >> 31 != 0;
999210027 extra_index += 1;
999310028 const items = sema.code.refSlice(extra_index, items_len);
999410029 extra_index += items_len;
999510030
999610031 case_block.instructions.shrinkRetainingCapacity(0);
999710032 case_block.wip_capture_scope = child_block.wip_capture_scope;
10033 case_block.inline_case_capture = .none;
10034
10035 // Generate all possible cases as scalar prongs.
10036 if (is_inline) {
10037 const body_start = extra_index + 2 * ranges_len;
10038 const body = sema.code.extra[body_start..][0..body_len];
10039 const case_src = src; // TODO better source location
10040 var emit_bb = false;
10041
10042 var range_i: usize = 0;
10043 while (range_i < ranges_len) : (range_i += 1) {
10044 const first_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
10045 extra_index += 1;
10046 const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
10047 extra_index += 1;
10048
10049 const item_first_ref = try sema.resolveInst(first_ref);
10050 var item_first = sema.resolveConstValue(block, .unneeded, item_first_ref, undefined) catch unreachable;
10051 const item_last_ref = try sema.resolveInst(last_ref);
10052 const item_last = sema.resolveConstValue(block, .unneeded, item_last_ref, undefined) catch unreachable;
10053
10054 while (item_first.compare(.lte, item_last, operand_ty, sema.mod)) : ({
10055 item_first = try sema.intAddScalar(block, case_src, item_first, Value.one);
10056 }) {
10057 cases_len += 1;
10058
10059 const item_ref = try sema.addConstant(operand_ty, item_first);
10060 case_block.inline_case_capture = item_ref;
10061
10062 case_block.instructions.shrinkRetainingCapacity(0);
10063 case_block.wip_capture_scope = child_block.wip_capture_scope;
10064
10065 if (emit_bb) try sema.emitBackwardBranch(block, case_src);
10066 emit_bb = true;
10067
10068 _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {
10069 error.ComptimeBreak => {
10070 const zir_datas = sema.code.instructions.items(.data);
10071 const break_data = zir_datas[sema.comptime_break_inst].@"break";
10072 try sema.addRuntimeBreak(&case_block, .{
10073 .block_inst = break_data.block_inst,
10074 .operand = break_data.operand,
10075 .inst = sema.comptime_break_inst,
10076 });
10077 },
10078 else => |e| return e,
10079 };
10080
10081 // try wip_captures.finalize();
10082
10083 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
10084 cases_extra.appendAssumeCapacity(1); // items_len
10085 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
10086 cases_extra.appendAssumeCapacity(@enumToInt(item_ref));
10087 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
10088 }
10089 }
10090
10091 for (items) |item_ref| {
10092 cases_len += 1;
10093
10094 const item = try sema.resolveInst(item_ref);
10095 case_block.inline_case_capture = item;
10096
10097 case_block.instructions.shrinkRetainingCapacity(0);
10098 case_block.wip_capture_scope = child_block.wip_capture_scope;
10099
10100 const analyze_body = if (union_originally) blk: {
10101 const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;
10102 const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
10103 break :blk field_ty.zigTypeTag() != .NoReturn;
10104 } else true;
10105
10106 if (emit_bb) try sema.emitBackwardBranch(block, case_src);
10107 emit_bb = true;
10108
10109 if (analyze_body) {
10110 _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) {
10111 error.ComptimeBreak => {
10112 const zir_datas = sema.code.instructions.items(.data);
10113 const break_data = zir_datas[sema.comptime_break_inst].@"break";
10114 try sema.addRuntimeBreak(&case_block, .{
10115 .block_inst = break_data.block_inst,
10116 .operand = break_data.operand,
10117 .inst = sema.comptime_break_inst,
10118 });
10119 },
10120 else => |e| return e,
10121 };
10122 } else {
10123 _ = try case_block.addNoOp(.unreach);
10124 }
10125
10126 // try wip_captures.finalize();
10127
10128 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
10129 cases_extra.appendAssumeCapacity(1); // items_len
10130 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
10131 cases_extra.appendAssumeCapacity(@enumToInt(item));
10132 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
10133 }
10134
10135 extra_index += body_len;
10136 continue;
10137 }
999810138
999910139 var any_ok: Air.Inst.Ref = .none;
1000010140
......@@ -10158,6 +10298,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1015810298
1015910299 case_block.instructions.shrinkRetainingCapacity(0);
1016010300 case_block.wip_capture_scope = wip_captures.scope;
10301 case_block.inline_case_capture = .none;
10302 if (special.is_inline) {
10303 return sema.fail(block, src, "TODO special.is_inline", .{});
10304 }
1016110305
1016210306 const analyze_body = if (union_originally)
1016310307 for (seen_union_fields) |seen_field, index| {
test/behavior.zig+1
......@@ -180,6 +180,7 @@ test {
180180 _ = @import("behavior/decltest.zig");
181181 _ = @import("behavior/packed_struct_explicit_backing_int.zig");
182182 _ = @import("behavior/empty_union.zig");
183 _ = @import("behavior/inline_switch.zig");
183184 }
184185
185186 if (builtin.os.tag != .wasi) {
test/behavior/inline_switch.zig created+57
......@@ -0,0 +1,57 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const builtin = @import("builtin");
4
5test "inline scalar prongs" {
6 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7
8 var x: usize = 0;
9 switch (x) {
10 10 => |*item| try expect(@TypeOf(item) == *usize),
11 inline 11 => |*item| {
12 try expect(@TypeOf(item) == *const usize);
13 try expect(item.* == 11);
14 },
15 else => {},
16 }
17}
18
19test "inline prong ranges" {
20 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
21
22 var x: usize = 0;
23 switch (x) {
24 inline 0...20, 24 => |item| {
25 if (item > 25) @compileError("bad");
26 },
27 else => {},
28 }
29}
30
31const E = enum { a, b, c, d };
32test "inline switch enums" {
33 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
34
35 var x: E = .a;
36 switch (x) {
37 inline .a, .b => |aorb| if (aorb != .a and aorb != .b) @compileError("bad"),
38 inline .c, .d => |cord| if (cord != .c and cord != .d) @compileError("bad"),
39 }
40}
41
42const U = union(E) { a: void, b: u2, c: u3, d: u4 };
43test "inline switch unions" {
44 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
45 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
46 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
47
48 var x: U = .a;
49 switch (x) {
50 inline .a, .b => |aorb| {
51 try expect(@TypeOf(aorb) == void or @TypeOf(aorb) == u2);
52 },
53 inline .c, .d => |cord| {
54 try expect(@TypeOf(cord) == u3 or @TypeOf(cord) == u4);
55 },
56 }
57}