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 {...@@ -2445,8 +2445,8 @@ pub const SrcLoc = struct {
2445 const case_nodes = tree.extra_data[extra.start..extra.end];2445 const case_nodes = tree.extra_data[extra.start..extra.end];
2446 for (case_nodes) |case_node| {2446 for (case_nodes) |case_node| {
2447 const case = switch (node_tags[case_node]) {2447 const case = switch (node_tags[case_node]) {
2448 .switch_case_one => tree.switchCaseOne(case_node),2448 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
2449 .switch_case => tree.switchCase(case_node),2449 .switch_case, .switch_case_inline => tree.switchCase(case_node),
2450 else => unreachable,2450 else => unreachable,
2451 };2451 };
2452 const is_special = (case.ast.values.len == 0) or2452 const is_special = (case.ast.values.len == 0) or
...@@ -2469,8 +2469,8 @@ pub const SrcLoc = struct {...@@ -2469,8 +2469,8 @@ pub const SrcLoc = struct {
2469 const case_nodes = tree.extra_data[extra.start..extra.end];2469 const case_nodes = tree.extra_data[extra.start..extra.end];
2470 for (case_nodes) |case_node| {2470 for (case_nodes) |case_node| {
2471 const case = switch (node_tags[case_node]) {2471 const case = switch (node_tags[case_node]) {
2472 .switch_case_one => tree.switchCaseOne(case_node),2472 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
2473 .switch_case => tree.switchCase(case_node),2473 .switch_case, .switch_case_inline => tree.switchCase(case_node),
2474 else => unreachable,2474 else => unreachable,
2475 };2475 };
2476 const is_special = (case.ast.values.len == 0) or2476 const is_special = (case.ast.values.len == 0) or
...@@ -2491,8 +2491,8 @@ pub const SrcLoc = struct {...@@ -2491,8 +2491,8 @@ pub const SrcLoc = struct {
2491 const case_node = src_loc.declRelativeToNodeIndex(node_off);2491 const case_node = src_loc.declRelativeToNodeIndex(node_off);
2492 const node_tags = tree.nodes.items(.tag);2492 const node_tags = tree.nodes.items(.tag);
2493 const case = switch (node_tags[case_node]) {2493 const case = switch (node_tags[case_node]) {
2494 .switch_case_one => tree.switchCaseOne(case_node),2494 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
2495 .switch_case => tree.switchCase(case_node),2495 .switch_case, .switch_case_inline => tree.switchCase(case_node),
2496 else => unreachable,2496 else => unreachable,
2497 };2497 };
2498 const start_tok = case.payload_token.?;2498 const start_tok = case.payload_token.?;
...@@ -5937,8 +5937,8 @@ pub const SwitchProngSrc = union(enum) {...@@ -5937,8 +5937,8 @@ pub const SwitchProngSrc = union(enum) {
5937 var scalar_i: u32 = 0;5937 var scalar_i: u32 = 0;
5938 for (case_nodes) |case_node| {5938 for (case_nodes) |case_node| {
5939 const case = switch (node_tags[case_node]) {5939 const case = switch (node_tags[case_node]) {
5940 .switch_case_one => tree.switchCaseOne(case_node),5940 .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
5941 .switch_case => tree.switchCase(case_node),5941 .switch_case, .switch_case_inline => tree.switchCase(case_node),
5942 else => unreachable,5942 else => unreachable,
5943 };5943 };
5944 if (case.ast.values.len == 0)5944 if (case.ast.values.len == 0)
src/Sema.zig+146-2
...@@ -162,6 +162,9 @@ pub const Block = struct {...@@ -162,6 +162,9 @@ pub const Block = struct {
162 /// type of `err` in `else => |err|`162 /// type of `err` in `else => |err|`
163 switch_else_err_ty: ?Type = null,163 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
165 const Param = struct {168 const Param = struct {
166 /// `noreturn` means `anytype`.169 /// `noreturn` means `anytype`.
167 ty: Type,170 ty: Type,
...@@ -9002,6 +9005,30 @@ fn zirSwitchCapture(...@@ -9002,6 +9005,30 @@ fn zirSwitchCapture(
9002 const operand_ptr_ty = sema.typeOf(operand_ptr);9005 const operand_ptr_ty = sema.typeOf(operand_ptr);
9003 const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;9006 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
9005 const operand = if (operand_is_ref)9032 const operand = if (operand_is_ref)
9006 try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)9033 try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
9007 else9034 else
...@@ -9234,14 +9261,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9234,14 +9261,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9234 } else 0;9261 } else 0;
92359262
9236 const special_prong = extra.data.bits.specialProng();9263 const special_prong = extra.data.bits.specialProng();
9237 const special: struct { body: []const Zir.Inst.Index, end: usize } = switch (special_prong) {9264 const special: struct { body: []const Zir.Inst.Index, end: usize, is_inline: bool } = switch (special_prong) {
9238 .none => .{ .body = &.{}, .end = header_extra_index },9265 .none => .{ .body = &.{}, .end = header_extra_index, .is_inline = false },
9239 .under, .@"else" => blk: {9266 .under, .@"else" => blk: {
9240 const body_len = @truncate(u31, sema.code.extra[header_extra_index]);9267 const body_len = @truncate(u31, sema.code.extra[header_extra_index]);
9241 const extra_body_start = header_extra_index + 1;9268 const extra_body_start = header_extra_index + 1;
9242 break :blk .{9269 break :blk .{
9243 .body = sema.code.extra[extra_body_start..][0..body_len],9270 .body = sema.code.extra[extra_body_start..][0..body_len],
9244 .end = extra_body_start + body_len,9271 .end = extra_body_start + body_len,
9272 .is_inline = sema.code.extra[header_extra_index] >> 31 != 0,
9245 };9273 };
9246 },9274 },
9247 };9275 };
...@@ -9901,6 +9929,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9901,6 +9929,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9901 if (special_prong == .none) {9929 if (special_prong == .none) {
9902 return sema.fail(block, src, "switch must handle all possibilities", .{});9930 return sema.fail(block, src, "switch must handle all possibilities", .{});
9903 }9931 }
9932 if (special.is_inline) {
9933 return sema.fail(block, src, "TODO special.is_inline", .{});
9934 }
9904 if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand)) {9935 if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand)) {
9905 return Air.Inst.Ref.unreachable_value;9936 return Air.Inst.Ref.unreachable_value;
9906 }9937 }
...@@ -9927,6 +9958,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9927,6 +9958,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9927 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);9958 const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
9928 extra_index += 1;9959 extra_index += 1;
9929 const body_len = @truncate(u31, sema.code.extra[extra_index]);9960 const body_len = @truncate(u31, sema.code.extra[extra_index]);
9961 const is_inline = sema.code.extra[extra_index] >> 31 != 0;
9930 extra_index += 1;9962 extra_index += 1;
9931 const body = sema.code.extra[extra_index..][0..body_len];9963 const body = sema.code.extra[extra_index..][0..body_len];
9932 extra_index += body_len;9964 extra_index += body_len;
...@@ -9936,8 +9968,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9936,8 +9968,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
99369968
9937 case_block.instructions.shrinkRetainingCapacity(0);9969 case_block.instructions.shrinkRetainingCapacity(0);
9938 case_block.wip_capture_scope = wip_captures.scope;9970 case_block.wip_capture_scope = wip_captures.scope;
9971 case_block.inline_case_capture = .none;
99399972
9940 const item = try sema.resolveInst(item_ref);9973 const item = try sema.resolveInst(item_ref);
9974 if (is_inline) case_block.inline_case_capture = item;
9941 // `item` is already guaranteed to be constant known.9975 // `item` is already guaranteed to be constant known.
99429976
9943 const analyze_body = if (union_originally) blk: {9977 const analyze_body = if (union_originally) blk: {
...@@ -9989,12 +10023,118 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9989,12 +10023,118 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9989 const ranges_len = sema.code.extra[extra_index];10023 const ranges_len = sema.code.extra[extra_index];
9990 extra_index += 1;10024 extra_index += 1;
9991 const body_len = @truncate(u31, sema.code.extra[extra_index]);10025 const body_len = @truncate(u31, sema.code.extra[extra_index]);
10026 const is_inline = sema.code.extra[extra_index] >> 31 != 0;
9992 extra_index += 1;10027 extra_index += 1;
9993 const items = sema.code.refSlice(extra_index, items_len);10028 const items = sema.code.refSlice(extra_index, items_len);
9994 extra_index += items_len;10029 extra_index += items_len;
999510030
9996 case_block.instructions.shrinkRetainingCapacity(0);10031 case_block.instructions.shrinkRetainingCapacity(0);
9997 case_block.wip_capture_scope = child_block.wip_capture_scope;10032 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
9999 var any_ok: Air.Inst.Ref = .none;10139 var any_ok: Air.Inst.Ref = .none;
1000010140
...@@ -10158,6 +10298,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -10158,6 +10298,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1015810298
10159 case_block.instructions.shrinkRetainingCapacity(0);10299 case_block.instructions.shrinkRetainingCapacity(0);
10160 case_block.wip_capture_scope = wip_captures.scope;10300 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
10162 const analyze_body = if (union_originally)10306 const analyze_body = if (union_originally)
10163 for (seen_union_fields) |seen_field, index| {10307 for (seen_union_fields) |seen_field, index| {
test/behavior.zig+1
...@@ -180,6 +180,7 @@ test {...@@ -180,6 +180,7 @@ test {
180 _ = @import("behavior/decltest.zig");180 _ = @import("behavior/decltest.zig");
181 _ = @import("behavior/packed_struct_explicit_backing_int.zig");181 _ = @import("behavior/packed_struct_explicit_backing_int.zig");
182 _ = @import("behavior/empty_union.zig");182 _ = @import("behavior/empty_union.zig");
183 _ = @import("behavior/inline_switch.zig");
183 }184 }
184185
185 if (builtin.os.tag != .wasi) {186 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}