authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-27 15:32:34+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-27 18:33:23+03:00
log83fa216c8d2375476ee02ccf53bf6b5a9ed7480e
tree004dfd214b6d235219a59492f1424f83a610ad77
parent950a0e2405fb3de63c860c47d73af80f7f1fda2c

Sema: implement `inline else` for ints


2 files changed, 115 insertions(+), 1 deletions(-)

src/Sema.zig+84-1
...@@ -10414,7 +10414,41 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -10414,7 +10414,41 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
10414 }10414 }
10415 },10415 },
10416 .Int => {10416 .Int => {
10417 return sema.fail(block, special_prong_src, "TODO 'inline else' Int", .{});10417 var it = try RangeSetUnhandledIterator.init(sema, block, special_prong_src, operand_ty, range_set);
10418 var emit_bb = false;
10419 while (try it.next()) |cur| {
10420 cases_len += 1;
10421
10422 const item_ref = try sema.addConstant(operand_ty, cur);
10423 case_block.inline_case_capture = item_ref;
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(item_ref));
10450 cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
10451 }
10418 },10452 },
10419 .Bool => {10453 .Bool => {
10420 var emit_bb = false;10454 var emit_bb = false;
...@@ -10561,6 +10595,55 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -10561,6 +10595,55 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
10561 return sema.analyzeBlockBody(block, src, &child_block, merges);10595 return sema.analyzeBlockBody(block, src, &child_block, merges);
10562}10596}
1056310597
10598const RangeSetUnhandledIterator = struct {
10599 sema: *Sema,
10600 block: *Block,
10601 src: LazySrcLoc,
10602 ty: Type,
10603 cur: Value,
10604 max: Value,
10605 ranges: []const RangeSet.Range,
10606 range_i: usize = 0,
10607 first: bool = true,
10608
10609 fn init(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator {
10610 const target = sema.mod.getTarget();
10611 const min = try ty.minInt(sema.arena, target);
10612 const max = try ty.maxInt(sema.arena, target);
10613
10614 return RangeSetUnhandledIterator{
10615 .sema = sema,
10616 .block = block,
10617 .src = src,
10618 .ty = ty,
10619 .cur = min,
10620 .max = max,
10621 .ranges = range_set.ranges.items,
10622 };
10623 }
10624
10625 fn next(it: *RangeSetUnhandledIterator) !?Value {
10626 while (it.range_i < it.ranges.len) : (it.range_i += 1) {
10627 if (!it.first) {
10628 it.cur = try it.sema.intAdd(it.block, it.src, it.cur, Value.one, it.ty);
10629 }
10630 it.first = false;
10631 if (it.cur.compare(.lt, it.ranges[it.range_i].first, it.ty, it.sema.mod)) {
10632 return it.cur;
10633 }
10634 it.cur = it.ranges[it.range_i].last;
10635 }
10636 if (!it.first) {
10637 it.cur = try it.sema.intAdd(it.block, it.src, it.cur, Value.one, it.ty);
10638 }
10639 it.first = false;
10640 if (it.cur.compare(.lte, it.max, it.ty, it.sema.mod)) {
10641 return it.cur;
10642 }
10643 return null;
10644 }
10645};
10646
10564fn resolveSwitchItemVal(10647fn resolveSwitchItemVal(
10565 sema: *Sema,10648 sema: *Sema,
10566 block: *Block,10649 block: *Block,
test/behavior/inline_switch.zig+31
...@@ -98,3 +98,34 @@ test "inline else enum" {...@@ -98,3 +98,34 @@ test "inline else enum" {
98 inline else => |val| comptime if (@enumToInt(val) < 4) @compileError("bad"),98 inline else => |val| comptime if (@enumToInt(val) < 4) @compileError("bad"),
99 }99 }
100}100}
101
102test "inline else int with gaps" {
103 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
104
105 var a: u8 = 0;
106 switch (a) {
107 1...125, 128...254 => {},
108 inline else => |val| {
109 if (val != 0 and
110 val != 126 and
111 val != 127 and
112 val != 255)
113 @compileError("bad");
114 },
115 }
116}
117
118test "inline else int all values" {
119 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
120
121 var a: u2 = 0;
122 switch (a) {
123 inline else => |val| {
124 if (val != 0 and
125 val != 1 and
126 val != 2 and
127 val != 3)
128 @compileError("bad");
129 },
130 }
131}