From 83fa216c8d2375476ee02ccf53bf6b5a9ed7480e Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Tue, 27 Sep 2022 15:32:34 +0300 Subject: [PATCH] Sema: implement `inline else` for ints --- src/Sema.zig | 85 ++++++++++++++++++++++++++++++++- test/behavior/inline_switch.zig | 31 ++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 08deb31714..3a3dc5f6ec 100644 --- a/src/Sema.zig +++ b/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, diff --git a/test/behavior/inline_switch.zig b/test/behavior/inline_switch.zig index 353157fbfa..ecc7bba280 100644 --- a/test/behavior/inline_switch.zig +++ b/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"); + }, + } +}