Commit 83fa216c8d

Veikka Tuominen <git@vexu.eu>
2022-09-27 14:32:34
Sema: implement `inline else` for ints
1 parent 950a0e2
Changed files (2)
src
test
src/Sema.zig
@@ -10414,7 +10414,41 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                 }
             },
             .Int => {
-                return sema.fail(block, special_prong_src, "TODO 'inline else' Int", .{});
+                var it = try RangeSetUnhandledIterator.init(sema, block, special_prong_src, operand_ty, range_set);
+                var emit_bb = false;
+                while (try it.next()) |cur| {
+                    cases_len += 1;
+
+                    const item_ref = try sema.addConstant(operand_ty, cur);
+                    case_block.inline_case_capture = item_ref;
+
+                    case_block.instructions.shrinkRetainingCapacity(0);
+                    case_block.wip_capture_scope = child_block.wip_capture_scope;
+
+                    if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
+                    emit_bb = true;
+
+                    _ = sema.analyzeBodyInner(&case_block, special.body) catch |err| switch (err) {
+                        error.ComptimeBreak => {
+                            const zir_datas = sema.code.instructions.items(.data);
+                            const break_data = zir_datas[sema.comptime_break_inst].@"break";
+                            try sema.addRuntimeBreak(&case_block, .{
+                                .block_inst = break_data.block_inst,
+                                .operand = break_data.operand,
+                                .inst = sema.comptime_break_inst,
+                            });
+                        },
+                        else => |e| return e,
+                    };
+
+                    // try wip_captures.finalize();
+
+                    try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
+                    cases_extra.appendAssumeCapacity(1); // items_len
+                    cases_extra.appendAssumeCapacity(@intCast(u32, case_block.instructions.items.len));
+                    cases_extra.appendAssumeCapacity(@enumToInt(item_ref));
+                    cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
+                }
             },
             .Bool => {
                 var emit_bb = false;
@@ -10561,6 +10595,55 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     return sema.analyzeBlockBody(block, src, &child_block, merges);
 }
 
+const RangeSetUnhandledIterator = struct {
+    sema: *Sema,
+    block: *Block,
+    src: LazySrcLoc,
+    ty: Type,
+    cur: Value,
+    max: Value,
+    ranges: []const RangeSet.Range,
+    range_i: usize = 0,
+    first: bool = true,
+
+    fn init(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator {
+        const target = sema.mod.getTarget();
+        const min = try ty.minInt(sema.arena, target);
+        const max = try ty.maxInt(sema.arena, target);
+
+        return RangeSetUnhandledIterator{
+            .sema = sema,
+            .block = block,
+            .src = src,
+            .ty = ty,
+            .cur = min,
+            .max = max,
+            .ranges = range_set.ranges.items,
+        };
+    }
+
+    fn next(it: *RangeSetUnhandledIterator) !?Value {
+        while (it.range_i < it.ranges.len) : (it.range_i += 1) {
+            if (!it.first) {
+                it.cur = try it.sema.intAdd(it.block, it.src, it.cur, Value.one, it.ty);
+            }
+            it.first = false;
+            if (it.cur.compare(.lt, it.ranges[it.range_i].first, it.ty, it.sema.mod)) {
+                return it.cur;
+            }
+            it.cur = it.ranges[it.range_i].last;
+        }
+        if (!it.first) {
+            it.cur = try it.sema.intAdd(it.block, it.src, it.cur, Value.one, it.ty);
+        }
+        it.first = false;
+        if (it.cur.compare(.lte, it.max, it.ty, it.sema.mod)) {
+            return it.cur;
+        }
+        return null;
+    }
+};
+
 fn resolveSwitchItemVal(
     sema: *Sema,
     block: *Block,
test/behavior/inline_switch.zig
@@ -98,3 +98,34 @@ test "inline else enum" {
         inline else => |val| comptime if (@enumToInt(val) < 4) @compileError("bad"),
     }
 }
+
+test "inline else int with gaps" {
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
+    var a: u8 = 0;
+    switch (a) {
+        1...125, 128...254 => {},
+        inline else => |val| {
+            if (val != 0 and
+                val != 126 and
+                val != 127 and
+                val != 255)
+                @compileError("bad");
+        },
+    }
+}
+
+test "inline else int all values" {
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
+    var a: u2 = 0;
+    switch (a) {
+        inline else => |val| {
+            if (val != 0 and
+                val != 1 and
+                val != 2 and
+                val != 3)
+                @compileError("bad");
+        },
+    }
+}