authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-26 17:30:24+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-27 18:33:23+03:00
log0e77259f44307a5d9b1e91723a226f8da6fe97d5
treed1dcc77e23fbb38985431b871ecf67ccb2da256c
parent5baaf90e3c10d197131eaf5908da4401b9a07e7b

add inline switch union tag captures


12 files changed, 186 insertions(+), 59 deletions(-)

lib/std/zig/parse.zig+2-2
......@@ -3100,7 +3100,7 @@ const Parser = struct {
31003100 return identifier;
31013101 }
31023102
3103 /// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrPayload? AssignExpr
3103 /// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr
31043104 /// SwitchCase
31053105 /// <- SwitchItem (COMMA SwitchItem)* COMMA?
31063106 /// / KEYWORD_else
......@@ -3123,7 +3123,7 @@ const Parser = struct {
31233123 }
31243124 }
31253125 const arrow_token = try p.expectToken(.equal_angle_bracket_right);
3126 _ = try p.parsePtrPayload();
3126 _ = try p.parsePtrIndexPayload();
31273127
31283128 const items = p.scratch.items[scratch_top..];
31293129 switch (items.len) {
lib/std/zig/parser_test.zig+2
......@@ -3276,6 +3276,8 @@ test "zig fmt: switch" {
32763276 \\ switch (u) {
32773277 \\ Union.Int => |int| {},
32783278 \\ Union.Float => |*float| unreachable,
3279 \\ 1 => |a, b| unreachable,
3280 \\ 2 => |*a, b| unreachable,
32793281 \\ }
32803282 \\}
32813283 \\
lib/std/zig/render.zig+8-4
......@@ -1541,13 +1541,17 @@ fn renderSwitchCase(
15411541
15421542 if (switch_case.payload_token) |payload_token| {
15431543 try renderToken(ais, tree, payload_token - 1, .none); // pipe
1544 const ident = payload_token + @boolToInt(token_tags[payload_token] == .asterisk);
15441545 if (token_tags[payload_token] == .asterisk) {
15451546 try renderToken(ais, tree, payload_token, .none); // asterisk
1546 try renderToken(ais, tree, payload_token + 1, .none); // identifier
1547 try renderToken(ais, tree, payload_token + 2, pre_target_space); // pipe
1547 }
1548 try renderToken(ais, tree, ident, .none); // identifier
1549 if (token_tags[ident + 1] == .comma) {
1550 try renderToken(ais, tree, ident + 1, .space); // ,
1551 try renderToken(ais, tree, ident + 2, .none); // identifier
1552 try renderToken(ais, tree, ident + 3, pre_target_space); // pipe
15481553 } else {
1549 try renderToken(ais, tree, payload_token, .none); // identifier
1550 try renderToken(ais, tree, payload_token + 1, pre_target_space); // pipe
1554 try renderToken(ais, tree, ident + 1, pre_target_space); // pipe
15511555 }
15521556 }
15531557
src/AstGen.zig+92-45
......@@ -2373,6 +2373,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
23732373 .switch_capture_ref,
23742374 .switch_capture_multi,
23752375 .switch_capture_multi_ref,
2376 .switch_capture_tag,
23762377 .struct_init_empty,
23772378 .struct_init,
23782379 .struct_init_ref,
......@@ -6378,8 +6379,12 @@ fn switchExpr(
63786379
63796380 var dbg_var_name: ?u32 = null;
63806381 var dbg_var_inst: Zir.Inst.Ref = undefined;
6382 var dbg_var_tag_name: ?u32 = null;
6383 var dbg_var_tag_inst: Zir.Inst.Ref = undefined;
63816384 var capture_inst: Zir.Inst.Index = 0;
6385 var tag_inst: Zir.Inst.Index = 0;
63826386 var capture_val_scope: Scope.LocalVal = undefined;
6387 var tag_scope: Scope.LocalVal = undefined;
63836388 const sub_scope = blk: {
63846389 const payload_token = case.payload_token orelse break :blk &case_scope.base;
63856390 const ident = if (token_tags[payload_token] == .asterisk)
......@@ -6387,59 +6392,96 @@ fn switchExpr(
63876392 else
63886393 payload_token;
63896394 const is_ptr = ident != payload_token;
6390 if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
6395 const ident_slice = tree.tokenSlice(ident);
6396 var payload_sub_scope: *Scope = undefined;
6397 if (mem.eql(u8, ident_slice, "_")) {
63916398 if (is_ptr) {
63926399 return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
63936400 }
6394 break :blk &case_scope.base;
6395 }
6396 if (case_node == special_node) {
6397 const capture_tag: Zir.Inst.Tag = if (is_ptr)
6398 .switch_capture_ref
6399 else
6400 .switch_capture;
6401 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
6402 try astgen.instructions.append(gpa, .{
6403 .tag = capture_tag,
6404 .data = .{
6405 .switch_capture = .{
6406 .switch_inst = switch_block,
6407 // Max int communicates that this is the else/underscore prong.
6408 .prong_index = std.math.maxInt(u32),
6409 },
6410 },
6411 });
6401 payload_sub_scope = &case_scope.base;
64126402 } else {
6413 const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
6414 const is_ptr_bits: u2 = @boolToInt(is_ptr);
6415 const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
6416 0b00 => .switch_capture,
6417 0b01 => .switch_capture_ref,
6418 0b10 => .switch_capture_multi,
6419 0b11 => .switch_capture_multi_ref,
6403 if (case_node == special_node) {
6404 const capture_tag: Zir.Inst.Tag = if (is_ptr)
6405 .switch_capture_ref
6406 else
6407 .switch_capture;
6408 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
6409 try astgen.instructions.append(gpa, .{
6410 .tag = capture_tag,
6411 .data = .{
6412 .switch_capture = .{
6413 .switch_inst = switch_block,
6414 // Max int communicates that this is the else/underscore prong.
6415 .prong_index = std.math.maxInt(u32),
6416 },
6417 },
6418 });
6419 } else {
6420 const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
6421 const is_ptr_bits: u2 = @boolToInt(is_ptr);
6422 const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
6423 0b00 => .switch_capture,
6424 0b01 => .switch_capture_ref,
6425 0b10 => .switch_capture_multi,
6426 0b11 => .switch_capture_multi_ref,
6427 };
6428 const capture_index = if (is_multi_case) multi_case_index else scalar_case_index;
6429 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
6430 try astgen.instructions.append(gpa, .{
6431 .tag = capture_tag,
6432 .data = .{ .switch_capture = .{
6433 .switch_inst = switch_block,
6434 .prong_index = capture_index,
6435 } },
6436 });
6437 }
6438 const capture_name = try astgen.identAsString(ident);
6439 try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice);
6440 capture_val_scope = .{
6441 .parent = &case_scope.base,
6442 .gen_zir = &case_scope,
6443 .name = capture_name,
6444 .inst = indexToRef(capture_inst),
6445 .token_src = payload_token,
6446 .id_cat = .@"capture",
64206447 };
6421 const capture_index = if (is_multi_case) multi_case_index else scalar_case_index;
6422 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
6423 try astgen.instructions.append(gpa, .{
6424 .tag = capture_tag,
6425 .data = .{ .switch_capture = .{
6426 .switch_inst = switch_block,
6427 .prong_index = capture_index,
6428 } },
6429 });
6448 dbg_var_name = capture_name;
6449 dbg_var_inst = indexToRef(capture_inst);
6450 payload_sub_scope = &capture_val_scope.base;
64306451 }
6431 const capture_name = try astgen.identAsString(ident);
6432 capture_val_scope = .{
6433 .parent = &case_scope.base,
6452
6453 const tag_token = if (token_tags[ident + 1] == .comma)
6454 ident + 2
6455 else
6456 break :blk payload_sub_scope;
6457 const tag_slice = tree.tokenSlice(tag_token);
6458 if (mem.eql(u8, tag_slice, "_")) {
6459 return astgen.failTok(tag_token, "discard of tag capture; omit it instead", .{});
6460 } else if (case.inline_token == null) {
6461 return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});
6462 }
6463 const tag_name = try astgen.identAsString(tag_token);
6464 try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice);
6465 tag_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
6466 try astgen.instructions.append(gpa, .{
6467 .tag = .switch_capture_tag,
6468 .data = .{ .un_tok = .{
6469 .operand = cond,
6470 .src_tok = case_scope.tokenIndexToRelative(tag_token),
6471 } },
6472 });
6473
6474 tag_scope = .{
6475 .parent = payload_sub_scope,
64346476 .gen_zir = &case_scope,
6435 .name = capture_name,
6436 .inst = indexToRef(capture_inst),
6437 .token_src = payload_token,
6438 .id_cat = .@"capture",
6477 .name = tag_name,
6478 .inst = indexToRef(tag_inst),
6479 .token_src = tag_token,
6480 .id_cat = .@"switch tag capture",
64396481 };
6440 dbg_var_name = capture_name;
6441 dbg_var_inst = indexToRef(capture_inst);
6442 break :blk &capture_val_scope.base;
6482 dbg_var_tag_name = tag_name;
6483 dbg_var_tag_inst = indexToRef(tag_inst);
6484 break :blk &tag_scope.base;
64436485 };
64446486
64456487 const header_index = @intCast(u32, payloads.items.len);
......@@ -6494,10 +6536,14 @@ fn switchExpr(
64946536 defer case_scope.unstack();
64956537
64966538 if (capture_inst != 0) try case_scope.instructions.append(gpa, capture_inst);
6539 if (tag_inst != 0) try case_scope.instructions.append(gpa, tag_inst);
64976540 try case_scope.addDbgBlockBegin();
64986541 if (dbg_var_name) |some| {
64996542 try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
65006543 }
6544 if (dbg_var_tag_name) |some| {
6545 try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_tag_inst);
6546 }
65016547 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
65026548 try checkUsed(parent_gz, &case_scope.base, sub_scope);
65036549 try case_scope.addDbgBlockEnd();
......@@ -10073,6 +10119,7 @@ const Scope = struct {
1007310119 @"local constant",
1007410120 @"local variable",
1007510121 @"loop index capture",
10122 @"switch tag capture",
1007610123 @"capture",
1007710124 };
1007810125
src/Sema.zig+28
......@@ -799,6 +799,7 @@ fn analyzeBodyInner(
799799 .switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
800800 .switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
801801 .switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true),
802 .switch_capture_tag => try sema.zirSwitchCaptureTag(block, inst),
802803 .type_info => try sema.zirTypeInfo(block, inst),
803804 .size_of => try sema.zirSizeOf(block, inst),
804805 .bit_size_of => try sema.zirBitSizeOf(block, inst),
......@@ -9164,6 +9165,33 @@ fn zirSwitchCapture(
91649165 }
91659166}
91669167
9168fn zirSwitchCaptureTag(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9169 const zir_datas = sema.code.instructions.items(.data);
9170 const inst_data = zir_datas[inst].un_tok;
9171 const src = inst_data.src();
9172
9173 const switch_tag = sema.code.instructions.items(.tag)[Zir.refToIndex(inst_data.operand).?];
9174 const is_ref = switch_tag == .switch_cond_ref;
9175 const cond_data = zir_datas[Zir.refToIndex(inst_data.operand).?].un_node;
9176 const operand_ptr = try sema.resolveInst(cond_data.operand);
9177 const operand_ptr_ty = sema.typeOf(operand_ptr);
9178 const operand_ty = if (is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
9179
9180 if (operand_ty.zigTypeTag() != .Union) {
9181 const msg = msg: {
9182 const msg = try sema.errMsg(block, src, "cannot capture tag of non-union type '{}'", .{
9183 operand_ty.fmt(sema.mod),
9184 });
9185 errdefer msg.destroy(sema.gpa);
9186 try sema.addDeclaredHereNote(msg, operand_ty);
9187 break :msg msg;
9188 };
9189 return sema.failWithOwnedErrorMsg(msg);
9190 }
9191
9192 return block.inline_case_capture;
9193}
9194
91679195fn zirSwitchCond(
91689196 sema: *Sema,
91699197 block: *Block,
src/Zir.zig+6
......@@ -683,6 +683,9 @@ pub const Inst = struct {
683683 /// Result is a pointer to the value.
684684 /// Uses the `switch_capture` field.
685685 switch_capture_multi_ref,
686 /// Produces the capture value for an inline switch prong tag capture.
687 /// Uses the `un_tok` field.
688 switch_capture_tag,
686689 /// Given a
687690 /// *A returns *A
688691 /// *E!A returns *A
......@@ -1128,6 +1131,7 @@ pub const Inst = struct {
11281131 .switch_capture_ref,
11291132 .switch_capture_multi,
11301133 .switch_capture_multi_ref,
1134 .switch_capture_tag,
11311135 .switch_block,
11321136 .switch_cond,
11331137 .switch_cond_ref,
......@@ -1422,6 +1426,7 @@ pub const Inst = struct {
14221426 .switch_capture_ref,
14231427 .switch_capture_multi,
14241428 .switch_capture_multi_ref,
1429 .switch_capture_tag,
14251430 .switch_block,
14261431 .switch_cond,
14271432 .switch_cond_ref,
......@@ -1681,6 +1686,7 @@ pub const Inst = struct {
16811686 .switch_capture_ref = .switch_capture,
16821687 .switch_capture_multi = .switch_capture,
16831688 .switch_capture_multi_ref = .switch_capture,
1689 .switch_capture_tag = .un_tok,
16841690 .array_base_ptr = .un_node,
16851691 .field_base_ptr = .un_node,
16861692 .validate_array_init_ty = .pl_node,
src/arch/x86_64/Emit.zig+1-1
......@@ -2159,7 +2159,7 @@ const RegisterOrMemory = union(enum) {
21592159 /// Returns size in bits.
21602160 fn size(reg_or_mem: RegisterOrMemory) u64 {
21612161 return switch (reg_or_mem) {
2162 .register => |reg| reg.size(),
2162 .register => |register| register.size(),
21632163 .memory => |memory| memory.size(),
21642164 };
21652165 }
src/print_zir.zig+1
......@@ -237,6 +237,7 @@ const Writer = struct {
237237 .ret_tok,
238238 .ensure_err_payload_void,
239239 .closure_capture,
240 .switch_capture_tag,
240241 => try self.writeUnTok(stream, inst),
241242
242243 .bool_br_and,
src/stage1/parser.cpp+3-3
......@@ -2306,17 +2306,17 @@ static Optional<PtrIndexPayload> ast_parse_ptr_index_payload(ParseContext *pc) {
23062306 return Optional<PtrIndexPayload>::some(res);
23072307}
23082308
2309// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrPayload? AssignExpr
2309// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr
23102310static AstNode *ast_parse_switch_prong(ParseContext *pc) {
23112311 AstNode *res = ast_parse_switch_case(pc);
23122312 if (res == nullptr)
23132313 return nullptr;
23142314
23152315 expect_token(pc, TokenIdFatArrow);
2316 Optional<PtrPayload> opt_payload = ast_parse_ptr_payload(pc);
2316 Optional<PtrIndexPayload> opt_payload = ast_parse_ptr_index_payload(pc);
23172317 AstNode *expr = ast_expect(pc, ast_parse_assign_expr);
23182318
2319 PtrPayload payload;
2319 PtrIndexPayload payload;
23202320 assert(res->type == NodeTypeSwitchProng);
23212321 res->data.switch_prong.expr = expr;
23222322 if (opt_payload.unwrap(&payload)) {
test/behavior/inline_switch.zig+14-4
......@@ -47,11 +47,21 @@ test "inline switch unions" {
4747
4848 var x: U = .a;
4949 switch (x) {
50 inline .a, .b => |aorb| {
51 try expect(@TypeOf(aorb) == void or @TypeOf(aorb) == u2);
50 inline .a, .b => |aorb, tag| {
51 if (tag == .a) {
52 try expect(@TypeOf(aorb) == void);
53 } else {
54 try expect(tag == .b);
55 try expect(@TypeOf(aorb) == u2);
56 }
5257 },
53 inline .c, .d => |cord| {
54 try expect(@TypeOf(cord) == u3 or @TypeOf(cord) == u4);
58 inline .c, .d => |cord, tag| {
59 if (tag == .c) {
60 try expect(@TypeOf(cord) == u3);
61 } else {
62 try expect(tag == .d);
63 try expect(@TypeOf(cord) == u4);
64 }
5565 },
5666 }
5767}
test/cases/compile_errors/invalid_tag_capture.zig created+15
......@@ -0,0 +1,15 @@
1const E = enum { a, b, c, d };
2pub export fn entry() void {
3 var x: E = .a;
4 switch (x) {
5 inline .a, .b => |aorb, d| @compileLog(aorb, d),
6 inline .c, .d => |*cord| @compileLog(cord),
7 }
8}
9
10// error
11// backend=stage2
12// target=native
13//
14// :5:33: error: cannot capture tag of non-union type 'tmp.E'
15// :1:11: note: enum declared here
test/cases/compile_errors/tag_capture_on_non_inline_prong.zig created+14
......@@ -0,0 +1,14 @@
1const E = enum { a, b, c, d };
2pub export fn entry() void {
3 var x: E = .a;
4 switch (x) {
5 .a, .b => |aorb, d| @compileLog(aorb, d),
6 inline .c, .d => |*cord| @compileLog(cord),
7 }
8}
9
10// error
11// backend=stage2
12// target=native
13//
14// :5:26: error: tag capture on non-inline prong