mirror of
https://github.com/ziglang/zig.git
synced 2026-02-20 08:14:48 +00:00
Merge pull request #12979 from Vexu/inline-switch
Implement inline switch cases
This commit is contained in:
commit
ff534d2267
@ -4255,6 +4255,134 @@ test "enum literals with switch" {
|
||||
}
|
||||
{#code_end#}
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|Inline switch#}
|
||||
<p>
|
||||
Switch prongs can be marked as {#syntax#}inline{#endsyntax#} to generate
|
||||
the prong's body for each possible value it could have:
|
||||
</p>
|
||||
{#code_begin|test|test_inline_switch#}
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
const expectError = std.testing.expectError;
|
||||
|
||||
fn isFieldOptional(comptime T: type, field_index: usize) !bool {
|
||||
const fields = @typeInfo(T).Struct.fields;
|
||||
return switch (field_index) {
|
||||
// This prong is analyzed `fields.len - 1` times with `idx` being an
|
||||
// unique comptime known value each time.
|
||||
inline 0...fields.len - 1 => |idx| @typeInfo(fields[idx].field_type) == .Optional,
|
||||
else => return error.IndexOutOfBounds,
|
||||
};
|
||||
}
|
||||
|
||||
const Struct1 = struct { a: u32, b: ?u32 };
|
||||
|
||||
test "using @typeInfo with runtime values" {
|
||||
var index: usize = 0;
|
||||
try expect(!try isFieldOptional(Struct1, index));
|
||||
index += 1;
|
||||
try expect(try isFieldOptional(Struct1, index));
|
||||
index += 1;
|
||||
try expectError(error.IndexOutOfBounds, isFieldOptional(Struct1, index));
|
||||
}
|
||||
|
||||
// Calls to `isFieldOptional` on `Struct1` get unrolled to an equivalent
|
||||
// of this function:
|
||||
fn isFieldOptionalUnrolled(field_index: usize) !bool {
|
||||
return switch (field_index) {
|
||||
0 => false,
|
||||
1 => true,
|
||||
else => return error.IndexOutOfBounds,
|
||||
};
|
||||
}
|
||||
{#code_end#}
|
||||
<p>
|
||||
{#syntax#}inline else{#endsyntax#} prongs can be used as a type safe
|
||||
alternative to {#syntax#}inline for{#endsyntax#} loops:
|
||||
</p>
|
||||
{#code_begin|test|test_inline_else#}
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
const SliceTypeA = extern struct {
|
||||
len: usize,
|
||||
ptr: [*]u32,
|
||||
};
|
||||
const SliceTypeB = extern struct {
|
||||
ptr: [*]SliceTypeA,
|
||||
len: usize,
|
||||
};
|
||||
const AnySlice = union(enum) {
|
||||
a: SliceTypeA,
|
||||
b: SliceTypeB,
|
||||
c: []const u8,
|
||||
d: []AnySlice,
|
||||
};
|
||||
|
||||
fn withFor(any: AnySlice) usize {
|
||||
const Tag = @typeInfo(AnySlice).Union.tag_type.?;
|
||||
inline for (@typeInfo(Tag).Enum.fields) |field| {
|
||||
// With `inline for` the function gets generated as
|
||||
// a series of `if` statements relying on the optimizer
|
||||
// to convert it to a switch.
|
||||
if (field.value == @enumToInt(any)) {
|
||||
return @field(any, field.name).len;
|
||||
}
|
||||
}
|
||||
// When using `inline for` the compiler doesn't know that every
|
||||
// possible case has been handled requiring an explicit `unreachable`.
|
||||
unreachable;
|
||||
}
|
||||
|
||||
fn withSwitch(any: AnySlice) usize {
|
||||
return switch (any) {
|
||||
// With `inline else` the function is explicitly generated
|
||||
// as the desired switch and the compiler can check that
|
||||
// every possible case is handled.
|
||||
inline else => |slice| slice.len,
|
||||
};
|
||||
}
|
||||
|
||||
test "inline for and inline else similarity" {
|
||||
var any = AnySlice{ .c = "hello" };
|
||||
try expect(withFor(any) == 5);
|
||||
try expect(withSwitch(any) == 5);
|
||||
}
|
||||
{#code_end#}
|
||||
<p>
|
||||
When using an inline prong switching on an union an additional
|
||||
capture can be used to obtain the union's enum tag value.
|
||||
</p>
|
||||
{#code_begin|test|test_inline_switch_union_tag#}
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
|
||||
const U = union(enum) {
|
||||
a: u32,
|
||||
b: f32,
|
||||
};
|
||||
|
||||
fn getNum(u: U) u32 {
|
||||
switch (u) {
|
||||
// Here `num` is a runtime known value that is either
|
||||
// `u.a` or `u.b` and `tag` is `u`'s comptime known tag value.
|
||||
inline else => |num, tag| {
|
||||
if (tag == .b) {
|
||||
return @floatToInt(u32, num);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "test" {
|
||||
var u = U{ .b = 42 };
|
||||
try expect(getNum(u) == 42);
|
||||
}
|
||||
{#code_end#}
|
||||
{#see_also|inline while|inline for#}
|
||||
{#header_close#}
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|while#}
|
||||
|
||||
@ -643,11 +643,23 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
|
||||
n = datas[n].lhs;
|
||||
}
|
||||
},
|
||||
.switch_case_inline_one => {
|
||||
if (datas[n].lhs == 0) {
|
||||
return main_tokens[n] - 2 - end_offset; // else token
|
||||
} else {
|
||||
return firstToken(tree, datas[n].lhs) - 1;
|
||||
}
|
||||
},
|
||||
.switch_case => {
|
||||
const extra = tree.extraData(datas[n].lhs, Node.SubRange);
|
||||
assert(extra.end - extra.start > 0);
|
||||
n = tree.extra_data[extra.start];
|
||||
},
|
||||
.switch_case_inline => {
|
||||
const extra = tree.extraData(datas[n].lhs, Node.SubRange);
|
||||
assert(extra.end - extra.start > 0);
|
||||
return firstToken(tree, tree.extra_data[extra.start]) - 1;
|
||||
},
|
||||
|
||||
.asm_output, .asm_input => {
|
||||
assert(token_tags[main_tokens[n] - 1] == .l_bracket);
|
||||
@ -763,7 +775,9 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
|
||||
.ptr_type_bit_range,
|
||||
.array_type,
|
||||
.switch_case_one,
|
||||
.switch_case_inline_one,
|
||||
.switch_case,
|
||||
.switch_case_inline,
|
||||
.switch_range,
|
||||
=> n = datas[n].rhs,
|
||||
|
||||
@ -1755,7 +1769,7 @@ pub fn switchCaseOne(tree: Ast, node: Node.Index) full.SwitchCase {
|
||||
.values = if (data.lhs == 0) values[0..0] else values[0..1],
|
||||
.arrow_token = tree.nodes.items(.main_token)[node],
|
||||
.target_expr = data.rhs,
|
||||
});
|
||||
}, node);
|
||||
}
|
||||
|
||||
pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase {
|
||||
@ -1765,7 +1779,7 @@ pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase {
|
||||
.values = tree.extra_data[extra.start..extra.end],
|
||||
.arrow_token = tree.nodes.items(.main_token)[node],
|
||||
.target_expr = data.rhs,
|
||||
});
|
||||
}, node);
|
||||
}
|
||||
|
||||
pub fn asmSimple(tree: Ast, node: Node.Index) full.Asm {
|
||||
@ -2038,15 +2052,21 @@ fn fullContainerDecl(tree: Ast, info: full.ContainerDecl.Components) full.Contai
|
||||
return result;
|
||||
}
|
||||
|
||||
fn fullSwitchCase(tree: Ast, info: full.SwitchCase.Components) full.SwitchCase {
|
||||
fn fullSwitchCase(tree: Ast, info: full.SwitchCase.Components, node: Node.Index) full.SwitchCase {
|
||||
const token_tags = tree.tokens.items(.tag);
|
||||
const node_tags = tree.nodes.items(.tag);
|
||||
var result: full.SwitchCase = .{
|
||||
.ast = info,
|
||||
.payload_token = null,
|
||||
.inline_token = null,
|
||||
};
|
||||
if (token_tags[info.arrow_token + 1] == .pipe) {
|
||||
result.payload_token = info.arrow_token + 2;
|
||||
}
|
||||
switch (node_tags[node]) {
|
||||
.switch_case_inline, .switch_case_inline_one => result.inline_token = firstToken(tree, node),
|
||||
else => {},
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -2454,6 +2474,7 @@ pub const full = struct {
|
||||
};
|
||||
|
||||
pub const SwitchCase = struct {
|
||||
inline_token: ?TokenIndex,
|
||||
/// Points to the first token after the `|`. Will either be an identifier or
|
||||
/// a `*` (with an identifier immediately after it).
|
||||
payload_token: ?TokenIndex,
|
||||
@ -2847,9 +2868,13 @@ pub const Node = struct {
|
||||
/// `lhs => rhs`. If lhs is omitted it means `else`.
|
||||
/// main_token is the `=>`
|
||||
switch_case_one,
|
||||
/// Same ast `switch_case_one` but the case is inline
|
||||
switch_case_inline_one,
|
||||
/// `a, b, c => rhs`. `SubRange[lhs]`.
|
||||
/// main_token is the `=>`
|
||||
switch_case,
|
||||
/// Same ast `switch_case` but the case is inline
|
||||
switch_case_inline,
|
||||
/// `lhs...rhs`.
|
||||
switch_range,
|
||||
/// `while (lhs) rhs`.
|
||||
|
||||
@ -3100,7 +3100,7 @@ const Parser = struct {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
/// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr
|
||||
/// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr
|
||||
/// SwitchCase
|
||||
/// <- SwitchItem (COMMA SwitchItem)* COMMA?
|
||||
/// / KEYWORD_else
|
||||
@ -3108,6 +3108,8 @@ const Parser = struct {
|
||||
const scratch_top = p.scratch.items.len;
|
||||
defer p.scratch.shrinkRetainingCapacity(scratch_top);
|
||||
|
||||
const is_inline = p.eatToken(.keyword_inline) != null;
|
||||
|
||||
if (p.eatToken(.keyword_else) == null) {
|
||||
while (true) {
|
||||
const item = try p.parseSwitchItem();
|
||||
@ -3115,15 +3117,18 @@ const Parser = struct {
|
||||
try p.scratch.append(p.gpa, item);
|
||||
if (p.eatToken(.comma) == null) break;
|
||||
}
|
||||
if (scratch_top == p.scratch.items.len) return null_node;
|
||||
if (scratch_top == p.scratch.items.len) {
|
||||
if (is_inline) p.tok_i -= 1;
|
||||
return null_node;
|
||||
}
|
||||
}
|
||||
const arrow_token = try p.expectToken(.equal_angle_bracket_right);
|
||||
_ = try p.parsePtrPayload();
|
||||
_ = try p.parsePtrIndexPayload();
|
||||
|
||||
const items = p.scratch.items[scratch_top..];
|
||||
switch (items.len) {
|
||||
0 => return p.addNode(.{
|
||||
.tag = .switch_case_one,
|
||||
.tag = if (is_inline) .switch_case_inline_one else .switch_case_one,
|
||||
.main_token = arrow_token,
|
||||
.data = .{
|
||||
.lhs = 0,
|
||||
@ -3131,7 +3136,7 @@ const Parser = struct {
|
||||
},
|
||||
}),
|
||||
1 => return p.addNode(.{
|
||||
.tag = .switch_case_one,
|
||||
.tag = if (is_inline) .switch_case_inline_one else .switch_case_one,
|
||||
.main_token = arrow_token,
|
||||
.data = .{
|
||||
.lhs = items[0],
|
||||
@ -3139,7 +3144,7 @@ const Parser = struct {
|
||||
},
|
||||
}),
|
||||
else => return p.addNode(.{
|
||||
.tag = .switch_case,
|
||||
.tag = if (is_inline) .switch_case_inline else .switch_case,
|
||||
.main_token = arrow_token,
|
||||
.data = .{
|
||||
.lhs = try p.addExtra(try p.listToSpan(items)),
|
||||
|
||||
@ -3276,6 +3276,8 @@ test "zig fmt: switch" {
|
||||
\\ switch (u) {
|
||||
\\ Union.Int => |int| {},
|
||||
\\ Union.Float => |*float| unreachable,
|
||||
\\ 1 => |a, b| unreachable,
|
||||
\\ 2 => |*a, b| unreachable,
|
||||
\\ }
|
||||
\\}
|
||||
\\
|
||||
|
||||
@ -685,8 +685,8 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
|
||||
return renderToken(ais, tree, tree.lastToken(node), space); // rbrace
|
||||
},
|
||||
|
||||
.switch_case_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space),
|
||||
.switch_case => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space),
|
||||
.switch_case_one, .switch_case_inline_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space),
|
||||
.switch_case, .switch_case_inline => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space),
|
||||
|
||||
.while_simple => return renderWhile(gpa, ais, tree, tree.whileSimple(node), space),
|
||||
.while_cont => return renderWhile(gpa, ais, tree, tree.whileCont(node), space),
|
||||
@ -1509,6 +1509,11 @@ fn renderSwitchCase(
|
||||
break :blk hasComment(tree, tree.firstToken(switch_case.ast.values[0]), switch_case.ast.arrow_token);
|
||||
};
|
||||
|
||||
// render inline keyword
|
||||
if (switch_case.inline_token) |some| {
|
||||
try renderToken(ais, tree, some, .space);
|
||||
}
|
||||
|
||||
// Render everything before the arrow
|
||||
if (switch_case.ast.values.len == 0) {
|
||||
try renderToken(ais, tree, switch_case.ast.arrow_token - 1, .space); // else keyword
|
||||
@ -1536,13 +1541,17 @@ fn renderSwitchCase(
|
||||
|
||||
if (switch_case.payload_token) |payload_token| {
|
||||
try renderToken(ais, tree, payload_token - 1, .none); // pipe
|
||||
const ident = payload_token + @boolToInt(token_tags[payload_token] == .asterisk);
|
||||
if (token_tags[payload_token] == .asterisk) {
|
||||
try renderToken(ais, tree, payload_token, .none); // asterisk
|
||||
try renderToken(ais, tree, payload_token + 1, .none); // identifier
|
||||
try renderToken(ais, tree, payload_token + 2, pre_target_space); // pipe
|
||||
}
|
||||
try renderToken(ais, tree, ident, .none); // identifier
|
||||
if (token_tags[ident + 1] == .comma) {
|
||||
try renderToken(ais, tree, ident + 1, .space); // ,
|
||||
try renderToken(ais, tree, ident + 2, .none); // identifier
|
||||
try renderToken(ais, tree, ident + 3, pre_target_space); // pipe
|
||||
} else {
|
||||
try renderToken(ais, tree, payload_token, .none); // identifier
|
||||
try renderToken(ais, tree, payload_token + 1, pre_target_space); // pipe
|
||||
try renderToken(ais, tree, ident + 1, pre_target_space); // pipe
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
170
src/AstGen.zig
170
src/AstGen.zig
@ -386,7 +386,9 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
|
||||
.simple_var_decl => unreachable,
|
||||
.aligned_var_decl => unreachable,
|
||||
.switch_case => unreachable,
|
||||
.switch_case_inline => unreachable,
|
||||
.switch_case_one => unreachable,
|
||||
.switch_case_inline_one => unreachable,
|
||||
.container_field_init => unreachable,
|
||||
.container_field_align => unreachable,
|
||||
.container_field => unreachable,
|
||||
@ -600,7 +602,9 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
|
||||
.@"errdefer" => unreachable, // Handled in `blockExpr`.
|
||||
|
||||
.switch_case => unreachable, // Handled in `switchExpr`.
|
||||
.switch_case_inline => unreachable, // Handled in `switchExpr`.
|
||||
.switch_case_one => unreachable, // Handled in `switchExpr`.
|
||||
.switch_case_inline_one => unreachable, // Handled in `switchExpr`.
|
||||
.switch_range => unreachable, // Handled in `switchExpr`.
|
||||
|
||||
.asm_output => unreachable, // Handled in `asmExpr`.
|
||||
@ -2369,6 +2373,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
|
||||
.switch_capture_ref,
|
||||
.switch_capture_multi,
|
||||
.switch_capture_multi_ref,
|
||||
.switch_capture_tag,
|
||||
.struct_init_empty,
|
||||
.struct_init,
|
||||
.struct_init_ref,
|
||||
@ -6213,14 +6218,15 @@ fn switchExpr(
|
||||
var any_payload_is_ref = false;
|
||||
var scalar_cases_len: u32 = 0;
|
||||
var multi_cases_len: u32 = 0;
|
||||
var inline_cases_len: u32 = 0;
|
||||
var special_prong: Zir.SpecialProng = .none;
|
||||
var special_node: Ast.Node.Index = 0;
|
||||
var else_src: ?Ast.TokenIndex = null;
|
||||
var underscore_src: ?Ast.TokenIndex = null;
|
||||
for (case_nodes) |case_node| {
|
||||
const case = switch (node_tags[case_node]) {
|
||||
.switch_case_one => tree.switchCaseOne(case_node),
|
||||
.switch_case => tree.switchCase(case_node),
|
||||
.switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
|
||||
.switch_case, .switch_case_inline => tree.switchCase(case_node),
|
||||
else => unreachable,
|
||||
};
|
||||
if (case.payload_token) |payload_token| {
|
||||
@ -6304,6 +6310,9 @@ fn switchExpr(
|
||||
},
|
||||
);
|
||||
}
|
||||
if (case.inline_token != null) {
|
||||
return astgen.failTok(case_src, "cannot inline '_' prong", .{});
|
||||
}
|
||||
special_node = case_node;
|
||||
special_prong = .under;
|
||||
underscore_src = case_src;
|
||||
@ -6315,6 +6324,9 @@ fn switchExpr(
|
||||
} else {
|
||||
multi_cases_len += 1;
|
||||
}
|
||||
if (case.inline_token != null) {
|
||||
inline_cases_len += 1;
|
||||
}
|
||||
}
|
||||
|
||||
const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
|
||||
@ -6354,8 +6366,8 @@ fn switchExpr(
|
||||
var scalar_case_index: u32 = 0;
|
||||
for (case_nodes) |case_node| {
|
||||
const case = switch (node_tags[case_node]) {
|
||||
.switch_case_one => tree.switchCaseOne(case_node),
|
||||
.switch_case => tree.switchCase(case_node),
|
||||
.switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
|
||||
.switch_case, .switch_case_inline => tree.switchCase(case_node),
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
@ -6364,8 +6376,12 @@ fn switchExpr(
|
||||
|
||||
var dbg_var_name: ?u32 = null;
|
||||
var dbg_var_inst: Zir.Inst.Ref = undefined;
|
||||
var dbg_var_tag_name: ?u32 = null;
|
||||
var dbg_var_tag_inst: Zir.Inst.Ref = undefined;
|
||||
var capture_inst: Zir.Inst.Index = 0;
|
||||
var tag_inst: Zir.Inst.Index = 0;
|
||||
var capture_val_scope: Scope.LocalVal = undefined;
|
||||
var tag_scope: Scope.LocalVal = undefined;
|
||||
const sub_scope = blk: {
|
||||
const payload_token = case.payload_token orelse break :blk &case_scope.base;
|
||||
const ident = if (token_tags[payload_token] == .asterisk)
|
||||
@ -6373,59 +6389,96 @@ fn switchExpr(
|
||||
else
|
||||
payload_token;
|
||||
const is_ptr = ident != payload_token;
|
||||
if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
|
||||
const ident_slice = tree.tokenSlice(ident);
|
||||
var payload_sub_scope: *Scope = undefined;
|
||||
if (mem.eql(u8, ident_slice, "_")) {
|
||||
if (is_ptr) {
|
||||
return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
|
||||
}
|
||||
break :blk &case_scope.base;
|
||||
}
|
||||
if (case_node == special_node) {
|
||||
const capture_tag: Zir.Inst.Tag = if (is_ptr)
|
||||
.switch_capture_ref
|
||||
else
|
||||
.switch_capture;
|
||||
capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
|
||||
try astgen.instructions.append(gpa, .{
|
||||
.tag = capture_tag,
|
||||
.data = .{
|
||||
.switch_capture = .{
|
||||
.switch_inst = switch_block,
|
||||
// Max int communicates that this is the else/underscore prong.
|
||||
.prong_index = std.math.maxInt(u32),
|
||||
},
|
||||
},
|
||||
});
|
||||
payload_sub_scope = &case_scope.base;
|
||||
} else {
|
||||
const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
|
||||
const is_ptr_bits: u2 = @boolToInt(is_ptr);
|
||||
const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
|
||||
0b00 => .switch_capture,
|
||||
0b01 => .switch_capture_ref,
|
||||
0b10 => .switch_capture_multi,
|
||||
0b11 => .switch_capture_multi_ref,
|
||||
if (case_node == special_node) {
|
||||
const capture_tag: Zir.Inst.Tag = if (is_ptr)
|
||||
.switch_capture_ref
|
||||
else
|
||||
.switch_capture;
|
||||
capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
|
||||
try astgen.instructions.append(gpa, .{
|
||||
.tag = capture_tag,
|
||||
.data = .{
|
||||
.switch_capture = .{
|
||||
.switch_inst = switch_block,
|
||||
// Max int communicates that this is the else/underscore prong.
|
||||
.prong_index = std.math.maxInt(u32),
|
||||
},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
|
||||
const is_ptr_bits: u2 = @boolToInt(is_ptr);
|
||||
const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
|
||||
0b00 => .switch_capture,
|
||||
0b01 => .switch_capture_ref,
|
||||
0b10 => .switch_capture_multi,
|
||||
0b11 => .switch_capture_multi_ref,
|
||||
};
|
||||
const capture_index = if (is_multi_case) multi_case_index else scalar_case_index;
|
||||
capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
|
||||
try astgen.instructions.append(gpa, .{
|
||||
.tag = capture_tag,
|
||||
.data = .{ .switch_capture = .{
|
||||
.switch_inst = switch_block,
|
||||
.prong_index = capture_index,
|
||||
} },
|
||||
});
|
||||
}
|
||||
const capture_name = try astgen.identAsString(ident);
|
||||
try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice);
|
||||
capture_val_scope = .{
|
||||
.parent = &case_scope.base,
|
||||
.gen_zir = &case_scope,
|
||||
.name = capture_name,
|
||||
.inst = indexToRef(capture_inst),
|
||||
.token_src = payload_token,
|
||||
.id_cat = .@"capture",
|
||||
};
|
||||
const capture_index = if (is_multi_case) multi_case_index else scalar_case_index;
|
||||
capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
|
||||
try astgen.instructions.append(gpa, .{
|
||||
.tag = capture_tag,
|
||||
.data = .{ .switch_capture = .{
|
||||
.switch_inst = switch_block,
|
||||
.prong_index = capture_index,
|
||||
} },
|
||||
});
|
||||
dbg_var_name = capture_name;
|
||||
dbg_var_inst = indexToRef(capture_inst);
|
||||
payload_sub_scope = &capture_val_scope.base;
|
||||
}
|
||||
const capture_name = try astgen.identAsString(ident);
|
||||
capture_val_scope = .{
|
||||
.parent = &case_scope.base,
|
||||
|
||||
const tag_token = if (token_tags[ident + 1] == .comma)
|
||||
ident + 2
|
||||
else
|
||||
break :blk payload_sub_scope;
|
||||
const tag_slice = tree.tokenSlice(tag_token);
|
||||
if (mem.eql(u8, tag_slice, "_")) {
|
||||
return astgen.failTok(tag_token, "discard of tag capture; omit it instead", .{});
|
||||
} else if (case.inline_token == null) {
|
||||
return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});
|
||||
}
|
||||
const tag_name = try astgen.identAsString(tag_token);
|
||||
try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice);
|
||||
tag_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
|
||||
try astgen.instructions.append(gpa, .{
|
||||
.tag = .switch_capture_tag,
|
||||
.data = .{ .un_tok = .{
|
||||
.operand = cond,
|
||||
.src_tok = case_scope.tokenIndexToRelative(tag_token),
|
||||
} },
|
||||
});
|
||||
|
||||
tag_scope = .{
|
||||
.parent = payload_sub_scope,
|
||||
.gen_zir = &case_scope,
|
||||
.name = capture_name,
|
||||
.inst = indexToRef(capture_inst),
|
||||
.token_src = payload_token,
|
||||
.id_cat = .@"capture",
|
||||
.name = tag_name,
|
||||
.inst = indexToRef(tag_inst),
|
||||
.token_src = tag_token,
|
||||
.id_cat = .@"switch tag capture",
|
||||
};
|
||||
dbg_var_name = capture_name;
|
||||
dbg_var_inst = indexToRef(capture_inst);
|
||||
break :blk &capture_val_scope.base;
|
||||
dbg_var_tag_name = tag_name;
|
||||
dbg_var_tag_inst = indexToRef(tag_inst);
|
||||
break :blk &tag_scope.base;
|
||||
};
|
||||
|
||||
const header_index = @intCast(u32, payloads.items.len);
|
||||
@ -6480,10 +6533,14 @@ fn switchExpr(
|
||||
defer case_scope.unstack();
|
||||
|
||||
if (capture_inst != 0) try case_scope.instructions.append(gpa, capture_inst);
|
||||
if (tag_inst != 0) try case_scope.instructions.append(gpa, tag_inst);
|
||||
try case_scope.addDbgBlockBegin();
|
||||
if (dbg_var_name) |some| {
|
||||
try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
|
||||
}
|
||||
if (dbg_var_tag_name) |some| {
|
||||
try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_tag_inst);
|
||||
}
|
||||
const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
|
||||
try checkUsed(parent_gz, &case_scope.base, sub_scope);
|
||||
try case_scope.addDbgBlockEnd();
|
||||
@ -6495,7 +6552,8 @@ fn switchExpr(
|
||||
const case_slice = case_scope.instructionsSlice();
|
||||
const body_len = astgen.countBodyLenAfterFixups(case_slice);
|
||||
try payloads.ensureUnusedCapacity(gpa, body_len);
|
||||
payloads.items[body_len_index] = body_len;
|
||||
const inline_bit = @as(u32, @boolToInt(case.inline_token != null)) << 31;
|
||||
payloads.items[body_len_index] = body_len | inline_bit;
|
||||
appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
|
||||
}
|
||||
}
|
||||
@ -6509,7 +6567,6 @@ fn switchExpr(
|
||||
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
|
||||
.operand = cond,
|
||||
.bits = Zir.Inst.SwitchBlock.Bits{
|
||||
.is_ref = any_payload_is_ref,
|
||||
.has_multi_cases = multi_cases_len != 0,
|
||||
.has_else = special_prong == .@"else",
|
||||
.has_under = special_prong == .under,
|
||||
@ -6543,7 +6600,7 @@ fn switchExpr(
|
||||
end_index += 3 + items_len + 2 * ranges_len;
|
||||
}
|
||||
|
||||
const body_len = payloads.items[body_len_index];
|
||||
const body_len = @truncate(u31, payloads.items[body_len_index]);
|
||||
end_index += body_len;
|
||||
|
||||
switch (strat.tag) {
|
||||
@ -8433,7 +8490,9 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_
|
||||
.@"usingnamespace",
|
||||
.test_decl,
|
||||
.switch_case,
|
||||
.switch_case_inline,
|
||||
.switch_case_one,
|
||||
.switch_case_inline_one,
|
||||
.container_field_init,
|
||||
.container_field_align,
|
||||
.container_field,
|
||||
@ -8665,7 +8724,9 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.Ev
|
||||
.@"usingnamespace",
|
||||
.test_decl,
|
||||
.switch_case,
|
||||
.switch_case_inline,
|
||||
.switch_case_one,
|
||||
.switch_case_inline_one,
|
||||
.container_field_init,
|
||||
.container_field_align,
|
||||
.container_field,
|
||||
@ -8876,7 +8937,9 @@ fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.In
|
||||
.@"usingnamespace",
|
||||
.test_decl,
|
||||
.switch_case,
|
||||
.switch_case_inline,
|
||||
.switch_case_one,
|
||||
.switch_case_inline_one,
|
||||
.container_field_init,
|
||||
.container_field_align,
|
||||
.container_field,
|
||||
@ -9118,7 +9181,9 @@ fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
|
||||
.@"usingnamespace",
|
||||
.test_decl,
|
||||
.switch_case,
|
||||
.switch_case_inline,
|
||||
.switch_case_one,
|
||||
.switch_case_inline_one,
|
||||
.container_field_init,
|
||||
.container_field_align,
|
||||
.container_field,
|
||||
@ -10051,6 +10116,7 @@ const Scope = struct {
|
||||
@"local constant",
|
||||
@"local variable",
|
||||
@"loop index capture",
|
||||
@"switch tag capture",
|
||||
@"capture",
|
||||
};
|
||||
|
||||
|
||||
@ -2445,8 +2445,8 @@ pub const SrcLoc = struct {
|
||||
const case_nodes = tree.extra_data[extra.start..extra.end];
|
||||
for (case_nodes) |case_node| {
|
||||
const case = switch (node_tags[case_node]) {
|
||||
.switch_case_one => tree.switchCaseOne(case_node),
|
||||
.switch_case => tree.switchCase(case_node),
|
||||
.switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
|
||||
.switch_case, .switch_case_inline => tree.switchCase(case_node),
|
||||
else => unreachable,
|
||||
};
|
||||
const is_special = (case.ast.values.len == 0) or
|
||||
@ -2469,8 +2469,8 @@ pub const SrcLoc = struct {
|
||||
const case_nodes = tree.extra_data[extra.start..extra.end];
|
||||
for (case_nodes) |case_node| {
|
||||
const case = switch (node_tags[case_node]) {
|
||||
.switch_case_one => tree.switchCaseOne(case_node),
|
||||
.switch_case => tree.switchCase(case_node),
|
||||
.switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
|
||||
.switch_case, .switch_case_inline => tree.switchCase(case_node),
|
||||
else => unreachable,
|
||||
};
|
||||
const is_special = (case.ast.values.len == 0) or
|
||||
@ -2491,8 +2491,8 @@ pub const SrcLoc = struct {
|
||||
const case_node = src_loc.declRelativeToNodeIndex(node_off);
|
||||
const node_tags = tree.nodes.items(.tag);
|
||||
const case = switch (node_tags[case_node]) {
|
||||
.switch_case_one => tree.switchCaseOne(case_node),
|
||||
.switch_case => tree.switchCase(case_node),
|
||||
.switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
|
||||
.switch_case, .switch_case_inline => tree.switchCase(case_node),
|
||||
else => unreachable,
|
||||
};
|
||||
const start_tok = case.payload_token.?;
|
||||
@ -5940,8 +5940,8 @@ pub const SwitchProngSrc = union(enum) {
|
||||
var scalar_i: u32 = 0;
|
||||
for (case_nodes) |case_node| {
|
||||
const case = switch (node_tags[case_node]) {
|
||||
.switch_case_one => tree.switchCaseOne(case_node),
|
||||
.switch_case => tree.switchCase(case_node),
|
||||
.switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
|
||||
.switch_case, .switch_case_inline => tree.switchCase(case_node),
|
||||
else => unreachable,
|
||||
};
|
||||
if (case.ast.values.len == 0)
|
||||
|
||||
531
src/Sema.zig
531
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);
|
||||
|
||||
27
src/Zir.zig
27
src/Zir.zig
@ -683,6 +683,9 @@ pub const Inst = struct {
|
||||
/// Result is a pointer to the value.
|
||||
/// Uses the `switch_capture` field.
|
||||
switch_capture_multi_ref,
|
||||
/// Produces the capture value for an inline switch prong tag capture.
|
||||
/// Uses the `un_tok` field.
|
||||
switch_capture_tag,
|
||||
/// Given a
|
||||
/// *A returns *A
|
||||
/// *E!A returns *A
|
||||
@ -1128,6 +1131,7 @@ pub const Inst = struct {
|
||||
.switch_capture_ref,
|
||||
.switch_capture_multi,
|
||||
.switch_capture_multi_ref,
|
||||
.switch_capture_tag,
|
||||
.switch_block,
|
||||
.switch_cond,
|
||||
.switch_cond_ref,
|
||||
@ -1422,6 +1426,7 @@ pub const Inst = struct {
|
||||
.switch_capture_ref,
|
||||
.switch_capture_multi,
|
||||
.switch_capture_multi_ref,
|
||||
.switch_capture_tag,
|
||||
.switch_block,
|
||||
.switch_cond,
|
||||
.switch_cond_ref,
|
||||
@ -1681,6 +1686,7 @@ pub const Inst = struct {
|
||||
.switch_capture_ref = .switch_capture,
|
||||
.switch_capture_multi = .switch_capture,
|
||||
.switch_capture_multi_ref = .switch_capture,
|
||||
.switch_capture_tag = .un_tok,
|
||||
.array_base_ptr = .un_node,
|
||||
.field_base_ptr = .un_node,
|
||||
.validate_array_init_ty = .pl_node,
|
||||
@ -2952,12 +2958,9 @@ pub const Inst = struct {
|
||||
has_else: bool,
|
||||
/// If true, there is an underscore prong. This is mutually exclusive with `has_else`.
|
||||
has_under: bool,
|
||||
/// If true, the `operand` is a pointer to the value being switched on.
|
||||
/// TODO this flag is redundant with the tag of operand and can be removed.
|
||||
is_ref: bool,
|
||||
scalar_cases_len: ScalarCasesLen,
|
||||
|
||||
pub const ScalarCasesLen = u28;
|
||||
pub const ScalarCasesLen = u29;
|
||||
|
||||
pub fn specialProng(bits: Bits) SpecialProng {
|
||||
const has_else: u2 = @boolToInt(bits.has_else);
|
||||
@ -2993,7 +2996,7 @@ pub const Inst = struct {
|
||||
}
|
||||
|
||||
if (self.bits.specialProng() != .none) {
|
||||
const body_len = zir.extra[extra_index];
|
||||
const body_len = @truncate(u31, zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body = zir.extra[extra_index..][0..body_len];
|
||||
extra_index += body.len;
|
||||
@ -3003,7 +3006,7 @@ pub const Inst = struct {
|
||||
while (true) : (scalar_i += 1) {
|
||||
const item = @intToEnum(Ref, zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body_len = zir.extra[extra_index];
|
||||
const body_len = @truncate(u31, zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body = zir.extra[extra_index..][0..body_len];
|
||||
extra_index += body.len;
|
||||
@ -3032,7 +3035,7 @@ pub const Inst = struct {
|
||||
var extra_index: usize = extra_end + 1;
|
||||
|
||||
if (self.bits.specialProng() != .none) {
|
||||
const body_len = zir.extra[extra_index];
|
||||
const body_len = @truncate(u31, zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body = zir.extra[extra_index..][0..body_len];
|
||||
extra_index += body.len;
|
||||
@ -3041,7 +3044,7 @@ pub const Inst = struct {
|
||||
var scalar_i: usize = 0;
|
||||
while (scalar_i < self.bits.scalar_cases_len) : (scalar_i += 1) {
|
||||
extra_index += 1;
|
||||
const body_len = zir.extra[extra_index];
|
||||
const body_len = @truncate(u31, zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
extra_index += body_len;
|
||||
}
|
||||
@ -3049,7 +3052,7 @@ pub const Inst = struct {
|
||||
while (true) : (multi_i += 1) {
|
||||
const items_len = zir.extra[extra_index];
|
||||
extra_index += 2;
|
||||
const body_len = zir.extra[extra_index];
|
||||
const body_len = @truncate(u31, zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const items = zir.refSlice(extra_index, items_len);
|
||||
extra_index += items_len;
|
||||
@ -3861,7 +3864,7 @@ fn findDeclsSwitch(
|
||||
|
||||
const special_prong = extra.data.bits.specialProng();
|
||||
if (special_prong != .none) {
|
||||
const body_len = zir.extra[extra_index];
|
||||
const body_len = @truncate(u31, zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body = zir.extra[extra_index..][0..body_len];
|
||||
extra_index += body.len;
|
||||
@ -3874,7 +3877,7 @@ fn findDeclsSwitch(
|
||||
var scalar_i: usize = 0;
|
||||
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||
extra_index += 1;
|
||||
const body_len = zir.extra[extra_index];
|
||||
const body_len = @truncate(u31, zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body = zir.extra[extra_index..][0..body_len];
|
||||
extra_index += body_len;
|
||||
@ -3889,7 +3892,7 @@ fn findDeclsSwitch(
|
||||
extra_index += 1;
|
||||
const ranges_len = zir.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const body_len = zir.extra[extra_index];
|
||||
const body_len = @truncate(u31, zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const items = zir.refSlice(extra_index, items_len);
|
||||
extra_index += items_len;
|
||||
|
||||
@ -2159,7 +2159,7 @@ const RegisterOrMemory = union(enum) {
|
||||
/// Returns size in bits.
|
||||
fn size(reg_or_mem: RegisterOrMemory) u64 {
|
||||
return switch (reg_or_mem) {
|
||||
.register => |reg| reg.size(),
|
||||
.register => |register| register.size(),
|
||||
.memory => |memory| memory.size(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -237,6 +237,7 @@ const Writer = struct {
|
||||
.ret_tok,
|
||||
.ensure_err_payload_void,
|
||||
.closure_capture,
|
||||
.switch_capture_tag,
|
||||
=> try self.writeUnTok(stream, inst),
|
||||
|
||||
.bool_br_and,
|
||||
@ -1857,7 +1858,6 @@ const Writer = struct {
|
||||
} else 0;
|
||||
|
||||
try self.writeInstRef(stream, extra.data.operand);
|
||||
try self.writeFlag(stream, ", ref", extra.data.bits.is_ref);
|
||||
|
||||
self.indent += 2;
|
||||
|
||||
@ -1869,14 +1869,15 @@ const Writer = struct {
|
||||
else => break :else_prong,
|
||||
};
|
||||
|
||||
const body_len = self.code.extra[extra_index];
|
||||
const body_len = @truncate(u31, self.code.extra[extra_index]);
|
||||
const inline_text = if (self.code.extra[extra_index] >> 31 != 0) "inline " else "";
|
||||
extra_index += 1;
|
||||
const body = self.code.extra[extra_index..][0..body_len];
|
||||
extra_index += body.len;
|
||||
|
||||
try stream.writeAll(",\n");
|
||||
try stream.writeByteNTimes(' ', self.indent);
|
||||
try stream.print("{s} => ", .{prong_name});
|
||||
try stream.print("{s}{s} => ", .{ inline_text, prong_name });
|
||||
try self.writeBracedBody(stream, body);
|
||||
}
|
||||
|
||||
@ -1886,13 +1887,15 @@ const Writer = struct {
|
||||
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||
const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body_len = self.code.extra[extra_index];
|
||||
const body_len = @truncate(u31, self.code.extra[extra_index]);
|
||||
const is_inline = self.code.extra[extra_index] >> 31 != 0;
|
||||
extra_index += 1;
|
||||
const body = self.code.extra[extra_index..][0..body_len];
|
||||
extra_index += body_len;
|
||||
|
||||
try stream.writeAll(",\n");
|
||||
try stream.writeByteNTimes(' ', self.indent);
|
||||
if (is_inline) try stream.writeAll("inline ");
|
||||
try self.writeInstRef(stream, item_ref);
|
||||
try stream.writeAll(" => ");
|
||||
try self.writeBracedBody(stream, body);
|
||||
@ -1905,13 +1908,15 @@ const Writer = struct {
|
||||
extra_index += 1;
|
||||
const ranges_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const body_len = self.code.extra[extra_index];
|
||||
const body_len = @truncate(u31, self.code.extra[extra_index]);
|
||||
const is_inline = self.code.extra[extra_index] >> 31 != 0;
|
||||
extra_index += 1;
|
||||
const items = self.code.refSlice(extra_index, items_len);
|
||||
extra_index += items_len;
|
||||
|
||||
try stream.writeAll(",\n");
|
||||
try stream.writeByteNTimes(' ', self.indent);
|
||||
if (is_inline) try stream.writeAll("inline ");
|
||||
|
||||
for (items) |item_ref, item_i| {
|
||||
if (item_i != 0) try stream.writeAll(", ");
|
||||
|
||||
@ -1039,6 +1039,7 @@ struct AstNodeSwitchProng {
|
||||
AstNode *expr;
|
||||
bool var_is_ptr;
|
||||
bool any_items_are_range;
|
||||
bool is_inline;
|
||||
};
|
||||
|
||||
struct AstNodeSwitchRange {
|
||||
|
||||
@ -6987,6 +6987,12 @@ static bool astgen_switch_prong_expr(Stage1AstGen *ag, Scope *scope, AstNode *sw
|
||||
assert(switch_node->type == NodeTypeSwitchExpr);
|
||||
assert(prong_node->type == NodeTypeSwitchProng);
|
||||
|
||||
if (prong_node->data.switch_prong.is_inline) {
|
||||
exec_add_error_node(ag->codegen, ag->exec, prong_node,
|
||||
buf_sprintf("inline switch cases not supported by stage1"));
|
||||
return ag->codegen->invalid_inst_src;
|
||||
}
|
||||
|
||||
AstNode *expr_node = prong_node->data.switch_prong.expr;
|
||||
AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol;
|
||||
Scope *child_scope;
|
||||
|
||||
@ -2306,17 +2306,17 @@ static Optional<PtrIndexPayload> ast_parse_ptr_index_payload(ParseContext *pc) {
|
||||
return Optional<PtrIndexPayload>::some(res);
|
||||
}
|
||||
|
||||
// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr
|
||||
// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr
|
||||
static AstNode *ast_parse_switch_prong(ParseContext *pc) {
|
||||
AstNode *res = ast_parse_switch_case(pc);
|
||||
if (res == nullptr)
|
||||
return nullptr;
|
||||
|
||||
expect_token(pc, TokenIdFatArrow);
|
||||
Optional<PtrPayload> opt_payload = ast_parse_ptr_payload(pc);
|
||||
Optional<PtrIndexPayload> opt_payload = ast_parse_ptr_index_payload(pc);
|
||||
AstNode *expr = ast_expect(pc, ast_parse_assign_expr);
|
||||
|
||||
PtrPayload payload;
|
||||
PtrIndexPayload payload;
|
||||
assert(res->type == NodeTypeSwitchProng);
|
||||
res->data.switch_prong.expr = expr;
|
||||
if (opt_payload.unwrap(&payload)) {
|
||||
@ -2331,9 +2331,11 @@ static AstNode *ast_parse_switch_prong(ParseContext *pc) {
|
||||
// <- SwitchItem (COMMA SwitchItem)* COMMA?
|
||||
// / KEYWORD_else
|
||||
static AstNode *ast_parse_switch_case(ParseContext *pc) {
|
||||
bool is_inline = eat_token_if(pc, TokenIdKeywordInline) != 0;
|
||||
AstNode *first = ast_parse_switch_item(pc);
|
||||
if (first != nullptr) {
|
||||
AstNode *res = ast_create_node_copy_line_info(pc, NodeTypeSwitchProng, first);
|
||||
res->data.switch_prong.is_inline = is_inline;
|
||||
res->data.switch_prong.items.append(first);
|
||||
res->data.switch_prong.any_items_are_range = first->type == NodeTypeSwitchRange;
|
||||
|
||||
@ -2350,9 +2352,13 @@ static AstNode *ast_parse_switch_case(ParseContext *pc) {
|
||||
}
|
||||
|
||||
TokenIndex else_token = eat_token_if(pc, TokenIdKeywordElse);
|
||||
if (else_token != 0)
|
||||
return ast_create_node(pc, NodeTypeSwitchProng, else_token);
|
||||
if (else_token != 0) {
|
||||
AstNode *res = ast_create_node(pc, NodeTypeSwitchProng, else_token);
|
||||
res->data.switch_prong.is_inline = is_inline;
|
||||
return res;
|
||||
}
|
||||
|
||||
if (is_inline) pc->current_token -= 1;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@ -182,6 +182,7 @@ test {
|
||||
_ = @import("behavior/decltest.zig");
|
||||
_ = @import("behavior/packed_struct_explicit_backing_int.zig");
|
||||
_ = @import("behavior/empty_union.zig");
|
||||
_ = @import("behavior/inline_switch.zig");
|
||||
}
|
||||
|
||||
if (builtin.os.tag != .wasi) {
|
||||
|
||||
131
test/behavior/inline_switch.zig
Normal file
131
test/behavior/inline_switch.zig
Normal file
@ -0,0 +1,131 @@
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
const builtin = @import("builtin");
|
||||
|
||||
test "inline scalar prongs" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
|
||||
var x: usize = 0;
|
||||
switch (x) {
|
||||
10 => |*item| try expect(@TypeOf(item) == *usize),
|
||||
inline 11 => |*item| {
|
||||
try expect(@TypeOf(item) == *const usize);
|
||||
try expect(item.* == 11);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
test "inline prong ranges" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
|
||||
var x: usize = 0;
|
||||
switch (x) {
|
||||
inline 0...20, 24 => |item| {
|
||||
if (item > 25) @compileError("bad");
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
const E = enum { a, b, c, d };
|
||||
test "inline switch enums" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
|
||||
var x: E = .a;
|
||||
switch (x) {
|
||||
inline .a, .b => |aorb| if (aorb != .a and aorb != .b) @compileError("bad"),
|
||||
inline .c, .d => |cord| if (cord != .c and cord != .d) @compileError("bad"),
|
||||
}
|
||||
}
|
||||
|
||||
const U = union(E) { a: void, b: u2, c: u3, d: u4 };
|
||||
test "inline switch unions" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
var x: U = .a;
|
||||
switch (x) {
|
||||
inline .a, .b => |aorb, tag| {
|
||||
if (tag == .a) {
|
||||
try expect(@TypeOf(aorb) == void);
|
||||
} else {
|
||||
try expect(tag == .b);
|
||||
try expect(@TypeOf(aorb) == u2);
|
||||
}
|
||||
},
|
||||
inline .c, .d => |cord, tag| {
|
||||
if (tag == .c) {
|
||||
try expect(@TypeOf(cord) == u3);
|
||||
} else {
|
||||
try expect(tag == .d);
|
||||
try expect(@TypeOf(cord) == u4);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
test "inline else bool" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
|
||||
var a = true;
|
||||
switch (a) {
|
||||
true => {},
|
||||
inline else => |val| if (val != false) @compileError("bad"),
|
||||
}
|
||||
}
|
||||
|
||||
test "inline else error" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
|
||||
const Err = error{ a, b, c };
|
||||
var a = Err.a;
|
||||
switch (a) {
|
||||
error.a => {},
|
||||
inline else => |val| comptime if (val == error.a) @compileError("bad"),
|
||||
}
|
||||
}
|
||||
|
||||
test "inline else enum" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
const E2 = enum(u8) { a = 2, b = 3, c = 4, d = 5 };
|
||||
var a: E2 = .a;
|
||||
switch (a) {
|
||||
.a, .b => {},
|
||||
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");
|
||||
},
|
||||
}
|
||||
}
|
||||
15
test/cases/compile_errors/inline_underscore_prong.zig
Normal file
15
test/cases/compile_errors/inline_underscore_prong.zig
Normal file
@ -0,0 +1,15 @@
|
||||
const E = enum(u8) { a, b, c, d, _ };
|
||||
pub export fn entry() void {
|
||||
var x: E = .a;
|
||||
switch (x) {
|
||||
inline .a, .b => |aorb| @compileLog(aorb),
|
||||
.c, .d => |cord| @compileLog(cord),
|
||||
inline _ => {},
|
||||
}
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :7:16: error: cannot inline '_' prong
|
||||
27
test/cases/compile_errors/invalid_inline_else_type.zig
Normal file
27
test/cases/compile_errors/invalid_inline_else_type.zig
Normal file
@ -0,0 +1,27 @@
|
||||
pub export fn entry1() void {
|
||||
var a: anyerror = undefined;
|
||||
switch (a) {
|
||||
inline else => {},
|
||||
}
|
||||
}
|
||||
const E = enum(u8) { a, _ };
|
||||
pub export fn entry2() void {
|
||||
var a: E = undefined;
|
||||
switch (a) {
|
||||
inline else => {},
|
||||
}
|
||||
}
|
||||
pub export fn entry3() void {
|
||||
var a: *u32 = undefined;
|
||||
switch (a) {
|
||||
inline else => {},
|
||||
}
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:21: error: cannot enumerate values of type 'anyerror' for 'inline else'
|
||||
// :11:21: error: cannot enumerate values of type 'tmp.E' for 'inline else'
|
||||
// :17:21: error: cannot enumerate values of type '*u32' for 'inline else'
|
||||
15
test/cases/compile_errors/invalid_tag_capture.zig
Normal file
15
test/cases/compile_errors/invalid_tag_capture.zig
Normal file
@ -0,0 +1,15 @@
|
||||
const E = enum { a, b, c, d };
|
||||
pub export fn entry() void {
|
||||
var x: E = .a;
|
||||
switch (x) {
|
||||
inline .a, .b => |aorb, d| @compileLog(aorb, d),
|
||||
inline .c, .d => |*cord| @compileLog(cord),
|
||||
}
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :5:33: error: cannot capture tag of non-union type 'tmp.E'
|
||||
// :1:11: note: enum declared here
|
||||
@ -0,0 +1,14 @@
|
||||
const E = enum { a, b, c, d };
|
||||
pub export fn entry() void {
|
||||
var x: E = .a;
|
||||
switch (x) {
|
||||
.a, .b => |aorb, d| @compileLog(aorb, d),
|
||||
inline .c, .d => |*cord| @compileLog(cord),
|
||||
}
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :5:26: error: tag capture on non-inline prong
|
||||
Loading…
x
Reference in New Issue
Block a user