aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-10-03 23:43:09 -0400
committerGitHub <noreply@github.com>2022-10-03 23:43:09 -0400
commitff534d22676b8a934acf1931f91d70c554a4bdca (patch)
treed04b360f7831f6428d13a85d9d9c0d7c04fc1079 /src/Sema.zig
parent9d5462dcb5b4b4601bdf2e628b9d80fb74000cb2 (diff)
parent17eea918aee98ca29c3762a7ecd568d2f14f66ef (diff)
downloadzig-ff534d22676b8a934acf1931f91d70c554a4bdca.tar.gz
zig-ff534d22676b8a934acf1931f91d70c554a4bdca.zip
Merge pull request #12979 from Vexu/inline-switch
Implement inline switch cases
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig531
1 files changed, 407 insertions, 124 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index eb46e7481a..c185752dc0 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -162,6 +162,9 @@ pub const Block = struct {
/// type of `err` in `else => |err|`
switch_else_err_ty: ?Type = null,
+ /// Value for switch_capture in an inline case
+ inline_case_capture: Air.Inst.Ref = .none,
+
const Param = struct {
/// `noreturn` means `anytype`.
ty: Type,
@@ -603,6 +606,21 @@ fn resolveBody(
return try sema.resolveInst(break_data.operand);
}
+fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) !void {
+ _ = sema.analyzeBodyInner(block, 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(block, .{
+ .block_inst = break_data.block_inst,
+ .operand = break_data.operand,
+ .inst = sema.comptime_break_inst,
+ });
+ },
+ else => |e| return e,
+ };
+}
+
pub fn analyzeBody(
sema: *Sema,
block: *Block,
@@ -796,6 +814,7 @@ fn analyzeBodyInner(
.switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
.switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
.switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true),
+ .switch_capture_tag => try sema.zirSwitchCaptureTag(block, inst),
.type_info => try sema.zirTypeInfo(block, inst),
.size_of => try sema.zirSizeOf(block, inst),
.bit_size_of => try sema.zirBitSizeOf(block, inst),
@@ -9030,13 +9049,38 @@ fn zirSwitchCapture(
const switch_info = zir_datas[capture_info.switch_inst].pl_node;
const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index);
const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node };
- const operand_is_ref = switch_extra.data.bits.is_ref;
const cond_inst = Zir.refToIndex(switch_extra.data.operand).?;
- const cond_info = sema.code.instructions.items(.data)[cond_inst].un_node;
+ const cond_info = zir_datas[cond_inst].un_node;
+ const cond_tag = sema.code.instructions.items(.tag)[cond_inst];
+ const operand_is_ref = cond_tag == .switch_cond_ref;
const operand_ptr = try sema.resolveInst(cond_info.operand);
const operand_ptr_ty = sema.typeOf(operand_ptr);
const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
+ if (block.inline_case_capture != .none) {
+ const item_val = sema.resolveConstValue(block, .unneeded, block.inline_case_capture, undefined) catch unreachable;
+ if (operand_ty.zigTypeTag() == .Union) {
+ const field_index = @intCast(u32, operand_ty.unionTagFieldIndex(item_val, sema.mod).?);
+ const union_obj = operand_ty.cast(Type.Payload.Union).?.data;
+ const field_ty = union_obj.fields.values()[field_index].ty;
+ if (is_ref) {
+ const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = field_ty,
+ .mutable = operand_ptr_ty.ptrIsMutable(),
+ .@"volatile" = operand_ptr_ty.isVolatilePtr(),
+ .@"addrspace" = operand_ptr_ty.ptrAddressSpace(),
+ });
+ return block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty);
+ } else {
+ return block.addStructFieldVal(operand_ptr, field_index, field_ty);
+ }
+ } else if (is_ref) {
+ return sema.addConstantMaybeRef(block, operand_src, operand_ty, item_val, true);
+ } else {
+ return block.inline_case_capture;
+ }
+ }
+
const operand = if (operand_is_ref)
try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
else
@@ -9045,7 +9089,6 @@ fn zirSwitchCapture(
if (capture_info.prong_index == std.math.maxInt(@TypeOf(capture_info.prong_index))) {
// It is the else/`_` prong.
if (is_ref) {
- assert(operand_is_ref);
return operand_ptr;
}
@@ -9105,8 +9148,6 @@ fn zirSwitchCapture(
}
if (is_ref) {
- assert(operand_is_ref);
-
const field_ty_ptr = try Type.ptr(sema.arena, sema.mod, .{
.pointee_type = first_field.ty,
.@"addrspace" = .generic,
@@ -9167,7 +9208,6 @@ fn zirSwitchCapture(
// In this case the capture value is just the passed-through value of the
// switch condition.
if (is_ref) {
- assert(operand_is_ref);
return operand_ptr;
} else {
return operand;
@@ -9176,6 +9216,33 @@ fn zirSwitchCapture(
}
}
+fn zirSwitchCaptureTag(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const zir_datas = sema.code.instructions.items(.data);
+ const inst_data = zir_datas[inst].un_tok;
+ const src = inst_data.src();
+
+ const switch_tag = sema.code.instructions.items(.tag)[Zir.refToIndex(inst_data.operand).?];
+ const is_ref = switch_tag == .switch_cond_ref;
+ const cond_data = zir_datas[Zir.refToIndex(inst_data.operand).?].un_node;
+ const operand_ptr = try sema.resolveInst(cond_data.operand);
+ const operand_ptr_ty = sema.typeOf(operand_ptr);
+ const operand_ty = if (is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
+
+ if (operand_ty.zigTypeTag() != .Union) {
+ const msg = msg: {
+ const msg = try sema.errMsg(block, src, "cannot capture tag of non-union type '{}'", .{
+ operand_ty.fmt(sema.mod),
+ });
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, operand_ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(msg);
+ }
+
+ return block.inline_case_capture;
+}
+
fn zirSwitchCond(
sema: *Sema,
block: *Block,
@@ -9273,14 +9340,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
} else 0;
const special_prong = extra.data.bits.specialProng();
- const special: struct { body: []const Zir.Inst.Index, end: usize } = switch (special_prong) {
- .none => .{ .body = &.{}, .end = header_extra_index },
+ const special: struct { body: []const Zir.Inst.Index, end: usize, is_inline: bool } = switch (special_prong) {
+ .none => .{ .body = &.{}, .end = header_extra_index, .is_inline = false },
.under, .@"else" => blk: {
- const body_len = sema.code.extra[header_extra_index];
+ const body_len = @truncate(u31, sema.code.extra[header_extra_index]);
const extra_body_start = header_extra_index + 1;
break :blk .{
.body = sema.code.extra[extra_body_start..][0..body_len],
.end = extra_body_start + body_len,
+ .is_inline = sema.code.extra[header_extra_index] >> 31 != 0,
};
},
};
@@ -9292,8 +9360,19 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
break :blk sema.typeOf(raw_operand);
};
const union_originally = maybe_union_ty.zigTypeTag() == .Union;
- var seen_union_fields: []?Module.SwitchProngSrc = &.{};
- defer gpa.free(seen_union_fields);
+
+ // Duplicate checking variables later also used for `inline else`.
+ var seen_enum_fields: []?Module.SwitchProngSrc = &.{};
+ var seen_errors = SwitchErrorSet.init(gpa);
+ var range_set = RangeSet.init(gpa, sema.mod);
+ var true_count: u8 = 0;
+ var false_count: u8 = 0;
+
+ defer {
+ range_set.deinit();
+ gpa.free(seen_enum_fields);
+ seen_errors.deinit();
+ }
var empty_enum = false;
@@ -9330,15 +9409,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
switch (operand_ty.zigTypeTag()) {
.Union => unreachable, // handled in zirSwitchCond
.Enum => {
- var seen_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount());
- empty_enum = seen_fields.len == 0 and !operand_ty.isNonexhaustiveEnum();
- defer if (!union_originally) gpa.free(seen_fields);
- if (union_originally) seen_union_fields = seen_fields;
- mem.set(?Module.SwitchProngSrc, seen_fields, null);
-
- // This is used for non-exhaustive enum values that do not correspond to any tags.
- var range_set = RangeSet.init(gpa, sema.mod);
- defer range_set.deinit();
+ seen_enum_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount());
+ empty_enum = seen_enum_fields.len == 0 and !operand_ty.isNonexhaustiveEnum();
+ mem.set(?Module.SwitchProngSrc, seen_enum_fields, null);
+ // `range_set` is used for non-exhaustive enum values that do not correspond to any tags.
var extra_index: usize = special.end;
{
@@ -9346,13 +9420,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
extra_index += body_len;
try sema.validateSwitchItemEnum(
block,
- seen_fields,
+ seen_enum_fields,
&range_set,
item_ref,
src_node_offset,
@@ -9367,7 +9441,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len + body_len;
@@ -9375,7 +9449,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
for (items) |item_ref, item_i| {
try sema.validateSwitchItemEnum(
block,
- seen_fields,
+ seen_enum_fields,
&range_set,
item_ref,
src_node_offset,
@@ -9386,7 +9460,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
}
}
- const all_tags_handled = for (seen_fields) |seen_src| {
+ const all_tags_handled = for (seen_enum_fields) |seen_src| {
if (seen_src == null) break false;
} else true;
@@ -9406,7 +9480,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
.{},
);
errdefer msg.destroy(sema.gpa);
- for (seen_fields) |seen_src, i| {
+ for (seen_enum_fields) |seen_src, i| {
if (seen_src != null) continue;
const field_name = operand_ty.enumFieldName(i);
@@ -9437,16 +9511,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
}
},
.ErrorSet => {
- var seen_errors = SwitchErrorSet.init(gpa);
- defer seen_errors.deinit();
-
var extra_index: usize = special.end;
{
var scalar_i: u32 = 0;
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
extra_index += body_len;
@@ -9466,7 +9537,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len + body_len;
@@ -9579,16 +9650,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
}
},
.Int, .ComptimeInt => {
- var range_set = RangeSet.init(gpa, sema.mod);
- defer range_set.deinit();
-
var extra_index: usize = special.end;
{
var scalar_i: u32 = 0;
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
extra_index += body_len;
@@ -9609,7 +9677,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len;
@@ -9677,16 +9745,13 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
}
},
.Bool => {
- var true_count: u8 = 0;
- var false_count: u8 = 0;
-
var extra_index: usize = special.end;
{
var scalar_i: u32 = 0;
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
extra_index += body_len;
@@ -9707,7 +9772,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len + body_len;
@@ -9771,7 +9836,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
extra_index += body_len;
@@ -9791,7 +9856,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len + body_len;
@@ -9871,7 +9936,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
const body = sema.code.extra[extra_index..][0..body_len];
extra_index += body_len;
@@ -9892,7 +9957,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len;
@@ -9933,7 +9998,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
}
- if (scalar_cases_len + multi_cases_len == 0) {
+ if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) {
if (empty_enum) {
return Air.Inst.Ref.void_value;
}
@@ -9965,7 +10030,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
const item_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
+ const is_inline = sema.code.extra[extra_index] >> 31 != 0;
extra_index += 1;
const body = sema.code.extra[extra_index..][0..body_len];
extra_index += body_len;
@@ -9975,8 +10041,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = wip_captures.scope;
+ case_block.inline_case_capture = .none;
const item = try sema.resolveInst(item_ref);
+ if (is_inline) case_block.inline_case_capture = item;
// `item` is already guaranteed to be constant known.
const analyze_body = if (union_originally) blk: {
@@ -9988,18 +10056,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
// nothing to do here
} else if (analyze_body) {
- _ = sema.analyzeBodyInner(&case_block, 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 sema.analyzeBodyRuntimeBreak(&case_block, body);
} else {
_ = try case_block.addNoOp(.unreach);
}
@@ -10021,19 +10078,115 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
defer gpa.free(prev_then_body);
var cases_len = scalar_cases_len;
- var multi_i: usize = 0;
+ var multi_i: u32 = 0;
while (multi_i < multi_cases_len) : (multi_i += 1) {
const items_len = sema.code.extra[extra_index];
extra_index += 1;
const ranges_len = sema.code.extra[extra_index];
extra_index += 1;
- const body_len = sema.code.extra[extra_index];
+ const body_len = @truncate(u31, sema.code.extra[extra_index]);
+ const is_inline = sema.code.extra[extra_index] >> 31 != 0;
extra_index += 1;
const items = sema.code.refSlice(extra_index, items_len);
extra_index += items_len;
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = child_block.wip_capture_scope;
+ case_block.inline_case_capture = .none;
+
+ // Generate all possible cases as scalar prongs.
+ if (is_inline) {
+ const body_start = extra_index + 2 * ranges_len;
+ const body = sema.code.extra[body_start..][0..body_len];
+ var emit_bb = false;
+
+ var range_i: u32 = 0;
+ while (range_i < ranges_len) : (range_i += 1) {
+ const first_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
+ extra_index += 1;
+ const last_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
+ extra_index += 1;
+
+ const item_first_ref = try sema.resolveInst(first_ref);
+ var item = sema.resolveConstValue(block, .unneeded, item_first_ref, undefined) catch unreachable;
+ const item_last_ref = try sema.resolveInst(last_ref);
+ const item_last = sema.resolveConstValue(block, .unneeded, item_last_ref, undefined) catch unreachable;
+
+ while (item.compare(.lte, item_last, operand_ty, sema.mod)) : ({
+ // Previous validation has resolved any possible lazy values.
+ item = try sema.intAddScalar(block, .unneeded, item, Value.one);
+ }) {
+ cases_len += 1;
+
+ const item_ref = try sema.addConstant(operand_ty, item);
+ 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) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) {
+ error.NeededSourceLocation => {
+ const case_src = Module.SwitchProngSrc{ .range = .{ .prong = multi_i, .item = range_i } };
+ const decl = sema.mod.declPtr(case_block.src_decl);
+ try sema.emitBackwardBranch(block, case_src.resolve(sema.gpa, decl, src_node_offset, .none));
+ return error.AnalysisFail;
+ },
+ else => return err,
+ };
+ emit_bb = true;
+
+ try sema.analyzeBodyRuntimeBreak(&case_block, body);
+
+ 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(case_block.inline_case_capture));
+ cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
+ }
+ }
+
+ for (items) |item_ref, item_i| {
+ cases_len += 1;
+
+ const item = try sema.resolveInst(item_ref);
+ case_block.inline_case_capture = item;
+
+ case_block.instructions.shrinkRetainingCapacity(0);
+ case_block.wip_capture_scope = child_block.wip_capture_scope;
+
+ const analyze_body = if (union_originally) blk: {
+ const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;
+ const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
+ break :blk field_ty.zigTypeTag() != .NoReturn;
+ } else true;
+
+ if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) {
+ error.NeededSourceLocation => {
+ const case_src = Module.SwitchProngSrc{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } };
+ const decl = sema.mod.declPtr(case_block.src_decl);
+ try sema.emitBackwardBranch(block, case_src.resolve(sema.gpa, decl, src_node_offset, .none));
+ return error.AnalysisFail;
+ },
+ else => return err,
+ };
+ emit_bb = true;
+
+ if (analyze_body) {
+ try sema.analyzeBodyRuntimeBreak(&case_block, body);
+ } else {
+ _ = try case_block.addNoOp(.unreach);
+ }
+
+ 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(case_block.inline_case_capture));
+ cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
+ }
+
+ extra_index += body_len;
+ continue;
+ }
var any_ok: Air.Inst.Ref = .none;
@@ -10058,18 +10211,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
// nothing to do here
} else if (analyze_body) {
- _ = sema.analyzeBodyInner(&case_block, 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 sema.analyzeBodyRuntimeBreak(&case_block, body);
} else {
_ = try case_block.addNoOp(.unreach);
}
@@ -10150,18 +10292,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
// nothing to do here
} else {
- _ = sema.analyzeBodyInner(&case_block, 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 sema.analyzeBodyRuntimeBreak(&case_block, body);
}
try wip_captures.finalize();
@@ -10192,14 +10323,150 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
var final_else_body: []const Air.Inst.Index = &.{};
if (special.body.len != 0 or !is_first or case_block.wantSafety()) {
+ var emit_bb = false;
+ if (special.is_inline) switch (operand_ty.zigTypeTag()) {
+ .Enum => {
+ if (operand_ty.isNonexhaustiveEnum() and !union_originally) {
+ return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
+ operand_ty.fmt(sema.mod),
+ });
+ }
+ for (seen_enum_fields) |f, i| {
+ if (f != null) continue;
+ cases_len += 1;
+
+ const item_val = try Value.Tag.enum_field_index.create(sema.arena, @intCast(u32, i));
+ const item_ref = try sema.addConstant(operand_ty, item_val);
+ case_block.inline_case_capture = item_ref;
+
+ case_block.instructions.shrinkRetainingCapacity(0);
+ case_block.wip_capture_scope = child_block.wip_capture_scope;
+
+ const analyze_body = if (union_originally) blk: {
+ const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod);
+ break :blk field_ty.zigTypeTag() != .NoReturn;
+ } else true;
+
+ if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
+ emit_bb = true;
+
+ if (analyze_body) {
+ try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+ } else {
+ _ = try case_block.addNoOp(.unreach);
+ }
+
+ 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(case_block.inline_case_capture));
+ cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
+ }
+ },
+ .ErrorSet => {
+ if (operand_ty.isAnyError()) {
+ return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
+ operand_ty.fmt(sema.mod),
+ });
+ }
+ for (operand_ty.errorSetNames()) |error_name| {
+ if (seen_errors.contains(error_name)) continue;
+ cases_len += 1;
+
+ const item_val = try Value.Tag.@"error".create(sema.arena, .{ .name = error_name });
+ const item_ref = try sema.addConstant(operand_ty, item_val);
+ 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;
+
+ try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+
+ 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(case_block.inline_case_capture));
+ cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
+ }
+ },
+ .Int => {
+ var it = try RangeSetUnhandledIterator.init(sema, block, special_prong_src, operand_ty, range_set);
+ 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;
+
+ try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+
+ 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(case_block.inline_case_capture));
+ cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
+ }
+ },
+ .Bool => {
+ if (true_count == 0) {
+ cases_len += 1;
+ case_block.inline_case_capture = Air.Inst.Ref.bool_true;
+
+ 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;
+
+ try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+
+ 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(case_block.inline_case_capture));
+ cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
+ }
+ if (false_count == 0) {
+ cases_len += 1;
+ case_block.inline_case_capture = Air.Inst.Ref.bool_false;
+
+ 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;
+
+ try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
+
+ 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(case_block.inline_case_capture));
+ cases_extra.appendSliceAssumeCapacity(case_block.instructions.items);
+ }
+ },
+ else => return sema.fail(block, special_prong_src, "cannot enumerate values of type '{}' for 'inline else'", .{
+ operand_ty.fmt(sema.mod),
+ }),
+ };
+
var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, child_block.wip_capture_scope);
defer wip_captures.deinit();
case_block.instructions.shrinkRetainingCapacity(0);
case_block.wip_capture_scope = wip_captures.scope;
+ case_block.inline_case_capture = .none;
- const analyze_body = if (union_originally)
- for (seen_union_fields) |seen_field, index| {
+ const analyze_body = if (union_originally and !special.is_inline)
+ for (seen_enum_fields) |seen_field, index| {
if (seen_field != null) continue;
const union_obj = maybe_union_ty.cast(Type.Payload.Union).?.data;
const field_ty = union_obj.fields.values()[index].ty;
@@ -10211,19 +10478,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
try sema.maybeErrorUnwrap(&case_block, special.body, operand))
{
// nothing to do here
- } else if (special.body.len != 0 and analyze_body) {
- _ = 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,
- };
+ } else if (special.body.len != 0 and analyze_body and !special.is_inline) {
+ try sema.analyzeBodyRuntimeBreak(&case_block, special.body);
} else {
// We still need a terminator in this block, but we have proven
// that it is unreachable.
@@ -10269,6 +10525,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,
@@ -15351,18 +15656,7 @@ fn zirCondbr(
sub_block.runtime_index.increment();
defer sub_block.instructions.deinit(gpa);
- _ = sema.analyzeBodyInner(&sub_block, then_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(&sub_block, .{
- .block_inst = break_data.block_inst,
- .operand = break_data.operand,
- .inst = sema.comptime_break_inst,
- });
- },
- else => |e| return e,
- };
+ try sema.analyzeBodyRuntimeBreak(&sub_block, then_body);
const true_instructions = sub_block.instructions.toOwnedSlice(gpa);
defer gpa.free(true_instructions);
@@ -15381,18 +15675,7 @@ fn zirCondbr(
if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?)) {
// nothing to do
} else {
- _ = sema.analyzeBodyInner(&sub_block, else_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(&sub_block, .{
- .block_inst = break_data.block_inst,
- .operand = break_data.operand,
- .inst = sema.comptime_break_inst,
- });
- },
- else => |e| return e,
- };
+ try sema.analyzeBodyRuntimeBreak(&sub_block, else_body);
}
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
true_instructions.len + sub_block.instructions.items.len);