authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-27 14:56:56+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-27 18:33:23+03:00
log950a0e2405fb3de63c860c47d73af80f7f1fda2c
tree0490e512caad16f3d30aab7622f5e202a00e4a13
parent0e77259f44307a5d9b1e91723a226f8da6fe97d5

Sema: implement `inline else` for errors enums and bools


3 files changed, 253 insertions(+), 34 deletions(-)

src/Sema.zig+193-34
......@@ -9309,8 +9309,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
93099309 break :blk sema.typeOf(raw_operand);
93109310 };
93119311 const union_originally = maybe_union_ty.zigTypeTag() == .Union;
9312 var seen_union_fields: []?Module.SwitchProngSrc = &.{};
9313 defer gpa.free(seen_union_fields);
9312
9313 // Duplicate checking variables later also used for `inline else`.
9314 var seen_enum_fields: []?Module.SwitchProngSrc = &.{};
9315 var seen_errors = SwitchErrorSet.init(gpa);
9316 var range_set = RangeSet.init(gpa, sema.mod);
9317 var true_count: u8 = 0;
9318 var false_count: u8 = 0;
9319
9320 defer {
9321 range_set.deinit();
9322 gpa.free(seen_enum_fields);
9323 seen_errors.deinit();
9324 }
93149325
93159326 var empty_enum = false;
93169327
......@@ -9347,15 +9358,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
93479358 switch (operand_ty.zigTypeTag()) {
93489359 .Union => unreachable, // handled in zirSwitchCond
93499360 .Enum => {
9350 var seen_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount());
9351 empty_enum = seen_fields.len == 0 and !operand_ty.isNonexhaustiveEnum();
9352 defer if (!union_originally) gpa.free(seen_fields);
9353 if (union_originally) seen_union_fields = seen_fields;
9354 mem.set(?Module.SwitchProngSrc, seen_fields, null);
9355
9356 // This is used for non-exhaustive enum values that do not correspond to any tags.
9357 var range_set = RangeSet.init(gpa, sema.mod);
9358 defer range_set.deinit();
9361 seen_enum_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount());
9362 empty_enum = seen_enum_fields.len == 0 and !operand_ty.isNonexhaustiveEnum();
9363 mem.set(?Module.SwitchProngSrc, seen_enum_fields, null);
9364 // `range_set` is used for non-exhaustive enum values that do not correspond to any tags.
93599365
93609366 var extra_index: usize = special.end;
93619367 {
......@@ -9369,7 +9375,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
93699375
93709376 try sema.validateSwitchItemEnum(
93719377 block,
9372 seen_fields,
9378 seen_enum_fields,
93739379 &range_set,
93749380 item_ref,
93759381 src_node_offset,
......@@ -9392,7 +9398,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
93929398 for (items) |item_ref, item_i| {
93939399 try sema.validateSwitchItemEnum(
93949400 block,
9395 seen_fields,
9401 seen_enum_fields,
93969402 &range_set,
93979403 item_ref,
93989404 src_node_offset,
......@@ -9403,7 +9409,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
94039409 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
94049410 }
94059411 }
9406 const all_tags_handled = for (seen_fields) |seen_src| {
9412 const all_tags_handled = for (seen_enum_fields) |seen_src| {
94079413 if (seen_src == null) break false;
94089414 } else true;
94099415
......@@ -9423,7 +9429,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
94239429 .{},
94249430 );
94259431 errdefer msg.destroy(sema.gpa);
9426 for (seen_fields) |seen_src, i| {
9432 for (seen_enum_fields) |seen_src, i| {
94279433 if (seen_src != null) continue;
94289434
94299435 const field_name = operand_ty.enumFieldName(i);
......@@ -9454,9 +9460,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
94549460 }
94559461 },
94569462 .ErrorSet => {
9457 var seen_errors = SwitchErrorSet.init(gpa);
9458 defer seen_errors.deinit();
9459
94609463 var extra_index: usize = special.end;
94619464 {
94629465 var scalar_i: u32 = 0;
......@@ -9596,9 +9599,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
95969599 }
95979600 },
95989601 .Int, .ComptimeInt => {
9599 var range_set = RangeSet.init(gpa, sema.mod);
9600 defer range_set.deinit();
9601
96029602 var extra_index: usize = special.end;
96039603 {
96049604 var scalar_i: u32 = 0;
......@@ -9694,9 +9694,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
96949694 }
96959695 },
96969696 .Bool => {
9697 var true_count: u8 = 0;
9698 var false_count: u8 = 0;
9699
97009697 var extra_index: usize = special.end;
97019698 {
97029699 var scalar_i: u32 = 0;
......@@ -9950,16 +9947,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
99509947 return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
99519948 }
99529949
9953 if (scalar_cases_len + multi_cases_len == 0) {
9950 if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) {
99549951 if (empty_enum) {
99559952 return Air.Inst.Ref.void_value;
99569953 }
99579954 if (special_prong == .none) {
99589955 return sema.fail(block, src, "switch must handle all possibilities", .{});
99599956 }
9960 if (special.is_inline) {
9961 return sema.fail(block, src, "TODO special.is_inline", .{});
9962 }
99639957 if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand)) {
99649958 return Air.Inst.Ref.unreachable_value;
99659959 }
......@@ -10323,16 +10317,181 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1032310317 if (special.body.len != 0 or !is_first or case_block.wantSafety()) {
1032410318 var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);
1032510319 defer wip_captures.deinit();
10320 if (special.is_inline) switch (operand_ty.zigTypeTag()) {
10321 .Enum => {
10322 if (operand_ty.isNonexhaustiveEnum() and !union_originally) {
10323 return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
10324 operand_ty.fmt(sema.mod),
10325 });
10326 }
10327 var emit_bb = false;
10328 for (seen_enum_fields) |f, i| {
10329 if (f != null) continue;
10330 cases_len += 1;
10331
10332 const item_val = try Value.Tag.enum_field_index.create(sema.arena, @intCast(u32, i));
10333 const item_ref = try sema.addConstant(operand_ty, item_val);
10334 case_block.inline_case_capture = item_ref;
10335
10336 case_block.instructions.shrinkRetainingCapacity(0);
10337 case_block.wip_capture_scope = child_block.wip_capture_scope;
10338
10339 const analyze_body = if (union_originally) blk: {
10340 const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
10341 break :blk field_ty.zigTypeTag() != .NoReturn;
10342 } else true;
10343
10344 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
10345 emit_bb = true;
10346
10347 if (analyze_body) {
10348 _ = sema.analyzeBodyInner(&case_block, special.body) catch |err| switch (err) {
10349 error.ComptimeBreak => {
10350 const zir_datas = sema.code.instructions.items(.data);
10351 const break_data = zir_datas[sema.comptime_break_inst].@"break";
10352 try sema.addRuntimeBreak(&case_block, .{
10353 .block_inst = break_data.block_inst,
10354 .operand = break_data.operand,
10355 .inst = sema.comptime_break_inst,
10356 });
10357 },
10358 else => |e| return e,
10359 };
10360 } else {
10361 _ = try case_block.addNoOp(.unreach);
10362 }
10363
10364 // try wip_captures.finalize();
10365
10366 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
10367 cases_extra.appendAssumeCapacity(1); // items_len
10368 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
10369 cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
10370 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
10371 }
10372 },
10373 .ErrorSet => {
10374 if (operand_ty.isAnyError()) {
10375 return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
10376 operand_ty.fmt(sema.mod),
10377 });
10378 }
10379 var emit_bb = false;
10380 for (operand_ty.errorSetNames()) |error_name| {
10381 if (seen_errors.contains(error_name)) continue;
10382 cases_len += 1;
10383
10384 const item_val = try Value.Tag.@"error".create(sema.arena, .{ .name = error_name });
10385 const item_ref = try sema.addConstant(operand_ty, item_val);
10386 case_block.inline_case_capture = item_ref;
10387
10388 case_block.instructions.shrinkRetainingCapacity(0);
10389 case_block.wip_capture_scope = child_block.wip_capture_scope;
10390
10391 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
10392 emit_bb = true;
10393
10394 _ = sema.analyzeBodyInner(&case_block, special.body) catch |err| switch (err) {
10395 error.ComptimeBreak => {
10396 const zir_datas = sema.code.instructions.items(.data);
10397 const break_data = zir_datas[sema.comptime_break_inst].@"break";
10398 try sema.addRuntimeBreak(&case_block, .{
10399 .block_inst = break_data.block_inst,
10400 .operand = break_data.operand,
10401 .inst = sema.comptime_break_inst,
10402 });
10403 },
10404 else => |e| return e,
10405 };
10406
10407 // try wip_captures.finalize();
10408
10409 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
10410 cases_extra.appendAssumeCapacity(1); // items_len
10411 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
10412 cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
10413 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
10414 }
10415 },
10416 .Int => {
10417 return sema.fail(block, special_prong_src, "TODO 'inline else' Int", .{});
10418 },
10419 .Bool => {
10420 var emit_bb = false;
10421 if (true_count == 0) {
10422 cases_len += 1;
10423 case_block.inline_case_capture = Air.Inst.Ref.bool_true;
10424
10425 case_block.instructions.shrinkRetainingCapacity(0);
10426 case_block.wip_capture_scope = child_block.wip_capture_scope;
10427
10428 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
10429 emit_bb = true;
10430
10431 _ = sema.analyzeBodyInner(&case_block, special.body) catch |err| switch (err) {
10432 error.ComptimeBreak => {
10433 const zir_datas = sema.code.instructions.items(.data);
10434 const break_data = zir_datas[sema.comptime_break_inst].@"break";
10435 try sema.addRuntimeBreak(&case_block, .{
10436 .block_inst = break_data.block_inst,
10437 .operand = break_data.operand,
10438 .inst = sema.comptime_break_inst,
10439 });
10440 },
10441 else => |e| return e,
10442 };
10443
10444 // try wip_captures.finalize();
10445
10446 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
10447 cases_extra.appendAssumeCapacity(1); // items_len
10448 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
10449 cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
10450 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
10451 }
10452 if (false_count == 0) {
10453 cases_len += 1;
10454 case_block.inline_case_capture = Air.Inst.Ref.bool_false;
10455
10456 case_block.instructions.shrinkRetainingCapacity(0);
10457 case_block.wip_capture_scope = child_block.wip_capture_scope;
10458
10459 if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
10460 emit_bb = true;
10461
10462 _ = sema.analyzeBodyInner(&case_block, special.body) catch |err| switch (err) {
10463 error.ComptimeBreak => {
10464 const zir_datas = sema.code.instructions.items(.data);
10465 const break_data = zir_datas[sema.comptime_break_inst].@"break";
10466 try sema.addRuntimeBreak(&case_block, .{
10467 .block_inst = break_data.block_inst,
10468 .operand = break_data.operand,
10469 .inst = sema.comptime_break_inst,
10470 });
10471 },
10472 else => |e| return e,
10473 };
10474
10475 // try wip_captures.finalize();
10476
10477 try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
10478 cases_extra.appendAssumeCapacity(1); // items_len
10479 cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
10480 cases_extra.appendAssumeCapacity(@enumToInt(case_block.inline_case_capture));
10481 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
10482 }
10483 },
10484 else => return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
10485 operand_ty.fmt(sema.mod),
10486 }),
10487 };
1032610488
1032710489 case_block.instructions.shrinkRetainingCapacity(0);
1032810490 case_block.wip_capture_scope = wip_captures.scope;
1032910491 case_block.inline_case_capture = .none;
10330 if (special.is_inline) {
10331 return sema.fail(block, src, "TODO special.is_inline", .{});
10332 }
1033310492
10334 const analyze_body = if (union_originally)
10335 for (seen_union_fields) |seen_field, index| {
10493 const analyze_body = if (union_originally and !special.is_inline)
10494 for (seen_enum_fields) |seen_field, index| {
1033610495 if (seen_field != null) continue;
1033710496 const union_obj = maybe_union_ty.cast(Type.Payload.Union).?.data;
1033810497 const field_ty = union_obj.fields.values()[index].ty;
......@@ -10344,7 +10503,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1034410503 try sema.maybeErrorUnwrap(&case_block, special.body, operand))
1034510504 {
1034610505 // nothing to do here
10347 } else if (special.body.len != 0 and analyze_body) {
10506 } else if (special.body.len != 0 and analyze_body and !special.is_inline) {
1034810507 _ = sema.analyzeBodyInner(&case_block, special.body) catch |err| switch (err) {
1034910508 error.ComptimeBreak => {
1035010509 const zir_datas = sema.code.instructions.items(.data);
test/behavior/inline_switch.zig+33
......@@ -65,3 +65,36 @@ test "inline switch unions" {
6565 },
6666 }
6767}
68
69test "inline else bool" {
70 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
71
72 var a = true;
73 switch (a) {
74 true => {},
75 inline else => |val| if (val != false) @compileError("bad"),
76 }
77}
78
79test "inline else error" {
80 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
81
82 const Err = error{ a, b, c };
83 var a = Err.a;
84 switch (a) {
85 error.a => {},
86 inline else => |val| comptime if (val == error.a) @compileError("bad"),
87 }
88}
89
90test "inline else enum" {
91 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
92 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
93
94 const E2 = enum(u8) { a = 2, b = 3, c = 4, d = 5 };
95 var a: E2 = .a;
96 switch (a) {
97 .a, .b => {},
98 inline else => |val| comptime if (@enumToInt(val) < 4) @compileError("bad"),
99 }
100}
test/cases/compile_errors/invalid_inline_else_type.zig created+27
......@@ -0,0 +1,27 @@
1pub export fn entry1() void {
2 var a: anyerror = undefined;
3 switch (a) {
4 inline else => {},
5 }
6}
7const E = enum(u8) { a, _ };
8pub export fn entry2() void {
9 var a: E = undefined;
10 switch (a) {
11 inline else => {},
12 }
13}
14pub export fn entry3() void {
15 var a: *u32 = undefined;
16 switch (a) {
17 inline else => {},
18 }
19}
20
21// error
22// backend=stage2
23// target=native
24//
25// :4:21: error: cannot enumerate values of type 'anyerror' for 'inline else'
26// :11:21: error: cannot enumerate values of type 'tmp.E' for 'inline else'
27// :17:21: error: cannot enumerate values of type '*u32' for 'inline else'