authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-11 19:44:24+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-11 19:44:24+01:00
log76dd39d5316d302de6aa9bcab45ccb3f5a123bcb
tree19eb970a346b362eac55f7e73eb7ee3bfbf90cd7
parentc4345991340af5ff2e0155a9832f4eed9ec677fc
parent01546e68cd0d82ef78498a10649e6bc2937680da

Merge pull request 'frontend: rewrite `switch` logic' (#30776) from justusk/zig:have-you-tried-switching-it-off-and-on-again into master

Resolves: https://codeberg.org/ziglang/zig/issues/30660 Resolves: https://codeberg.org/ziglang/zig/issues/30606 Resolves: https://codeberg.org/ziglang/zig/issues/30157 Resolves: https://codeberg.org/ziglang/zig/issues/30154 Resolves: https://codeberg.org/ziglang/zig/issues/30153 Resolves: https://github.com/ziglang/zig/issues/25644 Resolves: https://github.com/ziglang/zig/issues/25632 Resolves: https://github.com/ziglang/zig/issues/24789 Resolves: https://github.com/ziglang/zig/issues/24152 Resolves: https://github.com/ziglang/zig/issues/24128 Resolves: https://github.com/ziglang/zig/issues/24126 Resolves: https://github.com/ziglang/zig/issues/23973 Resolves: https://github.com/ziglang/zig/issues/23156 Resolves: https://github.com/ziglang/zig/issues/23123 Resolves: https://github.com/ziglang/zig/issues/22138 Resolves: https://github.com/ziglang/zig/issues/21772 Resolves: https://github.com/ziglang/zig/issues/18087 Resolves: https://github.com/ziglang/zig/issues/15237 Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30776 Reviewed-by: Matthew Lugg <mlugg@mlugg.co.uk>

30 files changed, 5197 insertions(+), 5010 deletions(-)

doc/langref.html.in+13-4
......@@ -2461,7 +2461,8 @@ or
24612461 {#header_open|Tagged union#}
24622462 <p>Unions can be declared with an enum tag type.
24632463 This turns the union into a <em>tagged</em> union, which makes it eligible
2464 to use with {#link|switch#} expressions.
2464 to use with {#link|switch#} expressions. When switching on tagged unions,
2465 the tag value can be obtained using an additional capture.
24652466 Tagged unions coerce to their tag type: {#link|Type Coercion: Unions and Enums#}.
24662467 </p>
24672468 {#code|test_tagged_union.zig#}
......@@ -2594,6 +2595,13 @@ or
25942595
25952596 {#header_close#}
25962597
2598 {#header_open|Switching on Errors#}
2599 <p>
2600 When switching on errors, some special cases are allowed to simplify generic programming patterns:
2601 </p>
2602 {#code|test_switch_on_errors.zig#}
2603 {#header_close#}
2604
25972605 {#header_open|Labeled switch#}
25982606 <p>
25992607 When a switch statement is labeled, it can be referenced from a
......@@ -2659,12 +2667,13 @@ or
26592667 {#code|test_inline_else.zig#}
26602668
26612669 <p>
2662 When using an inline prong switching on an union an additional
2663 capture can be used to obtain the union's enum tag value.
2670 When using an inline prong switching on an union an additional capture
2671 can be used to obtain the union's enum tag value at comptime, even though
2672 its payload might only be known at runtime.
26642673 </p>
26652674 {#code|test_inline_switch_union_tag.zig#}
26662675
2667 {#see_also|inline while|inline for#}
2676 {#see_also|inline while|inline for|Tagged union#}
26682677 {#header_close#}
26692678 {#header_close#}
26702679
doc/langref/test_switch_on_errors.zig created+55
......@@ -0,0 +1,55 @@
1const FileOpenError0 = error{
2 AccessDenied,
3 OutOfMemory,
4 FileNotFound,
5};
6
7fn openFile0() FileOpenError0 {
8 return error.OutOfMemory;
9}
10
11test "unreachable else prong" {
12 switch (openFile0()) {
13 error.AccessDenied, error.FileNotFound => |e| return e,
14 error.OutOfMemory => {},
15 // 'openFile0' cannot return any more errors, so an 'else' prong would be
16 // statically known to be unreachable. Nonetheless, in this case, adding
17 // one does not raise an "unreachable else prong" compile error:
18 else => unreachable,
19 }
20
21 // Allowed unreachable else prongs are:
22 // `else => unreachable,`
23 // `else => return,`
24 // `else => |e| return e,` (where `e` is any identifier)
25}
26
27const FileOpenError1 = error{
28 AccessDenied,
29 SystemResources,
30 FileNotFound,
31};
32
33fn openFile1() FileOpenError1 {
34 return error.SystemResources;
35}
36
37fn openFileGeneric(comptime kind: u1) switch (kind) {
38 0 => FileOpenError0,
39 1 => FileOpenError1,
40} {
41 return switch (kind) {
42 0 => openFile0(),
43 1 => openFile1(),
44 };
45}
46
47test "comptime unreachable errors not in error set" {
48 switch (openFileGeneric(1)) {
49 error.AccessDenied, error.FileNotFound => |e| return e,
50 error.OutOfMemory => comptime unreachable, // not in `FileOpenError1`!
51 error.SystemResources => {},
52 }
53}
54
55// test
doc/langref/test_tagged_union.zig+8
......@@ -18,6 +18,14 @@ test "switch on tagged union" {
1818 .ok => |value| try expect(value == 42),
1919 .not_ok => unreachable,
2020 }
21
22 switch (c) {
23 .ok => |_, tag| {
24 // Because we're in the '.ok' prong, 'tag' is compile-time known to be '.ok':
25 comptime std.debug.assert(tag == .ok);
26 },
27 .not_ok => unreachable,
28 }
2129}
2230
2331test "get tag type" {
lib/std/tar/Writer.zig+2-2
......@@ -114,7 +114,7 @@ fn writeHeader(
114114 if (typeflag == .symbolic_link)
115115 header.setLinkname(link_name) catch |err| switch (err) {
116116 error.NameTooLong => try w.writeExtendedHeader(.gnu_long_link, &.{link_name}),
117 else => return err,
117 else => |e| return e,
118118 };
119119 try header.write(w.underlying_writer);
120120}
......@@ -131,7 +131,7 @@ fn setPath(w: *Writer, header: *Header, sub_path: []const u8) Error!void {
131131 &.{ w.prefix, "/", sub_path };
132132 try w.writeExtendedHeader(.gnu_long_name, buffers);
133133 },
134 else => return err,
134 else => |e| return e,
135135 };
136136}
137137
lib/std/testing.zig+2-2
......@@ -813,7 +813,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe
813813 }
814814 },
815815
816 .array => |_| {
816 .array => {
817817 if (expected.len != actual.len) {
818818 print("Array len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len });
819819 return error.TestExpectedEqual;
......@@ -1187,7 +1187,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime
11871187 return error.MemoryLeakDetected;
11881188 }
11891189 },
1190 else => return err,
1190 else => |e| return e,
11911191 }
11921192 }
11931193}
lib/std/zig/AstGen.zig+1105-1124
......@@ -115,7 +115,6 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {
115115 Zir.Inst.Call.Flags,
116116 Zir.Inst.BuiltinCall.Flags,
117117 Zir.Inst.SwitchBlock.Bits,
118 Zir.Inst.SwitchBlockErrUnion.Bits,
119118 Zir.Inst.FuncFancy.Bits,
120119 Zir.Inst.Param.Type,
121120 Zir.Inst.Func.RetTy,
......@@ -858,11 +857,11 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
858857 no_switch_on_err: {
859858 const error_token = if_full.error_token orelse break :no_switch_on_err;
860859 const else_node = if_full.ast.else_expr.unwrap() orelse break :no_switch_on_err;
861 const full_switch = tree.fullSwitch(else_node) orelse break :no_switch_on_err;
862 if (full_switch.label_token != null) break :no_switch_on_err;
863 if (tree.nodeTag(full_switch.ast.condition) != .identifier) break :no_switch_on_err;
864 if (!mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(tree.nodeMainToken(full_switch.ast.condition)))) break :no_switch_on_err;
865 return switchExprErrUnion(gz, scope, ri.br(), node, .@"if");
860 const switch_full = tree.fullSwitch(else_node) orelse break :no_switch_on_err;
861 if (switch_full.label_token != null) break :no_switch_on_err; // handled in `ifExpr`
862 if (tree.nodeTag(switch_full.ast.condition) != .identifier) break :no_switch_on_err;
863 if (!try astgen.tokenIdentEql(error_token, tree.nodeMainToken(switch_full.ast.condition))) break :no_switch_on_err;
864 return switchExpr(gz, scope, ri.br(), node, switch_full, .{ .@"if" = if_full });
866865 }
867866 return ifExpr(gz, scope, ri.br(), node, if_full);
868867 },
......@@ -1024,11 +1023,11 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
10241023 null;
10251024 no_switch_on_err: {
10261025 const capture_token = payload_token orelse break :no_switch_on_err;
1027 const full_switch = tree.fullSwitch(tree.nodeData(node).node_and_node[1]) orelse break :no_switch_on_err;
1028 if (full_switch.label_token != null) break :no_switch_on_err;
1029 if (tree.nodeTag(full_switch.ast.condition) != .identifier) break :no_switch_on_err;
1030 if (!mem.eql(u8, tree.tokenSlice(capture_token), tree.tokenSlice(tree.nodeMainToken(full_switch.ast.condition)))) break :no_switch_on_err;
1031 return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch");
1026 const switch_full = tree.fullSwitch(tree.nodeData(node).node_and_node[1]) orelse break :no_switch_on_err;
1027 if (switch_full.label_token != null) break :no_switch_on_err; // handled in `orelseCatchExpr`
1028 if (tree.nodeTag(switch_full.ast.condition) != .identifier) break :no_switch_on_err;
1029 if (!try astgen.tokenIdentEql(capture_token, tree.nodeMainToken(switch_full.ast.condition))) break :no_switch_on_err;
1030 return switchExpr(gz, scope, ri.br(), node, switch_full, .@"catch");
10321031 }
10331032 switch (ri.rl) {
10341033 .ref, .ref_coerced_ty => return orelseCatchExpr(
......@@ -1108,7 +1107,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
11081107 .error_set_decl => return errorSetDecl(gz, ri, node),
11091108 .array_access => return arrayAccess(gz, scope, ri, node),
11101109 .@"comptime" => return comptimeExprAst(gz, scope, ri, node),
1111 .@"switch", .switch_comma => return switchExpr(gz, scope, ri.br(), node, tree.fullSwitch(node).?),
1110 .@"switch", .switch_comma => return switchExpr(gz, scope, ri.br(), node, tree.fullSwitch(node).?, .none),
11121111
11131112 .@"nosuspend" => return nosuspendExpr(gz, scope, ri, node),
11141113 .@"suspend" => return suspendExpr(gz, scope, node),
......@@ -2161,93 +2160,90 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
21612160 const opt_break_label, const opt_rhs = tree.nodeData(node).opt_token_and_opt_node;
21622161
21632162 // Look for the label in the scope.
2164 var scope = parent_scope;
2165 while (true) {
2166 switch (scope.tag) {
2167 .gen_zir => {
2168 const block_gz = scope.cast(GenZir).?;
2163 find_scope: switch (parent_scope.unwrap()) {
2164 .gen_zir => |gen_zir| {
2165 const scope = &gen_zir.base;
21692166
2170 if (block_gz.cur_defer_node.unwrap()) |cur_defer_node| {
2171 // We are breaking out of a `defer` block.
2172 return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
2173 try astgen.errNoteNode(
2174 cur_defer_node,
2175 "defer expression here",
2176 .{},
2177 ),
2178 });
2179 }
2167 if (gen_zir.cur_defer_node.unwrap()) |cur_defer_node| {
2168 // We are breaking out of a `defer` block.
2169 return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
2170 try astgen.errNoteNode(
2171 cur_defer_node,
2172 "defer expression here",
2173 .{},
2174 ),
2175 });
2176 }
21802177
2181 const block_inst = blk: {
2182 if (opt_break_label.unwrap()) |break_label| {
2183 if (block_gz.label) |*label| {
2184 if (try astgen.tokenIdentEql(label.token, break_label)) {
2185 label.used = true;
2186 break :blk label.block_inst;
2187 }
2188 }
2189 } else if (block_gz.break_block.unwrap()) |i| {
2190 break :blk i;
2178 if (opt_break_label.unwrap()) |break_label| labeled: {
2179 if (gen_zir.label) |*label| {
2180 if (try astgen.tokenIdentEql(label.token, break_label)) {
2181 label.used = true;
2182 break :labeled;
21912183 }
2192 // If not the target, start over with the parent
2193 scope = block_gz.parent;
2194 continue;
2195 };
2196 // If we made it here, this block is the target of the break expr
2197
2198 const break_tag: Zir.Inst.Tag = if (block_gz.is_inline)
2199 .break_inline
2200 else
2201 .@"break";
2202
2203 const rhs = opt_rhs.unwrap() orelse {
2204 _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node);
2205
2206 try genDefers(parent_gz, scope, parent_scope, .normal_only);
2207
2208 // As our last action before the break, "pop" the error trace if needed
2209 if (!block_gz.is_comptime)
2210 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, node);
2184 }
2185 // gz without or with different label, continue to parent scopes.
2186 continue :find_scope gen_zir.parent.unwrap();
2187 } else if (!gen_zir.allow_unlabeled_control_flow) {
2188 // This `break` is unlabeled and the gz we've found doesn't allow
2189 // unlabeled control flow. Continue to parent scopes.
2190 continue :find_scope gen_zir.parent.unwrap();
2191 }
22112192
2212 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
2213 return Zir.Inst.Ref.unreachable_value;
2214 };
2193 const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline)
2194 .break_inline
2195 else
2196 .@"break";
22152197
2216 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node);
2198 if (opt_rhs.unwrap()) |rhs| {
2199 // We have a `break` operand.
2200 const operand = try reachableExpr(parent_gz, parent_scope, gen_zir.break_result_info, rhs, node);
22172201
22182202 try genDefers(parent_gz, scope, parent_scope, .normal_only);
22192203
22202204 // As our last action before the break, "pop" the error trace if needed
2221 if (!block_gz.is_comptime)
2222 try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand);
2223
2224 switch (block_gz.break_result_info.rl) {
2205 if (!gen_zir.is_comptime) {
2206 try restoreErrRetIndex(parent_gz, .{ .block = gen_zir.break_target }, gen_zir.break_result_info, rhs, operand);
2207 }
2208 switch (gen_zir.break_result_info.rl) {
22252209 .ptr => {
22262210 // In this case we don't have any mechanism to intercept it;
22272211 // we assume the result location is written, and we break with void.
2228 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
2212 _ = try parent_gz.addBreak(break_tag, gen_zir.break_target, .void_value);
22292213 },
22302214 .discard => {
2231 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
2215 _ = try parent_gz.addBreak(break_tag, gen_zir.break_target, .void_value);
22322216 },
22332217 else => {
2234 _ = try parent_gz.addBreakWithSrcNode(break_tag, block_inst, operand, rhs);
2218 _ = try parent_gz.addBreakWithSrcNode(break_tag, gen_zir.break_target, operand, rhs);
22352219 },
22362220 }
2237 return Zir.Inst.Ref.unreachable_value;
2238 },
2239 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2240 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2241 .namespace => break,
2242 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2243 .top => unreachable,
2244 }
2245 }
2246 if (opt_break_label.unwrap()) |break_label| {
2247 const label_name = try astgen.identifierTokenString(break_label);
2248 return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
2249 } else {
2250 return astgen.failNode(node, "break expression outside loop", .{});
2221 return .unreachable_value;
2222 } else {
2223 _ = try rvalue(parent_gz, gen_zir.break_result_info, .void_value, node);
2224
2225 try genDefers(parent_gz, scope, parent_scope, .normal_only);
2226
2227 // As our last action before the break, "pop" the error trace if needed
2228 if (!gen_zir.is_comptime)
2229 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = gen_zir.break_target }, .always, node);
2230
2231 _ = try parent_gz.addBreak(break_tag, gen_zir.break_target, .void_value);
2232 return .unreachable_value;
2233 }
2234 },
2235 .local_val => |local_val| continue :find_scope local_val.parent.unwrap(),
2236 .local_ptr => |local_ptr| continue :find_scope local_ptr.parent.unwrap(),
2237 .defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
2238 .namespace => {
2239 if (opt_break_label.unwrap()) |break_label| {
2240 const label_name = try astgen.identifierTokenString(break_label);
2241 return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
2242 } else {
2243 return astgen.failNode(node, "break expression outside loop", .{});
2244 }
2245 },
2246 .top => unreachable,
22512247 }
22522248}
22532249
......@@ -2261,101 +2257,104 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
22612257 }
22622258
22632259 // Look for the label in the scope.
2264 var scope = parent_scope;
2265 while (true) {
2266 switch (scope.tag) {
2267 .gen_zir => {
2268 const gen_zir = scope.cast(GenZir).?;
2260 find_scope: switch (parent_scope.unwrap()) {
2261 .gen_zir => |gen_zir| {
2262 const scope = &gen_zir.base;
22692263
2270 if (gen_zir.cur_defer_node.unwrap()) |cur_defer_node| {
2271 return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{
2272 try astgen.errNoteNode(
2273 cur_defer_node,
2274 "defer expression here",
2275 .{},
2276 ),
2277 });
2278 }
2279 const continue_block = gen_zir.continue_block.unwrap() orelse {
2280 scope = gen_zir.parent;
2281 continue;
2282 };
2283 if (opt_break_label.unwrap()) |break_label| blk: {
2284 if (gen_zir.label) |*label| {
2285 if (try astgen.tokenIdentEql(label.token, break_label)) {
2286 const maybe_switch_tag = astgen.instructions.items(.tag)[@intFromEnum(label.block_inst)];
2287 if (opt_rhs != .none) switch (maybe_switch_tag) {
2288 .switch_block, .switch_block_ref => {},
2289 else => return astgen.failNode(node, "cannot continue loop with operand", .{}),
2290 } else switch (maybe_switch_tag) {
2291 .switch_block, .switch_block_ref => return astgen.failNode(node, "cannot continue switch without operand", .{}),
2292 else => {},
2293 }
2294
2295 label.used = true;
2296 label.used_for_continue = true;
2297 break :blk;
2264 if (gen_zir.cur_defer_node.unwrap()) |cur_defer_node| {
2265 return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{
2266 try astgen.errNoteNode(
2267 cur_defer_node,
2268 "defer expression here",
2269 .{},
2270 ),
2271 });
2272 }
2273
2274 if (opt_break_label.unwrap()) |break_label| labeled: {
2275 if (gen_zir.label) |*label| {
2276 if (try astgen.tokenIdentEql(label.token, break_label)) {
2277 switch (gen_zir.continue_target) {
2278 .none => {
2279 return astgen.failNode(node, "continue outside of loop or labeled switch expression", .{});
2280 },
2281 .@"break" => if (opt_rhs != .none) {
2282 return astgen.failNode(node, "cannot continue loop with operand", .{});
2283 },
2284 .switch_continue => if (opt_rhs == .none) {
2285 return astgen.failNode(node, "cannot continue switch without operand", .{});
2286 },
22982287 }
2299 }
2300 // found continue but either it has a different label, or no label
2301 scope = gen_zir.parent;
2302 continue;
2303 } else if (gen_zir.label) |label| {
2304 // This `continue` is unlabeled. If the gz we've found corresponds to a labeled
2305 // `switch`, ignore it and continue to parent scopes.
2306 switch (astgen.instructions.items(.tag)[@intFromEnum(label.block_inst)]) {
2307 .switch_block, .switch_block_ref => {
2308 scope = gen_zir.parent;
2309 continue;
2310 },
2311 else => {},
2288 label.used = true;
2289 label.used_for_continue = true;
2290 break :labeled;
23122291 }
23132292 }
2293 // gz without or with different label, continue to parent scopes.
2294 continue :find_scope gen_zir.parent.unwrap();
2295 } else if (gen_zir.allow_unlabeled_control_flow) {
2296 // This `continue` is unlabeled. If the gz we've found doesn't
2297 // provide a `continue` target or corresponds to a labeled
2298 // `switch`, ignore it and continue to parent scopes.
2299 switch (gen_zir.continue_target) {
2300 .none, .switch_continue => {
2301 continue :find_scope gen_zir.parent.unwrap();
2302 },
2303 .@"break" => {},
2304 }
2305 } else {
2306 // We don't have a break label and the gz we found doesn't allow
2307 // unlabeled control flow, so we continue to its parent scopes.
2308 continue :find_scope gen_zir.parent.unwrap();
2309 }
23142310
2315 if (opt_rhs.unwrap()) |rhs| {
2316 // We need to figure out the result info to use.
2317 // The type should match
2318 const operand = try reachableExpr(parent_gz, parent_scope, gen_zir.continue_result_info, rhs, node);
2319
2311 switch (gen_zir.continue_target) {
2312 .none => unreachable, // should have failed or continued to parent scopes by now
2313 .@"break" => |block| {
23202314 try genDefers(parent_gz, scope, parent_scope, .normal_only);
23212315
2322 // As our last action before the continue, "pop" the error trace if needed
2323 if (!gen_zir.is_comptime)
2324 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always, node);
2325
2326 _ = try parent_gz.addBreakWithSrcNode(.switch_continue, continue_block, operand, rhs);
2327 return Zir.Inst.Ref.unreachable_value;
2328 }
2329
2330 try genDefers(parent_gz, scope, parent_scope, .normal_only);
2316 const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline)
2317 .break_inline
2318 else
2319 .@"break";
2320 if (break_tag == .break_inline) {
2321 _ = try parent_gz.addUnNode(.check_comptime_control_flow, block.toRef(), node);
2322 }
23312323
2332 const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline)
2333 .break_inline
2334 else
2335 .@"break";
2336 if (break_tag == .break_inline) {
2337 _ = try parent_gz.addUnNode(.check_comptime_control_flow, continue_block.toRef(), node);
2338 }
2324 // As our last action before the continue, "pop" the error trace if needed
2325 if (!gen_zir.is_comptime) {
2326 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = block }, .always, node);
2327 }
2328 _ = try parent_gz.addBreak(break_tag, block, .void_value);
2329 return .unreachable_value;
2330 },
2331 .switch_continue => |switch_block| {
2332 const rhs = opt_rhs.unwrap().?; // checked above
2333 const operand = try reachableExpr(parent_gz, parent_scope, gen_zir.continue_result_info, rhs, node);
23392334
2340 // As our last action before the continue, "pop" the error trace if needed
2341 if (!gen_zir.is_comptime)
2342 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always, node);
2335 try genDefers(parent_gz, scope, parent_scope, .normal_only);
23432336
2344 _ = try parent_gz.addBreak(break_tag, continue_block, .void_value);
2345 return Zir.Inst.Ref.unreachable_value;
2346 },
2347 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2348 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2349 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2350 .namespace => break,
2351 .top => unreachable,
2352 }
2353 }
2354 if (opt_break_label.unwrap()) |break_label| {
2355 const label_name = try astgen.identifierTokenString(break_label);
2356 return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
2357 } else {
2358 return astgen.failNode(node, "continue expression outside loop", .{});
2337 // As our last action before the continue, "pop" the error trace if needed
2338 if (!gen_zir.is_comptime) {
2339 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = switch_block }, .always, node);
2340 }
2341 _ = try parent_gz.addBreakWithSrcNode(.switch_continue, switch_block, operand, rhs);
2342 return .unreachable_value;
2343 },
2344 }
2345 },
2346 .local_val => |local_val| continue :find_scope local_val.parent.unwrap(),
2347 .local_ptr => |local_ptr| continue :find_scope local_ptr.parent.unwrap(),
2348 .defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
2349 .namespace => {
2350 if (opt_break_label.unwrap()) |break_label| {
2351 const label_name = try astgen.identifierTokenString(break_label);
2352 return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
2353 } else {
2354 return astgen.failNode(node, "continue expression outside loop", .{});
2355 }
2356 },
2357 .top => unreachable,
23592358 }
23602359}
23612360
......@@ -2441,33 +2440,29 @@ fn blockExpr(
24412440
24422441fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.TokenIndex) !void {
24432442 // Look for the label in the scope.
2444 var scope = parent_scope;
2445 while (true) {
2446 switch (scope.tag) {
2447 .gen_zir => {
2448 const gen_zir = scope.cast(GenZir).?;
2449 if (gen_zir.label) |prev_label| {
2450 if (try astgen.tokenIdentEql(label, prev_label.token)) {
2451 const label_name = try astgen.identifierTokenString(label);
2452 return astgen.failTokNotes(label, "redefinition of label '{s}'", .{
2453 label_name,
2454 }, &[_]u32{
2455 try astgen.errNoteTok(
2456 prev_label.token,
2457 "previous definition here",
2458 .{},
2459 ),
2460 });
2461 }
2443 find_scope: switch (parent_scope.unwrap()) {
2444 .gen_zir => |gen_zir| {
2445 if (gen_zir.label) |prev_label| {
2446 if (try astgen.tokenIdentEql(label, prev_label.token)) {
2447 const label_name = try astgen.identifierTokenString(label);
2448 return astgen.failTokNotes(label, "redefinition of label '{s}'", .{
2449 label_name,
2450 }, &[_]u32{
2451 try astgen.errNoteTok(
2452 prev_label.token,
2453 "previous definition here",
2454 .{},
2455 ),
2456 });
24622457 }
2463 scope = gen_zir.parent;
2464 },
2465 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2466 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2467 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2468 .namespace => break,
2469 .top => unreachable,
2470 }
2458 }
2459 continue :find_scope gen_zir.parent.unwrap();
2460 },
2461 .local_val => |local_val| continue :find_scope local_val.parent.unwrap(),
2462 .local_ptr => |local_ptr| continue :find_scope local_ptr.parent.unwrap(),
2463 .defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
2464 .namespace => break :find_scope,
2465 .top => unreachable,
24712466 }
24722467}
24732468
......@@ -2509,10 +2504,9 @@ fn labeledBlockExpr(
25092504 try gz.instructions.append(astgen.gpa, block_inst);
25102505 var block_scope = gz.makeSubBlock(parent_scope);
25112506 block_scope.is_inline = force_comptime;
2512 block_scope.label = GenZir.Label{
2513 .token = label_token,
2514 .block_inst = block_inst,
2515 };
2507 block_scope.label = .{ .token = label_token };
2508 block_scope.break_target = block_inst;
2509 block_scope.continue_target = .none;
25162510 block_scope.setBreakResultInfo(block_ri);
25172511 if (force_comptime) block_scope.is_comptime = true;
25182512 defer block_scope.unstack();
......@@ -2983,18 +2977,16 @@ fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct {
29832977 var need_err_code = false;
29842978 var scope = inner_scope;
29852979 while (scope != outer_scope) {
2986 switch (scope.tag) {
2987 .gen_zir => scope = scope.cast(GenZir).?.parent,
2988 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2989 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2990 .defer_normal => {
2991 const defer_scope = scope.cast(Scope.Defer).?;
2980 switch (scope.unwrap()) {
2981 .gen_zir => |gen_zir| scope = gen_zir.parent,
2982 .local_val => |local_val| scope = local_val.parent,
2983 .local_ptr => |local_ptr| scope = local_ptr.parent,
2984 .defer_normal => |defer_scope| {
29922985 scope = defer_scope.parent;
29932986
29942987 have_normal = true;
29952988 },
2996 .defer_error => {
2997 const defer_scope = scope.cast(Scope.Defer).?;
2989 .defer_error => |defer_scope| {
29982990 scope = defer_scope.parent;
29992991
30002992 have_err = true;
......@@ -3030,17 +3022,15 @@ fn genDefers(
30303022
30313023 var scope = inner_scope;
30323024 while (scope != outer_scope) {
3033 switch (scope.tag) {
3034 .gen_zir => scope = scope.cast(GenZir).?.parent,
3035 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
3036 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
3037 .defer_normal => {
3038 const defer_scope = scope.cast(Scope.Defer).?;
3025 switch (scope.unwrap()) {
3026 .gen_zir => |gen_zir| scope = gen_zir.parent,
3027 .local_val => |local_val| scope = local_val.parent,
3028 .local_ptr => |local_ptr| scope = local_ptr.parent,
3029 .defer_normal => |defer_scope| {
30393030 scope = defer_scope.parent;
30403031 try gz.addDefer(defer_scope.index, defer_scope.len);
30413032 },
3042 .defer_error => {
3043 const defer_scope = scope.cast(Scope.Defer).?;
3033 .defer_error => |defer_scope| {
30443034 scope = defer_scope.parent;
30453035 switch (which_ones) {
30463036 .both_sans_err => {
......@@ -3083,10 +3073,9 @@ fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!v
30833073
30843074 var scope = inner_scope;
30853075 while (scope != outer_scope) {
3086 switch (scope.tag) {
3087 .gen_zir => scope = scope.cast(GenZir).?.parent,
3088 .local_val => {
3089 const s = scope.cast(Scope.LocalVal).?;
3076 switch (scope.unwrap()) {
3077 .gen_zir => |gen_zir| scope = gen_zir.parent,
3078 .local_val => |s| {
30903079 if (s.used == .none and s.discarded == .none) {
30913080 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
30923081 } else if (s.used != .none and s.discarded != .none) {
......@@ -3096,8 +3085,7 @@ fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!v
30963085 }
30973086 scope = s.parent;
30983087 },
3099 .local_ptr => {
3100 const s = scope.cast(Scope.LocalPtr).?;
3088 .local_ptr => |s| {
31013089 if (s.used == .none and s.discarded == .none) {
31023090 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
31033091 } else {
......@@ -3112,10 +3100,9 @@ fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!v
31123100 });
31133101 }
31143102 }
3115
31163103 scope = s.parent;
31173104 },
3118 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
3105 .defer_normal, .defer_error => |defer_scope| scope = defer_scope.parent,
31193106 .namespace => unreachable,
31203107 .top => unreachable,
31213108 }
......@@ -3146,14 +3133,7 @@ fn deferStmt(
31463133 }
31473134 const remapped_err_code: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
31483135 opt_remapped_err_code = remapped_err_code.toOptional();
3149 try gz.astgen.instructions.append(gz.astgen.gpa, .{
3150 .tag = .extended,
3151 .data = .{ .extended = .{
3152 .opcode = .value_placeholder,
3153 .small = undefined,
3154 .operand = undefined,
3155 } },
3156 });
3136 _ = try gz.astgen.appendPlaceholder();
31573137 const remapped_err_code_ref = remapped_err_code.toRef();
31583138 local_val_scope = .{
31593139 .parent = &defer_gen.base,
......@@ -4781,13 +4761,11 @@ fn testDecl(
47814761
47824762 // Local variables, including function parameters.
47834763 const name_str_index = try astgen.identAsString(test_name_token);
4784 var s = scope;
47854764 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
47864765 var num_namespaces_out: u32 = 0;
47874766 var capturing_namespace: ?*Scope.Namespace = null;
4788 while (true) switch (s.tag) {
4789 .local_val => {
4790 const local_val = s.cast(Scope.LocalVal).?;
4767 find_scope: switch (scope.unwrap()) {
4768 .local_val => |local_val| {
47914769 if (local_val.name == name_str_index) {
47924770 local_val.used = .fromToken(test_name_token);
47934771 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
......@@ -4798,10 +4776,9 @@ fn testDecl(
47984776 }),
47994777 });
48004778 }
4801 s = local_val.parent;
4779 continue :find_scope local_val.parent.unwrap();
48024780 },
4803 .local_ptr => {
4804 const local_ptr = s.cast(Scope.LocalPtr).?;
4781 .local_ptr => |local_ptr| {
48054782 if (local_ptr.name == name_str_index) {
48064783 local_ptr.used = .fromToken(test_name_token);
48074784 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
......@@ -4812,12 +4789,11 @@ fn testDecl(
48124789 }),
48134790 });
48144791 }
4815 s = local_ptr.parent;
4792 continue :find_scope local_ptr.parent.unwrap();
48164793 },
4817 .gen_zir => s = s.cast(GenZir).?.parent,
4818 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
4819 .namespace => {
4820 const ns = s.cast(Scope.Namespace).?;
4794 .gen_zir => |gen_zir| continue :find_scope gen_zir.parent.unwrap(),
4795 .defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
4796 .namespace => |ns| {
48214797 if (ns.decls.get(name_str_index)) |i| {
48224798 if (found_already) |f| {
48234799 return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{
......@@ -4830,10 +4806,10 @@ fn testDecl(
48304806 }
48314807 num_namespaces_out += 1;
48324808 capturing_namespace = ns;
4833 s = ns.parent;
4809 continue :find_scope ns.parent.unwrap();
48344810 },
4835 .top => break,
4836 };
4811 .top => break :find_scope,
4812 }
48374813 if (found_already == null) {
48384814 const ident_name = try astgen.identifierTokenString(test_name_token);
48394815 return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
......@@ -6131,7 +6107,30 @@ fn orelseCatchExpr(
61316107 break :blk &err_val_scope.base;
61326108 };
61336109
6134 const else_result = try fullBodyExpr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs, .allow_branch_hint);
6110 const else_result = else_result: {
6111 if (tree.fullSwitch(rhs)) |switch_full| no_switch_on_err: {
6112 if (tree.nodeTag(node) != .@"catch") break :no_switch_on_err;
6113 const catch_token = tree.nodeMainToken(node);
6114 const capture_token = if (tree.tokenTag(catch_token + 1) == .pipe) token: {
6115 break :token catch_token + 2;
6116 } else break :no_switch_on_err;
6117 if (switch_full.label_token == null) break :no_switch_on_err; // must use `switchExpr` with `non_err = .@"if"`
6118 if (tree.nodeTag(switch_full.ast.condition) != .identifier) break :no_switch_on_err;
6119 if (!try astgen.tokenIdentEql(capture_token, tree.nodeMainToken(switch_full.ast.condition))) break :no_switch_on_err;
6120 break :else_result try switchExpr(
6121 &else_scope,
6122 else_sub_scope,
6123 block_scope.break_result_info,
6124 rhs,
6125 switch_full,
6126 .{ .peer_break_target = .{
6127 .block_inst = block,
6128 .block_ri = block_ri,
6129 } },
6130 );
6131 }
6132 break :else_result try fullBodyExpr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs, .allow_branch_hint);
6133 };
61356134 if (!else_scope.endsWithNoReturn()) {
61366135 // As our last action before the break, "pop" the error trace if needed
61376136 if (do_err_trace)
......@@ -6484,7 +6483,26 @@ fn ifExpr(
64846483 break :s &else_scope.base;
64856484 }
64866485 };
6487 const else_result = try fullBodyExpr(&else_scope, sub_scope, block_scope.break_result_info, else_node, .allow_branch_hint);
6486 const else_result = else_result: {
6487 if (tree.fullSwitch(else_node)) |switch_full| no_switch_on_err: {
6488 const error_token = if_full.error_token orelse break :no_switch_on_err;
6489 if (switch_full.label_token == null) break :no_switch_on_err; // must use `switchExpr` with `non_err = .@"if"`
6490 if (tree.nodeTag(switch_full.ast.condition) != .identifier) break :no_switch_on_err;
6491 if (!try astgen.tokenIdentEql(error_token, tree.nodeMainToken(switch_full.ast.condition))) break :no_switch_on_err;
6492 break :else_result try switchExpr(
6493 &else_scope,
6494 sub_scope,
6495 block_scope.break_result_info,
6496 else_node,
6497 switch_full,
6498 .{ .peer_break_target = .{
6499 .block_inst = block,
6500 .block_ri = block_ri,
6501 } },
6502 );
6503 }
6504 break :else_result try fullBodyExpr(&else_scope, sub_scope, block_scope.break_result_info, else_node, .allow_branch_hint);
6505 };
64886506 if (!else_scope.endsWithNoReturn()) {
64896507 // As our last action before the break, "pop" the error trace if needed
64906508 if (do_err_trace)
......@@ -6574,7 +6592,6 @@ fn whileExpr(
65746592
65756593 var loop_scope = parent_gz.makeSubBlock(scope);
65766594 loop_scope.is_inline = is_inline;
6577 loop_scope.setBreakResultInfo(block_ri);
65786595 defer loop_scope.unstack();
65796596
65806597 var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
......@@ -6707,14 +6724,13 @@ fn whileExpr(
67076724 _ = try loop_scope.addNode(repeat_tag, node);
67086725
67096726 try loop_scope.setBlockBody(loop_block);
6710 loop_scope.break_block = loop_block.toOptional();
6711 loop_scope.continue_block = continue_block.toOptional();
67126727 if (while_full.label_token) |label_token| {
6713 loop_scope.label = .{
6714 .token = label_token,
6715 .block_inst = loop_block,
6716 };
6728 loop_scope.label = .{ .token = label_token };
67176729 }
6730 loop_scope.allow_unlabeled_control_flow = true;
6731 loop_scope.break_target = loop_block;
6732 loop_scope.continue_target = .{ .@"break" = continue_block };
6733 loop_scope.setBreakResultInfo(block_ri);
67186734
67196735 // done adding instructions to loop_scope, can now stack then_scope
67206736 then_scope.instructions_top = then_scope.instructions.items.len;
......@@ -6787,10 +6803,11 @@ fn whileExpr(
67876803 break :s &else_scope.base;
67886804 }
67896805 };
6790 // Remove the continue block and break block so that `continue` and `break`
6791 // control flow apply to outer loops; not this one.
6792 loop_scope.continue_block = .none;
6793 loop_scope.break_block = .none;
6806 // Disallow unlabeled control flow to this scope so that bare `continue`
6807 // and `break` control flow apply to outer loops; not this one.
6808 // Also disallow `continue` targeting the loop label.
6809 loop_scope.allow_unlabeled_control_flow = false;
6810 loop_scope.continue_target = .none;
67946811 const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint);
67956812 if (is_statement) {
67966813 _ = try addEnsureResult(&else_scope, else_result, else_node);
......@@ -6979,14 +6996,12 @@ fn forExpr(
69796996 const cond_block = try loop_scope.makeBlockInst(block_tag, node);
69806997 try cond_scope.setBlockBody(cond_block);
69816998
6982 loop_scope.break_block = loop_block.toOptional();
6983 loop_scope.continue_block = cond_block.toOptional();
69846999 if (for_full.label_token) |label_token| {
6985 loop_scope.label = .{
6986 .token = label_token,
6987 .block_inst = loop_block,
6988 };
7000 loop_scope.label = .{ .token = label_token };
69897001 }
7002 loop_scope.allow_unlabeled_control_flow = true;
7003 loop_scope.break_target = loop_block;
7004 loop_scope.continue_target = .{ .@"break" = cond_block };
69907005
69917006 const then_node = for_full.ast.then_expr;
69927007 var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
......@@ -7077,10 +7092,11 @@ fn forExpr(
70777092
70787093 if (for_full.ast.else_expr.unwrap()) |else_node| {
70797094 const sub_scope = &else_scope.base;
7080 // Remove the continue block and break block so that `continue` and `break`
7081 // control flow apply to outer loops; not this one.
7082 loop_scope.continue_block = .none;
7083 loop_scope.break_block = .none;
7095 // Disallow unlabeled control flow to this scope so that bare `continue`
7096 // and `break` control flow apply to outer loops; not this one.
7097 // Also disallow `continue` targeting the loop label.
7098 loop_scope.allow_unlabeled_control_flow = false;
7099 loop_scope.continue_target = .none;
70847100 const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint);
70857101 if (is_statement) {
70867102 _ = try addEnsureResult(&else_scope, else_result, else_node);
......@@ -7133,503 +7149,34 @@ fn forExpr(
71337149 return result;
71347150}
71357151
7136fn switchExprErrUnion(
7137 parent_gz: *GenZir,
7138 scope: *Scope,
7139 ri: ResultInfo,
7140 catch_or_if_node: Ast.Node.Index,
7141 node_ty: enum { @"catch", @"if" },
7142) InnerError!Zir.Inst.Ref {
7143 const astgen = parent_gz.astgen;
7144 const gpa = astgen.gpa;
7145 const tree = astgen.tree;
7146
7147 const if_full = switch (node_ty) {
7148 .@"catch" => undefined,
7149 .@"if" => tree.fullIf(catch_or_if_node).?,
7150 };
7151
7152 const switch_node, const operand_node, const error_payload = switch (node_ty) {
7153 .@"catch" => .{
7154 tree.nodeData(catch_or_if_node).node_and_node[1],
7155 tree.nodeData(catch_or_if_node).node_and_node[0],
7156 tree.nodeMainToken(catch_or_if_node) + 2,
7157 },
7158 .@"if" => .{
7159 if_full.ast.else_expr.unwrap().?,
7160 if_full.ast.cond_expr,
7161 if_full.error_token.?,
7162 },
7163 };
7164 const switch_full = tree.fullSwitch(switch_node).?;
7165
7166 const do_err_trace = astgen.fn_block != null;
7167 const need_rl = astgen.nodes_need_rl.contains(catch_or_if_node);
7168 const block_ri: ResultInfo = if (need_rl) ri else .{
7169 .rl = switch (ri.rl) {
7170 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, catch_or_if_node)).? },
7171 .inferred_ptr => .none,
7172 else => ri.rl,
7173 },
7174 .ctx = ri.ctx,
7175 };
7176
7177 const payload_is_ref = switch (node_ty) {
7178 .@"if" => if_full.payload_token != null and tree.tokenTag(if_full.payload_token.?) == .asterisk,
7179 .@"catch" => ri.rl == .ref or ri.rl == .ref_coerced_ty,
7180 };
7181
7182 // We need to call `rvalue` to write through to the pointer only if we had a
7183 // result pointer and aren't forwarding it.
7184 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
7185 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
7186 var scalar_cases_len: u32 = 0;
7187 var multi_cases_len: u32 = 0;
7188 var inline_cases_len: u32 = 0;
7189 var has_else = false;
7190 var else_node: Ast.Node.OptionalIndex = .none;
7191 var else_src: ?Ast.TokenIndex = null;
7192 for (switch_full.ast.cases) |case_node| {
7193 const case = tree.fullSwitchCase(case_node).?;
7194
7195 if (case.ast.values.len == 0) {
7196 const case_src = case.ast.arrow_token - 1;
7197 if (else_src) |src| {
7198 return astgen.failTokNotes(
7199 case_src,
7200 "multiple else prongs in switch expression",
7201 .{},
7202 &[_]u32{
7203 try astgen.errNoteTok(
7204 src,
7205 "previous else prong here",
7206 .{},
7207 ),
7208 },
7209 );
7210 }
7211 has_else = true;
7212 else_node = case_node.toOptional();
7213 else_src = case_src;
7214 continue;
7215 } else if (case.ast.values.len == 1 and
7216 tree.nodeTag(case.ast.values[0]) == .identifier and
7217 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(case.ast.values[0])), "_"))
7218 {
7219 const case_src = case.ast.arrow_token - 1;
7220 return astgen.failTokNotes(
7221 case_src,
7222 "'_' prong is not allowed when switching on errors",
7223 .{},
7224 &[_]u32{
7225 try astgen.errNoteTok(
7226 case_src,
7227 "consider using 'else'",
7228 .{},
7229 ),
7230 },
7231 );
7232 }
7233
7234 for (case.ast.values) |val| {
7235 if (tree.nodeTag(val) == .string_literal)
7236 return astgen.failNode(val, "cannot switch on strings", .{});
7237 }
7238
7239 if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) != .switch_range) {
7240 scalar_cases_len += 1;
7241 } else {
7242 multi_cases_len += 1;
7243 }
7244 if (case.inline_token != null) {
7245 inline_cases_len += 1;
7246 }
7247 }
7248
7249 const operand_ri: ResultInfo = .{
7250 .rl = if (payload_is_ref) .ref else .none,
7251 .ctx = .error_handling_expr,
7252 };
7253
7254 astgen.advanceSourceCursorToNode(operand_node);
7255 const operand_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
7256
7257 const raw_operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, switch_node);
7258 const item_ri: ResultInfo = .{ .rl = .none };
7259
7260 // This contains the data that goes into the `extra` array for the SwitchBlockErrUnion, except
7261 // the first cases_nodes.len slots are a table that indexes payloads later in the array,
7262 // with the non-error and else case indices coming first, then scalar_cases_len indexes, then
7263 // multi_cases_len indexes
7264 const payloads = &astgen.scratch;
7265 const scratch_top = astgen.scratch.items.len;
7266 const case_table_start = scratch_top;
7267 const scalar_case_table = case_table_start + 1 + @intFromBool(has_else);
7268 const multi_case_table = scalar_case_table + scalar_cases_len;
7269 const case_table_end = multi_case_table + multi_cases_len;
7270
7271 try astgen.scratch.resize(gpa, case_table_end);
7272 defer astgen.scratch.items.len = scratch_top;
7273
7274 var block_scope = parent_gz.makeSubBlock(scope);
7275 // block_scope not used for collecting instructions
7276 block_scope.instructions_top = GenZir.unstacked_top;
7277 block_scope.setBreakResultInfo(block_ri);
7278
7279 // Sema expects a dbg_stmt immediately before switch_block_err_union
7280 try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc);
7281 // This gets added to the parent block later, after the item expressions.
7282 const switch_block = try parent_gz.makeBlockInst(.switch_block_err_union, switch_node);
7283
7284 // We re-use this same scope for all cases, including the special prong, if any.
7285 var case_scope = parent_gz.makeSubBlock(&block_scope.base);
7286 case_scope.instructions_top = GenZir.unstacked_top;
7287
7288 {
7289 const body_len_index: u32 = @intCast(payloads.items.len);
7290 payloads.items[case_table_start] = body_len_index;
7291 try payloads.resize(gpa, body_len_index + 1); // body_len
7292
7293 case_scope.instructions_top = parent_gz.instructions.items.len;
7294 defer case_scope.unstack();
7295
7296 const unwrap_payload_tag: Zir.Inst.Tag = if (payload_is_ref)
7297 .err_union_payload_unsafe_ptr
7298 else
7299 .err_union_payload_unsafe;
7300
7301 const unwrapped_payload = try case_scope.addUnNode(
7302 unwrap_payload_tag,
7303 raw_operand,
7304 catch_or_if_node,
7305 );
7306
7307 switch (node_ty) {
7308 .@"catch" => {
7309 const case_result = switch (ri.rl) {
7310 .ref, .ref_coerced_ty => unwrapped_payload,
7311 else => try rvalue(
7312 &case_scope,
7313 block_scope.break_result_info,
7314 unwrapped_payload,
7315 catch_or_if_node,
7316 ),
7317 };
7318 _ = try case_scope.addBreakWithSrcNode(
7319 .@"break",
7320 switch_block,
7321 case_result,
7322 catch_or_if_node,
7323 );
7324 },
7325 .@"if" => {
7326 var payload_val_scope: Scope.LocalVal = undefined;
7327
7328 const then_node = if_full.ast.then_expr;
7329 const then_sub_scope = s: {
7330 assert(if_full.error_token != null);
7331 if (if_full.payload_token) |payload_token| {
7332 const token_name_index = payload_token + @intFromBool(payload_is_ref);
7333 const ident_name = try astgen.identAsString(token_name_index);
7334 const token_name_str = tree.tokenSlice(token_name_index);
7335 if (mem.eql(u8, "_", token_name_str))
7336 break :s &case_scope.base;
7337 try astgen.detectLocalShadowing(
7338 &case_scope.base,
7339 ident_name,
7340 token_name_index,
7341 token_name_str,
7342 .capture,
7343 );
7344 payload_val_scope = .{
7345 .parent = &case_scope.base,
7346 .gen_zir = &case_scope,
7347 .name = ident_name,
7348 .inst = unwrapped_payload,
7349 .token_src = token_name_index,
7350 .id_cat = .capture,
7351 };
7352 try case_scope.addDbgVar(.dbg_var_val, ident_name, unwrapped_payload);
7353 break :s &payload_val_scope.base;
7354 } else {
7355 _ = try case_scope.addUnNode(
7356 .ensure_err_union_payload_void,
7357 raw_operand,
7358 catch_or_if_node,
7359 );
7360 break :s &case_scope.base;
7361 }
7362 };
7363 const then_result = try expr(
7364 &case_scope,
7365 then_sub_scope,
7366 block_scope.break_result_info,
7367 then_node,
7368 );
7369 try checkUsed(parent_gz, &case_scope.base, then_sub_scope);
7370 if (!case_scope.endsWithNoReturn()) {
7371 _ = try case_scope.addBreakWithSrcNode(
7372 .@"break",
7373 switch_block,
7374 then_result,
7375 then_node,
7376 );
7377 }
7378 },
7379 }
7380
7381 const case_slice = case_scope.instructionsSlice();
7382 const body_len = astgen.countBodyLenAfterFixupsExtraRefs(case_slice, &.{switch_block});
7383 try payloads.ensureUnusedCapacity(gpa, body_len);
7384 const capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = switch (node_ty) {
7385 .@"catch" => .none,
7386 .@"if" => if (if_full.payload_token == null)
7387 .none
7388 else if (payload_is_ref)
7389 .by_ref
7390 else
7391 .by_val,
7392 };
7393 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
7394 .body_len = @intCast(body_len),
7395 .capture = capture,
7396 .is_inline = false,
7397 .has_tag_capture = false,
7398 });
7399 appendBodyWithFixupsExtraRefsArrayList(astgen, payloads, case_slice, &.{switch_block});
7400 }
7401
7402 const err_name = blk: {
7403 const err_str = tree.tokenSlice(error_payload);
7404 if (mem.eql(u8, err_str, "_")) {
7405 // This is fatal because we already know we're switching on the captured error.
7406 return astgen.failTok(error_payload, "discard of error capture; omit it instead", .{});
7407 }
7408 const err_name = try astgen.identAsString(error_payload);
7409 try astgen.detectLocalShadowing(scope, err_name, error_payload, err_str, .capture);
7410
7411 break :blk err_name;
7412 };
7413
7414 // allocate a shared dummy instruction for the error capture
7415 const err_inst = err_inst: {
7416 const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
7417 try astgen.instructions.append(astgen.gpa, .{
7418 .tag = .extended,
7419 .data = .{ .extended = .{
7420 .opcode = .value_placeholder,
7421 .small = undefined,
7422 .operand = undefined,
7423 } },
7424 });
7425 break :err_inst inst;
7426 };
7427
7428 // In this pass we generate all the item and prong expressions for error cases.
7429 var multi_case_index: u32 = 0;
7430 var scalar_case_index: u32 = 0;
7431 var any_uses_err_capture = false;
7432 for (switch_full.ast.cases) |case_node| {
7433 const case = tree.fullSwitchCase(case_node).?;
7434
7435 const is_multi_case = case.ast.values.len > 1 or
7436 (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) == .switch_range);
7437
7438 var dbg_var_name: Zir.NullTerminatedString = .empty;
7439 var dbg_var_inst: Zir.Inst.Ref = undefined;
7440 var err_scope: Scope.LocalVal = undefined;
7441 var capture_scope: Scope.LocalVal = undefined;
7442
7443 const sub_scope = blk: {
7444 err_scope = .{
7445 .parent = &case_scope.base,
7446 .gen_zir = &case_scope,
7447 .name = err_name,
7448 .inst = err_inst.toRef(),
7449 .token_src = error_payload,
7450 .id_cat = .capture,
7451 };
7452
7453 const capture_token = case.payload_token orelse break :blk &err_scope.base;
7454 if (tree.tokenTag(capture_token) != .identifier) {
7455 return astgen.failTok(capture_token + 1, "error set cannot be captured by reference", .{});
7456 }
7457
7458 const capture_slice = tree.tokenSlice(capture_token);
7459 if (mem.eql(u8, capture_slice, "_")) {
7460 try astgen.appendErrorTok(capture_token, "discard of error capture; omit it instead", .{});
7461 }
7462 const tag_name = try astgen.identAsString(capture_token);
7463 try astgen.detectLocalShadowing(&case_scope.base, tag_name, capture_token, capture_slice, .capture);
7464
7465 capture_scope = .{
7466 .parent = &case_scope.base,
7467 .gen_zir = &case_scope,
7468 .name = tag_name,
7469 .inst = switch_block.toRef(),
7470 .token_src = capture_token,
7471 .id_cat = .capture,
7472 };
7473 dbg_var_name = tag_name;
7474 dbg_var_inst = switch_block.toRef();
7475
7476 err_scope.parent = &capture_scope.base;
7477
7478 break :blk &err_scope.base;
7479 };
7480
7481 const header_index: u32 = @intCast(payloads.items.len);
7482 const body_len_index = if (is_multi_case) blk: {
7483 payloads.items[multi_case_table + multi_case_index] = header_index;
7484 multi_case_index += 1;
7485 try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
7486
7487 // items
7488 var items_len: u32 = 0;
7489 for (case.ast.values) |item_node| {
7490 if (tree.nodeTag(item_node) == .switch_range) continue;
7491 items_len += 1;
7492
7493 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);
7494 try payloads.append(gpa, @intFromEnum(item_inst));
7495 }
7496
7497 // ranges
7498 var ranges_len: u32 = 0;
7499 for (case.ast.values) |range| {
7500 if (tree.nodeTag(range) != .switch_range) continue;
7501 ranges_len += 1;
7502
7503 const first_node, const last_node = tree.nodeData(range).node_and_node;
7504 const first = try comptimeExpr(parent_gz, scope, item_ri, first_node, .switch_item);
7505 const last = try comptimeExpr(parent_gz, scope, item_ri, last_node, .switch_item);
7506 try payloads.appendSlice(gpa, &[_]u32{
7507 @intFromEnum(first), @intFromEnum(last),
7508 });
7509 }
7510
7511 payloads.items[header_index] = items_len;
7512 payloads.items[header_index + 1] = ranges_len;
7513 break :blk header_index + 2;
7514 } else if (case_node.toOptional() == else_node) blk: {
7515 payloads.items[case_table_start + 1] = header_index;
7516 try payloads.resize(gpa, header_index + 1); // body_len
7517 break :blk header_index;
7518 } else blk: {
7519 payloads.items[scalar_case_table + scalar_case_index] = header_index;
7520 scalar_case_index += 1;
7521 try payloads.resize(gpa, header_index + 2); // item, body_len
7522 const item_node = case.ast.values[0];
7523 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);
7524 payloads.items[header_index] = @intFromEnum(item_inst);
7525 break :blk header_index + 1;
7526 };
7527
7528 {
7529 // temporarily stack case_scope on parent_gz
7530 case_scope.instructions_top = parent_gz.instructions.items.len;
7531 defer case_scope.unstack();
7532
7533 if (do_err_trace and nodeMayAppendToErrorTrace(tree, operand_node))
7534 _ = try case_scope.addSaveErrRetIndex(.always);
7535
7536 if (dbg_var_name != .empty) {
7537 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
7538 }
7539
7540 const target_expr_node = case.ast.target_expr;
7541 const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint);
7542 // check capture_scope, not err_scope to avoid false positive unused error capture
7543 try checkUsed(parent_gz, &case_scope.base, err_scope.parent);
7544 const uses_err = err_scope.used != .none or err_scope.discarded != .none;
7545 if (uses_err) {
7546 try case_scope.addDbgVar(.dbg_var_val, err_name, err_inst.toRef());
7547 any_uses_err_capture = true;
7548 }
7549
7550 if (!parent_gz.refIsNoReturn(case_result)) {
7551 if (do_err_trace)
7552 try restoreErrRetIndex(
7553 &case_scope,
7554 .{ .block = switch_block },
7555 block_scope.break_result_info,
7556 target_expr_node,
7557 case_result,
7558 );
7559
7560 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
7561 }
7562
7563 const case_slice = case_scope.instructionsSlice();
7564 const extra_insts: []const Zir.Inst.Index = if (uses_err) &.{ switch_block, err_inst } else &.{switch_block};
7565 const body_len = astgen.countBodyLenAfterFixupsExtraRefs(case_slice, extra_insts);
7566 try payloads.ensureUnusedCapacity(gpa, body_len);
7567 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
7568 .body_len = @intCast(body_len),
7569 .capture = if (case.payload_token != null) .by_val else .none,
7570 .is_inline = case.inline_token != null,
7571 .has_tag_capture = false,
7572 });
7573 appendBodyWithFixupsExtraRefsArrayList(astgen, payloads, case_slice, extra_insts);
7574 }
7575 }
7576 // Now that the item expressions are generated we can add this.
7577 try parent_gz.instructions.append(gpa, switch_block);
7578
7579 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlockErrUnion).@"struct".fields.len +
7580 @intFromBool(multi_cases_len != 0) +
7581 payloads.items.len - case_table_end +
7582 (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).@"struct".fields.len);
7583
7584 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlockErrUnion{
7585 .operand = raw_operand,
7586 .bits = Zir.Inst.SwitchBlockErrUnion.Bits{
7587 .has_multi_cases = multi_cases_len != 0,
7588 .has_else = has_else,
7589 .scalar_cases_len = @intCast(scalar_cases_len),
7590 .any_uses_err_capture = any_uses_err_capture,
7591 .payload_is_ref = payload_is_ref,
7592 },
7593 .main_src_node_offset = parent_gz.nodeIndexToRelative(catch_or_if_node),
7594 });
7595
7596 if (multi_cases_len != 0) {
7597 astgen.extra.appendAssumeCapacity(multi_cases_len);
7598 }
7599
7600 if (any_uses_err_capture) {
7601 astgen.extra.appendAssumeCapacity(@intFromEnum(err_inst));
7602 }
7603
7604 const zir_datas = astgen.instructions.items(.data);
7605 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
7606
7607 for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {
7608 var body_len_index = start_index;
7609 var end_index = start_index;
7610 const table_index = case_table_start + i;
7611 if (table_index < scalar_case_table) {
7612 end_index += 1;
7613 } else if (table_index < multi_case_table) {
7614 body_len_index += 1;
7615 end_index += 2;
7616 } else {
7617 body_len_index += 2;
7618 const items_len = payloads.items[start_index];
7619 const ranges_len = payloads.items[start_index + 1];
7620 end_index += 3 + items_len + 2 * ranges_len;
7621 }
7622 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
7623 end_index += prong_info.body_len;
7624 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
7625 }
7626
7627 if (need_result_rvalue) {
7628 return rvalue(parent_gz, ri, switch_block.toRef(), switch_node);
7629 } else {
7630 return switch_block.toRef();
7631 }
7632}
7152const SwitchNonErr = union(enum) {
7153 /// A regular switch expression.
7154 /// Emits `switch_block[_ref]`.
7155 none,
7156 /// `eu catch |err| switch (err) { ... }`
7157 ///
7158 /// `switch` must not be labeled.
7159 /// Emits `switch_block_err_union`.
7160 @"catch",
7161 /// `if (eu) |payload| { ... } else |err| switch (err) { ... }`
7162 ///
7163 /// `switch` must not be labeled.
7164 /// Emits `switch_block_err_union`.
7165 @"if": Ast.full.If,
7166 /// `eu catch |err| label: switch (err) { ... }`
7167 /// `if (eu) |payload| { ... } else |err| label: switch (err) { ... }`
7168 ///
7169 /// `switch` must be labeled.
7170 /// Emits a `condbr` on the non-error body and a regular switch, though the
7171 /// non-error prong and all `break`s from switch prongs are peers.
7172 /// Exists to avoid a rather complex special case of `switch_block_err_union`.
7173 peer_break_target: struct {
7174 /// Refers to the enclosing block of the entire switch-on-err expression.
7175 block_inst: Zir.Inst.Index,
7176 /// Belongs to `block_inst`.
7177 block_ri: ResultInfo,
7178 },
7179};
76337180
76347181fn switchExpr(
76357182 parent_gz: *GenZir,
......@@ -7637,13 +7184,38 @@ fn switchExpr(
76377184 ri: ResultInfo,
76387185 node: Ast.Node.Index,
76397186 switch_full: Ast.full.Switch,
7187 non_err: SwitchNonErr,
76407188) InnerError!Zir.Inst.Ref {
76417189 const astgen = parent_gz.astgen;
76427190 const gpa = astgen.gpa;
76437191 const tree = astgen.tree;
7644 const operand_node = switch_full.ast.condition;
7192
7193 const switch_node, const operand_node, const err_token = switch (non_err) {
7194 .none, .peer_break_target => .{
7195 node,
7196 switch_full.ast.condition,
7197 undefined,
7198 },
7199 .@"catch" => .{
7200 tree.nodeData(node).node_and_node[1],
7201 tree.nodeData(node).node_and_node[0],
7202 tree.nodeMainToken(node) + 2,
7203 },
7204 .@"if" => |if_full| .{
7205 if_full.ast.else_expr.unwrap().?,
7206 if_full.ast.cond_expr,
7207 if_full.error_token.?,
7208 },
7209 };
76457210 const case_nodes = switch_full.ast.cases;
76467211
7212 const is_err_switch = non_err != .none;
7213 const needs_non_err_handling = switch (non_err) {
7214 .none => false,
7215 .peer_break_target => false, // handled by parent expression
7216 .@"catch", .@"if" => true,
7217 };
7218
76477219 const need_rl = astgen.nodes_need_rl.contains(node);
76487220 const block_ri: ResultInfo = if (need_rl) ri else .{
76497221 .rl = switch (ri.rl) {
......@@ -7653,226 +7225,562 @@ fn switchExpr(
76537225 },
76547226 .ctx = ri.ctx,
76557227 };
7228
76567229 // We need to call `rvalue` to write through to the pointer only if we had a
76577230 // result pointer and aren't forwarding it.
76587231 const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
76597232 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
76607233
7234 const catch_or_if_node = if (needs_non_err_handling) node else undefined;
7235 const do_err_trace = needs_non_err_handling and astgen.fn_block != null;
7236 const non_err_is_ref: bool = switch (non_err) {
7237 .none, .peer_break_target => undefined,
7238 .@"catch" => ri.rl == .ref or ri.rl == .ref_coerced_ty,
7239 .@"if" => |if_full| if_full.payload_token != null and
7240 tree.tokenTag(if_full.payload_token.?) == .asterisk,
7241 };
7242
76617243 if (switch_full.label_token) |label_token| {
76627244 try astgen.checkLabelRedefinition(scope, label_token);
76637245 }
76647246
7247 const err_capture_name: Zir.NullTerminatedString = if (needs_non_err_handling) blk: {
7248 const err_str = tree.tokenSlice(err_token);
7249 if (mem.eql(u8, err_str, "_")) {
7250 // This is fatal because we already know we're switching on the captured error.
7251 return astgen.failTok(err_token, "discard of error capture; omit it instead", .{});
7252 }
7253 const err_name = try astgen.identAsString(err_token);
7254 try astgen.detectLocalShadowing(scope, err_name, err_token, err_str, .capture);
7255 break :blk err_name;
7256 } else undefined;
7257
76657258 // We perform two passes over the AST. This first pass is to collect information
7666 // for the following variables, make note of the special prong AST node index,
7667 // and bail out with a compile error if there are multiple special prongs present.
7259 // for the following variables, make note of the special prong AST node indices,
7260 // and bail out with a compile error if there are incompatible special prongs present.
76687261 var any_payload_is_ref = false;
7262 var any_has_payload_capture = false;
76697263 var any_has_tag_capture = false;
7670 var any_non_inline_capture = false;
7264 var any_maybe_runtime_capture = false;
76717265 var scalar_cases_len: u32 = 0;
76727266 var multi_cases_len: u32 = 0;
7673 var inline_cases_len: u32 = 0;
7267 var total_items_len: usize = 0;
7268 var total_ranges_len: usize = 0;
76747269 var else_case_node: Ast.Node.OptionalIndex = .none;
7675 var else_src: ?Ast.TokenIndex = null;
7676 var underscore_case_node: Ast.Node.OptionalIndex = .none;
76777270 var underscore_node: Ast.Node.OptionalIndex = .none;
7678 var underscore_src: ?Ast.TokenIndex = null;
7679 var underscore_additional_items: Zir.SpecialProngs.AdditionalItems = .none;
76807271 for (case_nodes) |case_node| {
76817272 const case = tree.fullSwitchCase(case_node).?;
76827273 if (case.payload_token) |payload_token| {
76837274 const ident = if (tree.tokenTag(payload_token) == .asterisk) blk: {
7275 // Capturing errors by reference is never allowed, but as we will
7276 // check for this again later we will fail as late as possible.
76847277 any_payload_is_ref = true;
76857278 break :blk payload_token + 1;
76867279 } else payload_token;
7280
7281 if (!mem.eql(u8, tree.tokenSlice(ident), "_")) {
7282 any_has_payload_capture = true;
7283
7284 // If we're capturing a union, its payload value cannot always be
7285 // comptime-known, even if its prong is inlined as inlining only
7286 // affects its enum tag.
7287 // This check isn't perfect, because for things like enums, the
7288 // entire capture *is* comptime-known for inline prongs! But such
7289 // knowledge requires semantic analysis.
7290 any_maybe_runtime_capture = true;
7291 }
76877292 if (tree.tokenTag(ident + 1) == .comma) {
76887293 any_has_tag_capture = true;
7689 }
76907294
7691 // If the first capture is ignored, then there is no runtime-known
7692 // capture, as the tag capture must be for an inline prong.
7693 // This check isn't perfect, because for things like enums, the
7694 // first prong *is* comptime-known for inline prongs! But such
7695 // knowledge requires semantic analysis.
7696 if (!mem.eql(u8, tree.tokenSlice(ident), "_")) {
7697 any_non_inline_capture = true;
7295 if (case.inline_token == null) {
7296 any_maybe_runtime_capture = true;
7297 }
76987298 }
76997299 }
77007300
77017301 // Check for else prong.
77027302 if (case.ast.values.len == 0) {
7703 const case_src = case.ast.arrow_token - 1;
7704 if (else_src) |src| {
7303 if (else_case_node.unwrap()) |prev_case_node| {
7304 const prev_else_tok = tree.fullSwitchCase(prev_case_node).?.ast.arrow_token - 1;
7305 const else_tok = case.ast.arrow_token - 1;
77057306 return astgen.failTokNotes(
7706 case_src,
7307 else_tok,
77077308 "multiple else prongs in switch expression",
77087309 .{},
7709 &[_]u32{
7710 try astgen.errNoteTok(
7711 src,
7712 "previous else prong here",
7713 .{},
7714 ),
7715 },
7310 &.{try astgen.errNoteTok(prev_else_tok, "previous else prong here", .{})},
77167311 );
77177312 }
77187313 else_case_node = case_node.toOptional();
7719 else_src = case_src;
77207314 continue;
77217315 }
77227316
7723 // Check for '_' prong.
7724 var case_has_underscore = false;
7317 // Check for '_' prong and ranges.
7318 var case_has_ranges = false;
77257319 for (case.ast.values) |val| {
77267320 switch (tree.nodeTag(val)) {
7727 .identifier => if (mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_")) {
7728 const val_src = tree.nodeMainToken(val);
7729 if (underscore_src) |src| {
7730 return astgen.failTokNotes(
7731 val_src,
7732 "multiple '_' prongs in switch expression",
7733 .{},
7734 &[_]u32{
7735 try astgen.errNoteTok(
7736 src,
7737 "previous '_' prong here",
7738 .{},
7739 ),
7740 },
7741 );
7742 }
7743 if (case.inline_token != null) {
7744 return astgen.failTok(val_src, "cannot inline '_' prong", .{});
7745 }
7746 underscore_case_node = case_node.toOptional();
7747 underscore_src = val_src;
7748 underscore_node = val.toOptional();
7749 underscore_additional_items = switch (case.ast.values.len) {
7750 0 => unreachable,
7751 1 => .none,
7752 2 => .one,
7753 else => .many,
7754 };
7755 case_has_underscore = true;
7321 .switch_range => {
7322 total_ranges_len += 1;
7323 case_has_ranges = true;
77567324 },
77577325 .string_literal => return astgen.failNode(val, "cannot switch on strings", .{}),
7758 else => {},
7326 else => |tag| {
7327 total_items_len += 1;
7328 if (tag == .identifier and
7329 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_"))
7330 {
7331 if (is_err_switch) {
7332 const case_src = case.ast.arrow_token - 1;
7333 return astgen.failTokNotes(
7334 case_src,
7335 "'_' prong is not allowed when switching on errors",
7336 .{},
7337 &.{
7338 try astgen.errNoteTok(
7339 case_src,
7340 "consider using 'else'",
7341 .{},
7342 ),
7343 },
7344 );
7345 }
7346 if (underscore_node.unwrap()) |prev_src| {
7347 return astgen.failNodeNotes(
7348 val,
7349 "multiple '_' prongs in switch expression",
7350 .{},
7351 &.{try astgen.errNoteNode(prev_src, "previous '_' prong here", .{})},
7352 );
7353 }
7354 if (case.inline_token != null) {
7355 return astgen.failNode(val, "cannot inline '_' prong", .{});
7356 }
7357 underscore_node = val.toOptional();
7358 }
7359 },
77597360 }
77607361 }
7761 if (case_has_underscore) continue;
77627362
7763 if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) != .switch_range) {
7363 const case_len = case.ast.values.len;
7364 if (case_len == 1 and !case_has_ranges) {
77647365 scalar_cases_len += 1;
7765 } else {
7366 } else if (case_len >= 1) {
77667367 multi_cases_len += 1;
77677368 }
7768 if (case.inline_token != null) {
7769 inline_cases_len += 1;
7770 }
77717369 }
77727370
7773 const special_prongs: Zir.SpecialProngs = .init(
7774 else_src != null,
7775 underscore_src != null,
7776 underscore_additional_items,
7777 );
7778 const has_else = special_prongs.hasElse();
7779 const has_under = special_prongs.hasUnder();
7780
7781 const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none };
7371 const has_else = else_case_node != .none;
7372 const has_under = underscore_node != .none;
7373 if (is_err_switch) assert(!has_under); // should have failed by now
7374 const any_ranges = total_ranges_len > 0;
77827375
7783 astgen.advanceSourceCursorToNode(operand_node);
7784 const operand_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
7785
7786 const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);
7787 const item_ri: ResultInfo = .{ .rl = .none };
7788
7789 // If this switch is labeled, it may have `continue`s targeting it, and thus we need the operand type
7790 // to provide a result type.
7791 const raw_operand_ty_ref = if (switch_full.label_token != null) t: {
7792 break :t try parent_gz.addUnNode(.typeof, raw_operand, operand_node);
7793 } else undefined;
7794
7795 // This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti,
7796 // except the first cases_nodes.len slots are a table that indexes payloads later in the array, with
7797 // the special case index coming first, then scalar_case_len indexes, then multi_cases_len indexes
7376 // This contains all of the body lengths (already in the correct order) and
7377 // the bodies they belong to that go into the `extra` array later, except the
7378 // first item_table_end slots are a table that indexes the item bodies (and
7379 // also indirectly the prong bodies, as they are always trailing after their
7380 // item bodies).
77987381 const payloads = &astgen.scratch;
77997382 const scratch_top = astgen.scratch.items.len;
7800 const case_table_start = scratch_top;
7801 const else_case_index = if (has_else) case_table_start else undefined;
7802 const under_case_index = if (has_under) case_table_start + @intFromBool(has_else) else undefined;
7803 const scalar_case_table = case_table_start + @intFromBool(has_else) + @intFromBool(has_under);
7804 const multi_case_table = scalar_case_table + scalar_cases_len;
7805 const case_table_end = multi_case_table + multi_cases_len;
7806 try astgen.scratch.resize(gpa, case_table_end);
7383 var payloads_end = scratch_top;
7384
7385 // Since range item body pairs are always contiguous we don't technically
7386 // have to keep track of the position of the second body. However handling
7387 // all of the several indices and offsets is complicated enough as it is,
7388 // so for the sake of keeping this function a little bit more simple we do
7389 // it anyway.
7390
7391 const scalar_body_table = payloads_end;
7392 payloads_end += scalar_cases_len;
7393 const multi_item_body_table = payloads_end;
7394 payloads_end += total_items_len + 2 * total_ranges_len - scalar_cases_len;
7395 const multi_prong_body_table = payloads_end;
7396 payloads_end += multi_cases_len;
7397 const body_table_end = payloads_end;
7398
7399 const scalar_prong_infos_start = payloads_end;
7400 payloads_end += scalar_cases_len;
7401 const multi_prong_infos_start = payloads_end;
7402 payloads_end += multi_cases_len;
7403 const multi_case_items_lens_start = payloads_end;
7404 payloads_end += multi_cases_len;
7405 const multi_case_ranges_lens_start = if (any_ranges) blk: {
7406 const multi_case_ranges_lens_start = payloads_end;
7407 payloads_end += multi_cases_len;
7408 break :blk multi_case_ranges_lens_start;
7409 } else undefined;
7410 const scalar_item_infos_start = payloads_end;
7411 payloads_end += scalar_cases_len;
7412 const multi_items_infos_start = payloads_end;
7413 payloads_end += total_items_len - scalar_cases_len + 2 * total_ranges_len;
7414 const bodies_start = payloads_end;
7415
7416 try payloads.resize(gpa, bodies_start);
78077417 defer astgen.scratch.items.len = scratch_top;
78087418
7419 var non_err_prong_body_start: u32 = undefined;
7420 var else_prong_body_start: u32 = undefined;
7421 var non_err_info: Zir.Inst.SwitchBlock.ProngInfo.NonErr = undefined;
7422 var else_info: Zir.Inst.SwitchBlock.ProngInfo.Else = undefined;
7423
78097424 var block_scope = parent_gz.makeSubBlock(scope);
78107425 // block_scope not used for collecting instructions
78117426 block_scope.instructions_top = GenZir.unstacked_top;
7812 block_scope.setBreakResultInfo(block_ri);
78137427
7814 // Sema expects a dbg_stmt immediately before switch_block(_ref)
7428 const operand_ri: ResultInfo = .{
7429 .rl = if (any_payload_is_ref or
7430 (needs_non_err_handling and non_err_is_ref)) .ref else .none,
7431 .ctx = if (do_err_trace) .error_handling_expr else .none,
7432 };
7433
7434 astgen.advanceSourceCursorToNode(operand_node);
7435 const operand_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
7436
7437 const raw_operand: Zir.Inst.Ref = if (needs_non_err_handling)
7438 try reachableExpr(parent_gz, scope, operand_ri, operand_node, switch_node)
7439 else
7440 try expr(parent_gz, scope, operand_ri, operand_node);
7441
7442 // Sema expects a dbg_stmt immediately before any kind of switch_block inst.
78157443 try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc);
78167444 // This gets added to the parent block later, after the item expressions.
7817 const switch_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_block_ref else .switch_block;
7818 const switch_block = try parent_gz.makeBlockInst(switch_tag, node);
7445 const switch_tag: Zir.Inst.Tag = switch (non_err) {
7446 .none, .peer_break_target => if (any_payload_is_ref) .switch_block_ref else .switch_block,
7447 .@"if", .@"catch" => .switch_block_err_union,
7448 };
7449 const switch_block = try parent_gz.makeBlockInst(switch_tag, switch_node);
7450
7451 // Set `break` target if applicable; `continue` target may differ!
7452 switch (non_err) {
7453 .none => {
7454 if (switch_full.label_token != null) {
7455 block_scope.break_target = switch_block;
7456 }
7457 block_scope.setBreakResultInfo(block_ri);
7458 },
7459 .@"catch", .@"if" => {
7460 assert(switch_full.label_token == null); // use `peer_break_target` code path instead!
7461 block_scope.setBreakResultInfo(block_ri);
7462 },
7463 .peer_break_target => |peer_break_target| {
7464
7465 // Special case; we have an error switch + label situation and we
7466 // want to generate this:
7467 // ```
7468 // %1 = block({
7469 // %2 = is_non_err(%operand)
7470 // %3 = condbr(%2, {
7471 // %4 = err_union_payload_unsafe(%operand)
7472 // %5 = break(%1, result) // targets enclosing `block`
7473 // }, {
7474 // %6 = err_union_code(%operand)
7475 // %7 = switch_block(%6,
7476 // { ... } => {
7477 // %8 = break(%1, result) // targets enclosing `block`
7478 // },
7479 // { ... } => {
7480 // %9 = switch_continue(%7, result) // targets `switch_block`
7481 // },
7482 // )
7483 // %10 = break(%1, @void_value)
7484 // })
7485 // })
7486 // ```
7487 // to ensure that the non-err case and the switch are only peers when
7488 // breaking from either, but not when continuing the switch. We use
7489 // this lowering to avoiding a rather complex special case in Sema.
7490
7491 assert(switch_full.label_token != null); // use `switch_block_err_union` code path instead!
7492 assert(.block == astgen.instructions.items(.tag)[@intFromEnum(peer_break_target.block_inst)]);
7493 block_scope.break_target = peer_break_target.block_inst;
7494 block_scope.setBreakResultInfo(peer_break_target.block_ri);
7495 },
7496 }
7497
7498 // We need a bunch of separate locations to store several capture values:
7499 // `... |err| switch (err) { else => |e| { ... } }` // `err` and `e`
7500 // `... => |payload, tag| { ... }` // `payload` and `tag`
7501 // and result types:
7502 // `foo => { ... }` // `foo` needs a result type
7503 // `... => continue :sw val` // `val` needs a result type
7504 // Some observations:
7505 // - If we just use the switch inst itself we don't need a placeholder!
7506 // - We can always tell for sure whether a capture exists. We also know
7507 // that its existence implies that it has to be used.
7508 // - We can't know whether there are any `continue`s before analyzing all
7509 // prong bodies. At that point we already need a result location. We do
7510 // know whether there even *could* be any though by looking for a label.
7511 // - Sema wants a result location in `zirSwitchContinue`. If that's the
7512 // switch inst itself, there's no need to look at the switch inst data.
7513 // Some conclusions:
7514 // - We should use the switch inst as the continue result location if needed.
7515 // - If we need more insts for captures and our switch inst is already used
7516 // for something else, we start creating placeholder insts.
7517
7518 // Prong items use the switch block instruction as their result type.
7519 // No other components of the switch statement are in scope while they are
7520 // being resolved, so this is never a problem.
7521 const item_ri: ResultInfo = .{ .rl = .{ .coerced_ty = switch_block.toRef() } };
7522
7523 var switch_block_inst_is_occupied: bool = false;
78197524
78207525 if (switch_full.label_token) |label_token| {
7821 block_scope.continue_block = switch_block.toOptional();
7526 block_scope.label = .{ .token = label_token };
7527 block_scope.continue_target = .{ .switch_continue = switch_block };
78227528 block_scope.continue_result_info = .{
78237529 .rl = if (any_payload_is_ref)
7824 .{ .ref_coerced_ty = raw_operand_ty_ref }
7530 .{ .ref_coerced_ty = switch_block.toRef() }
78257531 else
7826 .{ .coerced_ty = raw_operand_ty_ref },
7532 .{ .coerced_ty = switch_block.toRef() },
78277533 };
7534 switch_block_inst_is_occupied = true;
78287535
7829 block_scope.label = .{
7830 .token = label_token,
7831 .block_inst = switch_block,
7832 };
7833 // `break` can target this via `label.block_inst`
7834 // `break_result_info` already set by `setBreakResultInfo`
7536 // `break_target` and `break_result_info` already set above.
78357537 }
7538 if (needs_non_err_handling) {
7539 // `switch_block_err_union` uses the switch block inst as its err capture/
7540 // switch operand. This is always ok as its switch can never have a label.
7541 assert(!switch_block_inst_is_occupied);
7542 switch_block_inst_is_occupied = true;
7543 }
7544 // `... => |payload| { ... }`
7545 const payload_capture_inst, const payload_capture_inst_is_placeholder = inst: {
7546 if (!any_has_payload_capture) break :inst .{ undefined, false };
7547 if (!switch_block_inst_is_occupied) {
7548 switch_block_inst_is_occupied = true;
7549 break :inst .{ switch_block, false };
7550 }
7551 break :inst .{ try astgen.appendPlaceholder(), true };
7552 };
7553 // `... => |_, tag| { ... }`
7554 const tag_capture_inst, const tag_capture_inst_is_placeholder = inst: {
7555 if (!any_has_tag_capture) break :inst .{ undefined, false };
7556 if (!switch_block_inst_is_occupied) {
7557 switch_block_inst_is_occupied = true;
7558 break :inst .{ switch_block, false };
7559 }
7560 break :inst .{ try astgen.appendPlaceholder(), true };
7561 };
78367562
7837 // We re-use this same scope for all cases, including the special prong, if any.
7838 var case_scope = parent_gz.makeSubBlock(&block_scope.base);
7839 case_scope.instructions_top = GenZir.unstacked_top;
7563 var prong_body_extra_insts_buf: [3]Zir.Inst.Index = undefined;
7564 const prong_body_extra_insts: []const Zir.Inst.Index = extra_insts: {
7565 var extra_insts: std.ArrayList(Zir.Inst.Index) = .initBuffer(&prong_body_extra_insts_buf);
7566 if (switch_block_inst_is_occupied) extra_insts.appendAssumeCapacity(switch_block);
7567 if (payload_capture_inst_is_placeholder) extra_insts.appendAssumeCapacity(payload_capture_inst);
7568 if (tag_capture_inst_is_placeholder) extra_insts.appendAssumeCapacity(tag_capture_inst);
7569 break :extra_insts extra_insts.items;
7570 };
78407571
7841 // If any prong has an inline tag capture, allocate a shared dummy instruction for it
7842 const tag_inst = if (any_has_tag_capture) tag_inst: {
7843 const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
7844 try astgen.instructions.append(astgen.gpa, .{
7845 .tag = .extended,
7846 .data = .{ .extended = .{
7847 .opcode = .value_placeholder,
7848 .small = undefined,
7849 .operand = undefined,
7850 } },
7851 });
7852 break :tag_inst inst;
7853 } else undefined;
7572 const switch_operand, const catch_or_if_operand = if (needs_non_err_handling)
7573 .{ switch_block.toRef(), raw_operand }
7574 else
7575 .{ raw_operand, undefined };
7576
7577 // We re-use this same scope for all case items and contents.
7578 var scratch_scope = parent_gz.makeSubBlock(&block_scope.base);
7579 scratch_scope.instructions_top = GenZir.unstacked_top;
7580
7581 // We have to take care of the non-error body first if there is one.
7582 non_err_body: {
7583 if (!needs_non_err_handling) break :non_err_body;
7584
7585 scratch_scope.instructions_top = parent_gz.instructions.items.len;
7586 defer scratch_scope.unstack();
7587
7588 // It's always ok to use the switch block inst to refer to the error union
7589 // payload as the actual switch statement isn't even in scope yet.
7590 const non_err_payload_inst = switch_block;
7591 var non_err_capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = .none;
7592
7593 switch (non_err) {
7594 .none, .peer_break_target => unreachable,
7595 .@"catch" => {
7596 // We always effectively capture the error union payload; we use
7597 // it to `break` from the entire `switch_block_err_union`.
7598 non_err_capture = if (non_err_is_ref) .by_ref else .by_val;
7599
7600 const then_result = switch (ri.rl) {
7601 .ref, .ref_coerced_ty => non_err_payload_inst.toRef(),
7602 else => try rvalue(
7603 &scratch_scope,
7604 block_scope.break_result_info,
7605 non_err_payload_inst.toRef(),
7606 catch_or_if_node,
7607 ),
7608 };
7609 _ = try scratch_scope.addBreakWithSrcNode(
7610 .@"break",
7611 switch_block,
7612 then_result,
7613 catch_or_if_node,
7614 );
7615 },
7616 .@"if" => |if_full| {
7617 var payload_val_scope: Scope.LocalVal = undefined;
7618
7619 const then_node = if_full.ast.then_expr;
7620 const then_sub_scope: *Scope = scope: {
7621 if (if_full.payload_token) |payload_token| {
7622 const ident_token = payload_token + @intFromBool(non_err_is_ref);
7623 const ident_name = try astgen.identAsString(ident_token);
7624 const ident_name_str = tree.tokenSlice(ident_token);
7625 if (mem.eql(u8, "_", ident_name_str)) {
7626 break :scope &scratch_scope.base;
7627 }
7628 non_err_capture = if (non_err_is_ref) .by_ref else .by_val;
7629 try astgen.detectLocalShadowing(&scratch_scope.base, ident_name, ident_token, ident_name_str, .capture);
7630 payload_val_scope = .{
7631 .parent = &scratch_scope.base,
7632 .gen_zir = &scratch_scope,
7633 .name = ident_name,
7634 .inst = non_err_payload_inst.toRef(),
7635 .token_src = ident_token,
7636 .id_cat = .capture,
7637 };
7638 try scratch_scope.addDbgVar(.dbg_var_val, ident_name, non_err_payload_inst.toRef());
7639 break :scope &payload_val_scope.base;
7640 } else {
7641 _ = try scratch_scope.addUnNode(
7642 .ensure_err_union_payload_void,
7643 catch_or_if_operand,
7644 catch_or_if_node,
7645 );
7646 break :scope &scratch_scope.base;
7647 }
7648 };
7649 const then_result = try fullBodyExpr(&scratch_scope, then_sub_scope, block_scope.break_result_info, then_node, .allow_branch_hint);
7650 try checkUsed(parent_gz, &scratch_scope.base, then_sub_scope);
7651 if (!scratch_scope.endsWithNoReturn()) {
7652 _ = try scratch_scope.addBreakWithSrcNode(.@"break", switch_block, then_result, then_node);
7653 }
7654 },
7655 }
7656 const body_slice = scratch_scope.instructionsSlice();
7657 const body_start: u32 = @intCast(payloads.items.len);
7658 const body_len = astgen.countBodyLenAfterFixupsExtraRefs(body_slice, &.{non_err_payload_inst});
7659 try payloads.ensureUnusedCapacity(gpa, body_len);
7660 astgen.appendBodyWithFixupsExtraRefsArrayList(payloads, body_slice, &.{non_err_payload_inst});
7661
7662 non_err_prong_body_start = body_start;
7663 non_err_info = .{
7664 .body_len = @intCast(body_len),
7665 .capture = non_err_capture,
7666 .operand_is_ref = non_err_is_ref,
7667 };
7668 }
78547669
78557670 // In this pass we generate all the item and prong expressions.
78567671 var multi_case_index: u32 = 0;
78577672 var scalar_case_index: u32 = 0;
7673 var multi_item_offset: usize = 0;
78587674 for (case_nodes) |case_node| {
78597675 const case = tree.fullSwitchCase(case_node).?;
78607676
7861 const is_multi_case = case.ast.values.len > 1 or
7862 (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) == .switch_range);
7677 const ranges_len: u32 = if (any_ranges) blk: {
7678 var ranges_len: u32 = 0;
7679 for (case.ast.values) |value| {
7680 ranges_len += @intFromBool(tree.nodeTag(value) == .switch_range);
7681 }
7682 break :blk ranges_len;
7683 } else 0;
7684 const items_len: u32 = @intCast(case.ast.values.len - ranges_len);
7685 const is_multi_case = items_len > 1 or ranges_len > 0;
7686
7687 // item/range bodies in order of occurence
7688 var item_i: usize = 0;
7689 var range_i: usize = 0;
7690 for (case.ast.values) |value| {
7691 const is_range = tree.nodeTag(value) == .switch_range;
7692 const range: [2]Ast.Node.Index = if (is_range) tree.nodeData(value).node_and_node else undefined;
7693 const nodes: []const Ast.Node.Index = if (is_range) &range else &.{value};
7694 for (nodes) |item| {
7695 // We lower enum literals, error values and number literals
7696 // manually to save space since they are very commonly used as
7697 // switch case items.
7698 const body_start: u32 = @intCast(payloads.items.len);
7699 const item_info: Zir.Inst.SwitchBlock.ItemInfo = blk: switch (tree.nodeTag(item)) {
7700 .enum_literal => {
7701 const str_index = try astgen.identAsString(tree.nodeMainToken(item));
7702 break :blk .wrap(.{ .enum_literal = str_index });
7703 },
7704 .error_value => {
7705 const ident_token = tree.nodeMainToken(item) + 2; // skip 'error', '.'
7706 const str_index = try astgen.identAsString(ident_token);
7707 break :blk .wrap(.{ .error_value = str_index });
7708 },
7709 else => if (value.toOptional() == underscore_node) {
7710 break :blk .wrap(.under);
7711 } else {
7712 scratch_scope.instructions_top = parent_gz.instructions.items.len;
7713 defer scratch_scope.unstack();
7714 const item_result = try fullBodyExpr(&scratch_scope, scope, item_ri, item, .normal);
7715 if (!scratch_scope.endsWithNoReturn()) {
7716 _ = try scratch_scope.addBreakWithSrcNode(.break_inline, switch_block, item_result, item);
7717 }
7718 const item_slice = scratch_scope.instructionsSlice();
7719 const body_len = astgen.countBodyLenAfterFixupsExtraRefs(item_slice, &.{switch_block});
7720 try payloads.ensureUnusedCapacity(gpa, body_len);
7721 astgen.appendBodyWithFixupsExtraRefsArrayList(payloads, item_slice, &.{switch_block});
7722 break :blk .wrap(.{ .body_len = body_len });
7723 },
7724 };
7725 if (is_multi_case) {
7726 if (is_range) {
7727 const offset = multi_item_offset + items_len + range_i;
7728 payloads.items[multi_item_body_table + offset] = body_start;
7729 payloads.items[multi_items_infos_start + offset] = @bitCast(item_info);
7730 range_i += 1;
7731 } else {
7732 const offset = multi_item_offset + item_i;
7733 payloads.items[multi_item_body_table + offset] = body_start;
7734 payloads.items[multi_items_infos_start + offset] = @bitCast(item_info);
7735 item_i += 1;
7736 }
7737 } else {
7738 payloads.items[scalar_body_table + scalar_case_index] = body_start;
7739 payloads.items[scalar_item_infos_start + scalar_case_index] = @bitCast(item_info);
7740 }
7741 }
7742 }
7743 if (is_multi_case) {
7744 assert(item_i == items_len and range_i == 2 * ranges_len);
7745 payloads.items[multi_case_items_lens_start + multi_case_index] = items_len;
7746 if (any_ranges) {
7747 payloads.items[multi_case_ranges_lens_start + multi_case_index] = ranges_len;
7748 }
7749 multi_item_offset += items_len + 2 * ranges_len;
7750 }
7751
7752 // Capture and prong body
78637753
7864 var dbg_var_name: Zir.NullTerminatedString = .empty;
7865 var dbg_var_inst: Zir.Inst.Ref = undefined;
7754 var dbg_var_payload_name: Zir.NullTerminatedString = .empty;
7755 var dbg_var_payload_inst: Zir.Inst.Ref = undefined;
78667756 var dbg_var_tag_name: Zir.NullTerminatedString = .empty;
78677757 var dbg_var_tag_inst: Zir.Inst.Ref = undefined;
78687758 var has_tag_capture = false;
7869 var capture_val_scope: Scope.LocalVal = undefined;
7870 var tag_scope: Scope.LocalVal = undefined;
7759 var err_capture_scope: Scope.LocalVal = undefined;
7760 var payload_capture_scope: Scope.LocalVal = undefined;
7761 var tag_capture_scope: Scope.LocalVal = undefined;
78717762
78727763 var capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = .none;
78737764
7874 const sub_scope = blk: {
7875 const payload_token = case.payload_token orelse break :blk &case_scope.base;
7765 // Check all captures and make them available to the prong body.
7766 // Potential captures are:
7767 // - for regular switch: payload and tag
7768 // - for error switch: switch operand and payload
7769 const prong_body_scope: *Scope = scope: {
7770 const switch_scope: *Scope = if (needs_non_err_handling) blk: {
7771 // We want to have the captured error we're switching on in scope!
7772 err_capture_scope = .{
7773 .parent = &scratch_scope.base,
7774 .gen_zir = &scratch_scope,
7775 .name = err_capture_name,
7776 .inst = switch_operand,
7777 .token_src = err_token,
7778 .id_cat = .capture,
7779 };
7780 break :blk &err_capture_scope.base;
7781 } else &scratch_scope.base;
7782
7783 const payload_token = case.payload_token orelse break :scope switch_scope;
78767784 const capture_is_ref = tree.tokenTag(payload_token) == .asterisk;
78777785 const ident = payload_token + @intFromBool(capture_is_ref);
78787786
......@@ -7882,34 +7790,42 @@ fn switchExpr(
78827790 var payload_sub_scope: *Scope = undefined;
78837791 if (mem.eql(u8, ident_slice, "_")) {
78847792 if (capture_is_ref) {
7793 // |*_, tag| is invalid, so we can fail early
78857794 return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
78867795 }
7887 payload_sub_scope = &case_scope.base;
7796 capture = .none;
7797 payload_sub_scope = switch_scope;
78887798 } else {
78897799 const capture_name = try astgen.identAsString(ident);
7890 try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .capture);
7891 capture_val_scope = .{
7892 .parent = &case_scope.base,
7893 .gen_zir = &case_scope,
7800 try astgen.detectLocalShadowing(&scratch_scope.base, capture_name, ident, ident_slice, .capture);
7801 payload_capture_scope = .{
7802 .parent = switch_scope,
7803 .gen_zir = &scratch_scope,
78947804 .name = capture_name,
7895 .inst = switch_block.toRef(),
7805 .inst = payload_capture_inst.toRef(),
78967806 .token_src = ident,
78977807 .id_cat = .capture,
78987808 };
7899 dbg_var_name = capture_name;
7900 dbg_var_inst = switch_block.toRef();
7901 payload_sub_scope = &capture_val_scope.base;
7809 dbg_var_payload_name = payload_capture_scope.name;
7810 dbg_var_payload_inst = payload_capture_scope.inst;
7811 payload_sub_scope = &payload_capture_scope.base;
79027812 }
79037813
7904 const tag_token = if (tree.tokenTag(ident + 1) == .comma)
7905 ident + 2
7906 else
7907 break :blk payload_sub_scope;
7814 if (is_err_switch and capture == .by_ref) {
7815 return astgen.failTok(ident, "error set cannot be captured by reference", .{});
7816 }
7817
7818 const tag_token = if (tree.tokenTag(ident + 1) == .comma) blk: {
7819 break :blk ident + 2;
7820 } else if (capture == .none) {
7821 // discarding the capture is only valid if the tag is captured
7822 // whether the tag capture is discarded is handled below
7823 return astgen.failTok(payload_token, "discard of capture; omit it instead", .{});
7824 } else break :scope payload_sub_scope;
7825
79087826 const tag_slice = tree.tokenSlice(tag_token);
79097827 if (mem.eql(u8, tag_slice, "_")) {
7910 try astgen.appendErrorTok(tag_token, "discard of tag capture; omit it instead", .{});
7911 } else if (case.inline_token == null) {
7912 return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});
7828 return astgen.failTok(tag_token, "discard of tag capture; omit it instead", .{});
79137829 }
79147830 const tag_name = try astgen.identAsString(tag_token);
79157831 try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture");
......@@ -7917,123 +7833,136 @@ fn switchExpr(
79177833 assert(any_has_tag_capture);
79187834 has_tag_capture = true;
79197835
7920 tag_scope = .{
7836 if (is_err_switch) {
7837 return astgen.failTok(tag_token, "cannot capture tag of error union", .{});
7838 }
7839
7840 tag_capture_scope = .{
79217841 .parent = payload_sub_scope,
7922 .gen_zir = &case_scope,
7842 .gen_zir = &scratch_scope,
79237843 .name = tag_name,
7924 .inst = tag_inst.toRef(),
7844 .inst = tag_capture_inst.toRef(),
79257845 .token_src = tag_token,
79267846 .id_cat = .@"switch tag capture",
79277847 };
7928 dbg_var_tag_name = tag_name;
7929 dbg_var_tag_inst = tag_inst.toRef();
7930 break :blk &tag_scope.base;
7848 dbg_var_tag_name = tag_capture_scope.name;
7849 dbg_var_tag_inst = tag_capture_scope.inst;
7850 break :scope &tag_capture_scope.base;
79317851 };
79327852
7933 const header_index: u32 = @intCast(payloads.items.len);
7934 const body_len_index = if (is_multi_case) blk: {
7935 if (case_node.toOptional() == underscore_case_node) {
7936 payloads.items[under_case_index] = header_index;
7937 if (special_prongs.hasOneAdditionalItem()) {
7938 try payloads.resize(gpa, header_index + 2); // item, body_len
7939 const maybe_item_node = case.ast.values[0];
7940 const item_node = if (maybe_item_node.toOptional() == underscore_node)
7941 case.ast.values[1]
7942 else
7943 maybe_item_node;
7944 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);
7945 payloads.items[header_index] = @intFromEnum(item_inst);
7946 break :blk header_index + 1;
7947 }
7948 } else {
7949 payloads.items[multi_case_table + multi_case_index] = header_index;
7950 multi_case_index += 1;
7951 }
7952 try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
7953
7954 // items
7955 var items_len: u32 = 0;
7956 for (case.ast.values) |item_node| {
7957 if (item_node.toOptional() == underscore_node or
7958 tree.nodeTag(item_node) == .switch_range)
7959 {
7960 continue;
7961 }
7962 items_len += 1;
7963
7964 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);
7965 try payloads.append(gpa, @intFromEnum(item_inst));
7966 }
7967
7968 // ranges
7969 var ranges_len: u32 = 0;
7970 for (case.ast.values) |range| {
7971 if (tree.nodeTag(range) != .switch_range) {
7972 continue;
7973 }
7974 ranges_len += 1;
7975
7976 const first_node, const last_node = tree.nodeData(range).node_and_node;
7977 const first = try comptimeExpr(parent_gz, scope, item_ri, first_node, .switch_item);
7978 const last = try comptimeExpr(parent_gz, scope, item_ri, last_node, .switch_item);
7979 try payloads.appendSlice(gpa, &[_]u32{
7980 @intFromEnum(first), @intFromEnum(last),
7981 });
7982 }
7983
7984 payloads.items[header_index] = items_len;
7985 payloads.items[header_index + 1] = ranges_len;
7986 break :blk header_index + 2;
7987 } else if (case_node.toOptional() == else_case_node) blk: {
7988 payloads.items[else_case_index] = header_index;
7989 try payloads.resize(gpa, header_index + 1); // body_len
7990 break :blk header_index;
7991 } else if (case_node.toOptional() == underscore_case_node) blk: {
7992 assert(!special_prongs.hasAdditionalItems());
7993 payloads.items[under_case_index] = header_index;
7994 try payloads.resize(gpa, header_index + 1); // body_len
7995 break :blk header_index;
7996 } else blk: {
7997 payloads.items[scalar_case_table + scalar_case_index] = header_index;
7998 scalar_case_index += 1;
7999 try payloads.resize(gpa, header_index + 2); // item, body_len
8000 const item_node = case.ast.values[0];
8001 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);
8002 payloads.items[header_index] = @intFromEnum(item_inst);
8003 break :blk header_index + 1;
8004 };
7853 if (capture != .none) assert(any_has_payload_capture);
7854 if (is_err_switch) {
7855 assert(!any_payload_is_ref); // should have failed by now
7856 assert(!any_has_tag_capture); // should have failed by now
7857 }
80057858
8006 {
8007 // temporarily stack case_scope on parent_gz
8008 case_scope.instructions_top = parent_gz.instructions.items.len;
8009 defer case_scope.unstack();
7859 prong_body: {
7860 scratch_scope.instructions_top = parent_gz.instructions.items.len;
7861 defer scratch_scope.unstack();
80107862
8011 if (dbg_var_name != .empty) {
8012 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
7863 if (dbg_var_payload_name != .empty) {
7864 try scratch_scope.addDbgVar(.dbg_var_val, dbg_var_payload_name, dbg_var_payload_inst);
80137865 }
80147866 if (dbg_var_tag_name != .empty) {
8015 try case_scope.addDbgVar(.dbg_var_val, dbg_var_tag_name, dbg_var_tag_inst);
7867 try scratch_scope.addDbgVar(.dbg_var_val, dbg_var_tag_name, dbg_var_tag_inst);
7868 }
7869 if (do_err_trace and nodeMayAppendToErrorTrace(tree, operand_node)) {
7870 _ = try scratch_scope.addSaveErrRetIndex(.always);
80167871 }
80177872 const target_expr_node = case.ast.target_expr;
8018 const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint);
8019 try checkUsed(parent_gz, &case_scope.base, sub_scope);
8020 if (!parent_gz.refIsNoReturn(case_result)) {
8021 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
7873 const case_result = try fullBodyExpr(&scratch_scope, prong_body_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint);
7874 if (needs_non_err_handling) {
7875 // If we would check `scratch_scope` here, we would get a false
7876 // positive, that being the switch operand itself!
7877 try checkUsed(parent_gz, &err_capture_scope.base, prong_body_scope);
7878 } else {
7879 try checkUsed(parent_gz, &scratch_scope.base, prong_body_scope);
7880 }
7881 if (!scratch_scope.endsWithNoReturn()) {
7882 // As our last action before the break, "pop" the error trace if needed
7883 if (do_err_trace) {
7884 try restoreErrRetIndex(
7885 &scratch_scope,
7886 .{ .block = switch_block },
7887 block_scope.break_result_info,
7888 target_expr_node,
7889 case_result,
7890 );
7891 }
7892 _ = try scratch_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
80227893 }
80237894
8024 const case_slice = case_scope.instructionsSlice();
8025 const extra_insts: []const Zir.Inst.Index = if (has_tag_capture) &.{ switch_block, tag_inst } else &.{switch_block};
8026 const body_len = astgen.countBodyLenAfterFixupsExtraRefs(case_slice, extra_insts);
7895 const body_slice = scratch_scope.instructionsSlice();
7896 const body_start: u32 = @intCast(payloads.items.len);
7897 const body_len = astgen.countBodyLenAfterFixupsExtraRefs(body_slice, prong_body_extra_insts);
80277898 try payloads.ensureUnusedCapacity(gpa, body_len);
8028 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
7899 astgen.appendBodyWithFixupsExtraRefsArrayList(payloads, body_slice, prong_body_extra_insts);
7900
7901 if (case_node.toOptional() == else_case_node) {
7902 assert(case.ast.values.len == 0);
7903
7904 // Specific `else` bodies can cause Sema to omit the
7905 // "unreachable else prong" error so that certain generic code
7906 // patterns don't trigger it. We do that for these bodies:
7907 // `else => unreachable,`
7908 // `else => return,`
7909 // `else => |e| return e,` (where `e` is any identifier)
7910 const is_simple_noreturn = switch (tree.nodeTag(target_expr_node)) {
7911 .unreachable_literal => true, // `=> unreachable,`
7912 .@"return" => simple_noreturn: {
7913 const retval_node = tree.nodeData(target_expr_node).opt_node.unwrap() orelse {
7914 break :simple_noreturn true; // `=> return,`
7915 };
7916 // Check for `=> |e| return e,`
7917 if (capture != .by_val) break :simple_noreturn false;
7918 if (tree.nodeTag(retval_node) != .identifier) break :simple_noreturn false;
7919 const payload_name = try astgen.identAsString(case.payload_token.?);
7920 const retval_name = try astgen.identAsString(tree.nodeMainToken(retval_node));
7921 break :simple_noreturn payload_name == retval_name;
7922 },
7923 else => false,
7924 };
7925
7926 else_info = .{
7927 .body_len = @intCast(body_len),
7928 .capture = capture,
7929 .is_inline = case.inline_token != null,
7930 .has_tag_capture = has_tag_capture,
7931 .is_simple_noreturn = is_simple_noreturn,
7932 };
7933 else_prong_body_start = body_start;
7934 break :prong_body;
7935 }
7936
7937 // We allow prongs with error items which are not inside the error set
7938 // being switched on if their body is `=> comptime unreachable,`.
7939 const is_comptime_unreach = comptime_unreach: {
7940 if (tree.nodeTag(target_expr_node) != .@"comptime") break :comptime_unreach false;
7941 const comptime_node = tree.nodeData(target_expr_node).node;
7942 break :comptime_unreach tree.nodeTag(comptime_node) == .unreachable_literal;
7943 };
7944
7945 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = .{
80297946 .body_len = @intCast(body_len),
80307947 .capture = capture,
80317948 .is_inline = case.inline_token != null,
80327949 .has_tag_capture = has_tag_capture,
8033 });
8034 appendBodyWithFixupsExtraRefsArrayList(astgen, payloads, case_slice, extra_insts);
7950 .is_comptime_unreach = is_comptime_unreach,
7951 };
7952
7953 if (is_multi_case) {
7954 payloads.items[multi_prong_body_table + multi_case_index] = body_start;
7955 payloads.items[multi_prong_infos_start + multi_case_index] = @bitCast(prong_info);
7956 multi_case_index += 1;
7957 } else {
7958 // prong body start is implicit, it's right behind our only item.
7959 payloads.items[scalar_prong_infos_start + scalar_case_index] = @bitCast(prong_info);
7960 scalar_case_index += 1;
7961 }
80357962 }
80367963 }
7964 assert(scalar_case_index + multi_case_index + @intFromBool(has_else) == case_nodes.len);
7965 assert(multi_items_infos_start + multi_item_offset == bodies_start);
80377966
80387967 if (switch_full.label_token) |label_token| if (!block_scope.label.?.used) {
80397968 try astgen.appendErrorTok(label_token, "unused switch label", .{});
......@@ -8042,84 +7971,100 @@ fn switchExpr(
80427971 // Now that the item expressions are generated we can add this.
80437972 try parent_gz.instructions.append(gpa, switch_block);
80447973
7974 // We've collected all of the data we need! Now we just have to finalize it
7975 // by copying our bodies from `payloads` to `extra`, this time in the order
7976 // expected by ZIR consumers.
7977
80457978 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).@"struct".fields.len +
8046 @intFromBool(multi_cases_len != 0) +
8047 @intFromBool(any_has_tag_capture) +
8048 payloads.items.len - scratch_top);
8049
8050 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
8051 .operand = raw_operand,
8052 .bits = Zir.Inst.SwitchBlock.Bits{
8053 .has_multi_cases = multi_cases_len != 0,
8054 .special_prongs = special_prongs,
8055 .any_has_tag_capture = any_has_tag_capture,
8056 .any_non_inline_capture = any_non_inline_capture,
7979 @intFromBool(multi_cases_len > 0) + // multi_cases_len
7980 @intFromBool(payload_capture_inst_is_placeholder) + // payload_capture_placeholder
7981 @intFromBool(tag_capture_inst_is_placeholder) + // tag_capture_placeholder
7982 @intFromBool(needs_non_err_handling) + // catch_or_if_src_node_offset
7983 @intFromBool(needs_non_err_handling) + // non_err_info
7984 @intFromBool(has_else) + // else_info
7985 payloads.items.len - body_table_end); // item infos and bodies
7986
7987 // singular pieces of data
7988 const zir_payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
7989 .raw_operand = raw_operand,
7990 .bits = .{
7991 .has_multi_cases = multi_cases_len > 0,
7992 .any_ranges = any_ranges,
7993 .has_else = has_else,
7994 .has_under = has_under,
80577995 .has_continue = switch_full.label_token != null and block_scope.label.?.used_for_continue,
7996 .any_maybe_runtime_capture = any_maybe_runtime_capture,
7997 .payload_capture_inst_is_placeholder = payload_capture_inst_is_placeholder,
7998 .tag_capture_inst_is_placeholder = tag_capture_inst_is_placeholder,
80587999 .scalar_cases_len = @intCast(scalar_cases_len),
80598000 },
80608001 });
8002 astgen.instructions.items(.data)[@intFromEnum(switch_block)].pl_node.payload_index = zir_payload_index;
80618003
8062 if (multi_cases_len != 0) {
8063 astgen.extra.appendAssumeCapacity(multi_cases_len);
8004 if (multi_cases_len > 0) astgen.extra.appendAssumeCapacity(multi_cases_len);
8005 if (payload_capture_inst_is_placeholder) astgen.extra.appendAssumeCapacity(@intFromEnum(payload_capture_inst));
8006 if (tag_capture_inst_is_placeholder) astgen.extra.appendAssumeCapacity(@intFromEnum(tag_capture_inst));
8007 if (needs_non_err_handling) {
8008 const catch_or_if_src_node_offset = parent_gz.nodeIndexToRelative(catch_or_if_node);
8009 astgen.extra.appendAssumeCapacity(@bitCast(@intFromEnum(catch_or_if_src_node_offset)));
8010 astgen.extra.appendAssumeCapacity(@bitCast(non_err_info));
80648011 }
8012 if (has_else) astgen.extra.appendAssumeCapacity(@bitCast(else_info));
80658013
8066 if (any_has_tag_capture) {
8067 astgen.extra.appendAssumeCapacity(@intFromEnum(tag_inst));
8068 }
8014 const extra_payloads_start = astgen.extra.items.len;
80698015
8070 const zir_datas = astgen.instructions.items(.data);
8071 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
8016 // body lens
8017 astgen.extra.appendSliceAssumeCapacity(payloads.items[body_table_end..bodies_start]);
80728018
8019 // bodies
8020 if (needs_non_err_handling) {
8021 const body = payloads.items[non_err_prong_body_start..][0..non_err_info.body_len];
8022 astgen.extra.appendSliceAssumeCapacity(body);
8023 }
80738024 if (has_else) {
8074 const start_index = payloads.items[else_case_index];
8075 var end_index = start_index + 1;
8076 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[start_index]);
8077 end_index += prong_info.body_len;
8078 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
8079 }
8080 if (has_under) {
8081 const start_index = payloads.items[under_case_index];
8082 var body_len_index = start_index;
8083 var end_index = start_index;
8084 switch (underscore_additional_items) {
8085 .none => {
8086 end_index += 1;
8087 },
8088 .one => {
8089 body_len_index += 1;
8090 end_index += 2;
8091 },
8092 .many => {
8093 body_len_index += 2;
8094 const items_len = payloads.items[start_index];
8095 const ranges_len = payloads.items[start_index + 1];
8096 end_index += 3 + items_len + 2 * ranges_len;
8097 },
8098 }
8099 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
8100 end_index += prong_info.body_len;
8101 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
8102 }
8103 for (payloads.items[scalar_case_table..case_table_end], 0..) |start_index, i| {
8104 var body_len_index = start_index;
8105 var end_index = start_index;
8106 const table_index = scalar_case_table + i;
8107 if (table_index < multi_case_table) {
8108 body_len_index += 1;
8109 end_index += 2;
8110 } else {
8111 body_len_index += 2;
8112 const items_len = payloads.items[start_index];
8113 const ranges_len = payloads.items[start_index + 1];
8114 end_index += 3 + items_len + 2 * ranges_len;
8025 const body = payloads.items[else_prong_body_start..][0..else_info.body_len];
8026 astgen.extra.appendSliceAssumeCapacity(body);
8027 }
8028 for (0..scalar_cases_len) |scalar_i| {
8029 const item_info: Zir.Inst.SwitchBlock.ItemInfo = @bitCast(payloads.items[scalar_item_infos_start + scalar_i]);
8030 const item_body_start = payloads.items[scalar_body_table + scalar_i];
8031 const item_body = payloads.items[item_body_start..][0 .. item_info.bodyLen() orelse 0];
8032 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[scalar_prong_infos_start + scalar_i]);
8033 const prong_body_start = item_body_start + item_body.len;
8034 const prong_body = payloads.items[prong_body_start..][0..prong_info.body_len];
8035 astgen.extra.appendSliceAssumeCapacity(prong_body);
8036 astgen.extra.appendSliceAssumeCapacity(item_body);
8037 }
8038 var multi_item_i: usize = 0;
8039 for (0..multi_cases_len) |multi_i| {
8040 const prong_body_start = payloads.items[multi_prong_body_table + multi_i];
8041 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[multi_prong_infos_start + multi_i]);
8042 const prong_body = payloads.items[prong_body_start..][0..prong_info.body_len];
8043 astgen.extra.appendSliceAssumeCapacity(prong_body);
8044
8045 const items_len = payloads.items[multi_case_items_lens_start + multi_i];
8046 const ranges_len = if (any_ranges) ranges_len: {
8047 break :ranges_len payloads.items[multi_case_ranges_lens_start + multi_i];
8048 } else 0;
8049 // The table entries and body lens are already in the correct order so we
8050 // don't have to differentiate between items and ranges here.
8051 for (0..items_len + 2 * ranges_len) |_| {
8052 const item_info: Zir.Inst.SwitchBlock.ItemInfo = @bitCast(payloads.items[multi_items_infos_start + multi_item_i]);
8053 if (item_info.bodyLen()) |body_len| {
8054 const body_start = payloads.items[multi_item_body_table + multi_item_i];
8055 const body = payloads.items[body_start..][0..body_len];
8056 astgen.extra.appendSliceAssumeCapacity(body);
8057 }
8058 multi_item_i += 1;
81158059 }
8116 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
8117 end_index += prong_info.body_len;
8118 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
81198060 }
81208061
8062 // Make sure we didn't forget anything...
8063 assert(multi_item_i == total_items_len + 2 * total_ranges_len - scalar_cases_len);
8064 assert(astgen.extra.items.len - extra_payloads_start == payloads.items.len - body_table_end);
8065
81218066 if (need_result_rvalue) {
8122 return rvalue(parent_gz, ri, switch_block.toRef(), node);
8067 return rvalue(parent_gz, ri, switch_block.toRef(), switch_node);
81238068 } else {
81248069 return switch_block.toRef();
81258070 }
......@@ -8382,7 +8327,6 @@ fn localVarRef(
83828327) InnerError!Zir.Inst.Ref {
83838328 const astgen = gz.astgen;
83848329 const name_str_index = try astgen.identAsString(ident_token);
8385 var s = scope;
83868330 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
83878331 var found_needs_tunnel: bool = undefined; // defined when `found_already != null`
83888332 var found_namespaces_out: u32 = undefined; // defined when `found_already != null`
......@@ -8392,10 +8336,8 @@ fn localVarRef(
83928336 // defined by `num_namespaces_out != 0`
83938337 var capturing_namespace: *Scope.Namespace = undefined;
83948338
8395 while (true) switch (s.tag) {
8396 .local_val => {
8397 const local_val = s.cast(Scope.LocalVal).?;
8398
8339 find_scope: switch (scope.unwrap()) {
8340 .local_val => |local_val| {
83998341 if (local_val.name == name_str_index) {
84008342 // Locals cannot shadow anything, so we do not need to look for ambiguous
84018343 // references in this case.
......@@ -8418,10 +8360,9 @@ fn localVarRef(
84188360
84198361 return rvalueNoCoercePreRef(gz, ri, value_inst, ident);
84208362 }
8421 s = local_val.parent;
8363 continue :find_scope local_val.parent.unwrap();
84228364 },
8423 .local_ptr => {
8424 const local_ptr = s.cast(Scope.LocalPtr).?;
8365 .local_ptr => |local_ptr| {
84258366 if (local_ptr.name == name_str_index) {
84268367 if (ri.rl == .discard and ri.ctx == .assignment) {
84278368 local_ptr.discarded = .fromToken(ident_token);
......@@ -8470,12 +8411,11 @@ fn localVarRef(
84708411 },
84718412 }
84728413 }
8473 s = local_ptr.parent;
8414 continue :find_scope local_ptr.parent.unwrap();
84748415 },
8475 .gen_zir => s = s.cast(GenZir).?.parent,
8476 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
8477 .namespace => {
8478 const ns = s.cast(Scope.Namespace).?;
8416 .gen_zir => |gen_zir| continue :find_scope gen_zir.parent.unwrap(),
8417 .defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
8418 .namespace => |ns| {
84798419 if (ns.decls.get(name_str_index)) |i| {
84808420 if (found_already) |f| {
84818421 return astgen.failNodeNotes(ident, "ambiguous reference", .{}, &.{
......@@ -8490,10 +8430,10 @@ fn localVarRef(
84908430 }
84918431 num_namespaces_out += 1;
84928432 capturing_namespace = ns;
8493 s = ns.parent;
8433 continue :find_scope ns.parent.unwrap();
84948434 },
8495 .top => break,
8496 };
8435 .top => break :find_scope,
8436 }
84978437 if (found_already == null) {
84988438 const ident_name = try astgen.identifierTokenString(ident_token);
84998439 return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name});
......@@ -11785,6 +11725,26 @@ const Scope = struct {
1178511725 };
1178611726 }
1178711727
11728 fn unwrap(base: *Scope) Unwrapped {
11729 return switch (base.tag) {
11730 inline else => |tag| @unionInit(
11731 Unwrapped,
11732 @tagName(tag),
11733 @alignCast(@fieldParentPtr("base", base)),
11734 ),
11735 };
11736 }
11737
11738 const Unwrapped = union(Tag) {
11739 gen_zir: *GenZir,
11740 local_val: *LocalVal,
11741 local_ptr: *LocalPtr,
11742 defer_normal: *Defer,
11743 defer_error: *Defer,
11744 namespace: *Namespace,
11745 top: *Top,
11746 };
11747
1178811748 const Tag = enum {
1178911749 gen_zir,
1179011750 local_val,
......@@ -11910,8 +11870,8 @@ const GenZir = struct {
1191011870 /// whenever we know Sema will analyze the current block with `is_comptime`,
1191111871 /// for instance when we're within a `struct_decl` or a `block_comptime`.
1191211872 is_comptime: bool,
11913 /// Whether we're in an expression within a `@TypeOf` operand. In this case, closure of runtime
11914 /// variables is permitted where it is usually not.
11873 /// Whether we're in an expression within a `@TypeOf` operand. In this case,
11874 /// closure of runtime variables is permitted where it is usually not.
1191511875 is_typeof: bool = false,
1191611876 /// This is set to true for a `GenZir` of a `block_inline`, indicating that
1191711877 /// exits from this block should use `break_inline` rather than `break`.
......@@ -11932,10 +11892,27 @@ const GenZir = struct {
1193211892 /// if use is strictly nested. This saves prior size of list for unstacking.
1193311893 instructions_top: usize,
1193411894 label: ?Label = null,
11935 break_block: Zir.Inst.OptionalIndex = .none,
11936 continue_block: Zir.Inst.OptionalIndex = .none,
11895 /// If `true`, unlabeled `break` and `continue` exprs can target this `GenZir`.
11896 allow_unlabeled_control_flow: bool = false,
11897 /// If `label` is `null` and `unlabeled_control_flow_target` is `false`,
11898 /// this is unused and may be `undefined`.
11899 /// Otherwise, this is the target for a `break` instruction when a `break`
11900 /// targets this `GenZir`.
11901 break_target: Zir.Inst.Index = undefined,
11902 /// If `label` is `null` and `unlabeled_control_flow_target` is `false`,
11903 /// this is unused and may be `undefined`.
11904 continue_target: union(enum) {
11905 /// A `continue` cannot target this `GenZir`; emit an error.
11906 none,
11907 /// Emit a `break` instruction targeting this block.
11908 @"break": Zir.Inst.Index,
11909 /// Emit a `switch_continue` instruction targeting this `switch_block`.
11910 switch_continue: Zir.Inst.Index,
11911 } = undefined,
1193711912 /// Only valid when setBreakResultInfo is called.
1193811913 break_result_info: AstGen.ResultInfo = undefined,
11914 /// If `continue_target` is *not* `switch_continue`, this is unused and may
11915 /// be `undefined`.
1193911916 continue_result_info: AstGen.ResultInfo = undefined,
1194011917
1194111918 suspend_node: Ast.Node.OptionalIndex = .none,
......@@ -12002,7 +11979,6 @@ const GenZir = struct {
1200211979
1200311980 const Label = struct {
1200411981 token: Ast.TokenIndex,
12005 block_inst: Zir.Inst.Index,
1200611982 used: bool = false,
1200711983 used_for_continue: bool = false,
1200811984 };
......@@ -13365,11 +13341,9 @@ fn detectLocalShadowing(
1336513341 });
1336613342 }
1336713343
13368 var s = scope;
1336913344 var outer_scope = false;
13370 while (true) switch (s.tag) {
13371 .local_val => {
13372 const local_val = s.cast(Scope.LocalVal).?;
13345 find_scope: switch (scope.unwrap()) {
13346 .local_val => |local_val| {
1337313347 if (local_val.name == ident_name) {
1337413348 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
1337513349 const name = try gpa.dupe(u8, name_slice);
......@@ -13395,10 +13369,9 @@ fn detectLocalShadowing(
1339513369 ),
1339613370 });
1339713371 }
13398 s = local_val.parent;
13372 continue :find_scope local_val.parent.unwrap();
1339913373 },
13400 .local_ptr => {
13401 const local_ptr = s.cast(Scope.LocalPtr).?;
13374 .local_ptr => |local_ptr| {
1340213375 if (local_ptr.name == ident_name) {
1340313376 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
1340413377 const name = try gpa.dupe(u8, name_slice);
......@@ -13424,14 +13397,12 @@ fn detectLocalShadowing(
1342413397 ),
1342513398 });
1342613399 }
13427 s = local_ptr.parent;
13400 continue :find_scope local_ptr.parent.unwrap();
1342813401 },
13429 .namespace => {
13402 .namespace => |ns| {
1343013403 outer_scope = true;
13431 const ns = s.cast(Scope.Namespace).?;
1343213404 const decl_node = ns.decls.get(ident_name) orelse {
13433 s = ns.parent;
13434 continue;
13405 continue :find_scope ns.parent.unwrap();
1343513406 };
1343613407 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
1343713408 const name = try gpa.dupe(u8, name_slice);
......@@ -13442,13 +13413,13 @@ fn detectLocalShadowing(
1344213413 try astgen.errNoteNode(decl_node, "declared here", .{}),
1344313414 });
1344413415 },
13445 .gen_zir => {
13446 s = s.cast(GenZir).?.parent;
13416 .gen_zir => |gen_zir| {
1344713417 outer_scope = true;
13418 continue :find_scope gen_zir.parent.unwrap();
1344813419 },
13449 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
13450 .top => break,
13451 };
13420 .defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
13421 .top => break :find_scope,
13422 }
1345213423}
1345313424
1345413425const LineColumn = struct { u32, u32 };
......@@ -13685,10 +13656,8 @@ fn scanContainer(
1368513656 continue;
1368613657 }
1368713658
13688 var s = namespace.parent;
13689 while (true) switch (s.tag) {
13690 .local_val => {
13691 const local_val = s.cast(Scope.LocalVal).?;
13659 find_scope: switch (namespace.parent.unwrap()) {
13660 .local_val => |local_val| {
1369213661 if (local_val.name == name_str_index) {
1369313662 try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
1369413663 token_bytes, @tagName(local_val.id_cat),
......@@ -13700,12 +13669,11 @@ fn scanContainer(
1370013669 ),
1370113670 });
1370213671 any_invalid_declarations = true;
13703 break;
13672 break :find_scope;
1370413673 }
13705 s = local_val.parent;
13674 continue :find_scope local_val.parent.unwrap();
1370613675 },
13707 .local_ptr => {
13708 const local_ptr = s.cast(Scope.LocalPtr).?;
13676 .local_ptr => |local_ptr| {
1370913677 if (local_ptr.name == name_str_index) {
1371013678 try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
1371113679 token_bytes, @tagName(local_ptr.id_cat),
......@@ -13717,15 +13685,15 @@ fn scanContainer(
1371713685 ),
1371813686 });
1371913687 any_invalid_declarations = true;
13720 break;
13688 break :find_scope;
1372113689 }
13722 s = local_ptr.parent;
13690 continue :find_scope local_ptr.parent.unwrap();
1372313691 },
13724 .namespace => s = s.cast(Scope.Namespace).?.parent,
13725 .gen_zir => s = s.cast(GenZir).?.parent,
13726 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
13727 .top => break,
13728 };
13692 .namespace => |ns| continue :find_scope ns.parent.unwrap(),
13693 .gen_zir => |gen_zir| continue :find_scope gen_zir.parent.unwrap(),
13694 .defer_normal, .defer_error => |defer_scope| continue :find_scope defer_scope.parent.unwrap(),
13695 .top => break :find_scope,
13696 }
1372913697 }
1373013698
1373113699 if (!any_duplicates) {
......@@ -13776,6 +13744,19 @@ fn scanContainer(
1377613744 return error.AnalysisFail;
1377713745}
1377813746
13747fn appendPlaceholder(astgen: *AstGen) Allocator.Error!Zir.Inst.Index {
13748 const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
13749 try astgen.instructions.append(astgen.gpa, .{
13750 .tag = .extended,
13751 .data = .{ .extended = .{
13752 .opcode = .value_placeholder,
13753 .small = undefined,
13754 .operand = undefined,
13755 } },
13756 });
13757 return inst;
13758}
13759
1377913760/// Assumes capacity for body has already been added. Needed capacity taking into
1378013761/// account fixups can be found with `countBodyLenAfterFixups`.
1378113762fn appendBodyWithFixups(astgen: *AstGen, body: []const Zir.Inst.Index) void {
lib/std/zig/Zir.zig+397-291
......@@ -95,7 +95,6 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) ExtraData(T) {
9595 Inst.Call.Flags,
9696 Inst.BuiltinCall.Flags,
9797 Inst.SwitchBlock.Bits,
98 Inst.SwitchBlockErrUnion.Bits,
9998 Inst.FuncFancy.Bits,
10099 Inst.Declaration.Flags,
101100 Inst.Param.Type,
......@@ -350,7 +349,8 @@ pub const Inst = struct {
350349 /// Uses the `break` union field.
351350 break_inline,
352351 /// Branch from within a switch case to the case specified by the operand.
353 /// Uses the `break` union field. `block_inst` refers to a `switch_block` or `switch_block_ref`.
352 /// Uses the `break` union field. `block_inst` refers to a `switch_block`/
353 /// `switch_block_ref`/`switch_block_err_union`.
354354 switch_continue,
355355 /// Checks that comptime control flow does not happen inside a runtime block.
356356 /// Uses the `un_node` union field.
......@@ -722,8 +722,10 @@ pub const Inst = struct {
722722 /// A switch expression. Uses the `pl_node` union field.
723723 /// AST node is the switch, payload is `SwitchBlock`. Operand is a pointer.
724724 switch_block_ref,
725 /// A switch on an error union `a catch |err| switch (err) {...}`.
726 /// Uses the `pl_node` union field. AST node is the `catch`, payload is `SwitchBlockErrUnion`.
725 /// A switch on an error union:
726 /// - `eu catch |err| switch (err) {...}`, AST node is the `catch`.
727 /// - `if (eu) |payload| {...} else |err| {...}`, AST node is the `if`.
728 /// Uses the `pl_node` union field. Payload is `SwitchBlock`.
727729 switch_block_err_union,
728730 /// Check that operand type supports the dereference operand (.*).
729731 /// Uses the `un_node` field.
......@@ -3293,143 +3295,151 @@ pub const Inst = struct {
32933295 };
32943296
32953297 /// Trailing:
3296 /// 0. multi_cases_len: u32 // if `has_multi_cases`
3297 /// 1. err_capture_inst: u32 // if `any_uses_err_capture`
3298 /// 2. non_err_body {
3299 /// info: ProngInfo,
3300 /// inst: Index // for every `info.body_len`
3301 /// }
3302 /// 3. else_body { // if `has_else`
3303 /// info: ProngInfo,
3304 /// inst: Index // for every `info.body_len`
3305 /// }
3306 /// 4. scalar_cases: { // for every `scalar_cases_len`
3307 /// item: Ref,
3308 /// info: ProngInfo,
3309 /// inst: Index // for every `info.body_len`
3298 /// 0. multi_cases_len: u32, // If has_multi_cases is set.
3299 /// 1. payload_capture_placeholder: Inst.Index, // If payload_capture_inst_is_placeholder is set.
3300 /// // Index of instruction prongs use to refer to their payload capture.
3301 /// 2. tag_capture_placeholder: Inst.Index, // If tag_capture_inst_is_placeholder is set.
3302 /// // Index of instruction prongs use to refer to their tag capture.
3303 /// 3. catch_or_if_src_node_offset: Ast.Node.Offset, // If inst is switch_block_err_union.
3304 /// 4. non_err_info: ProngInfo.NonErr, // If inst is switch_block_err_union.
3305 /// 5. else_info: ProngInfo.Else, // If has_else is set.
3306 /// 6. scalar_prong_info: ProngInfo, // for every scalar_cases_len
3307 /// 7. multi_prong_info: ProngInfo, // for every multi_cases_len
3308 /// 8. multi_case_items_len: u32, // for every multi_cases_len
3309 /// 9. multi_case_ranges_len: u32, // If has_ranges is set: for every multi_cases_len
3310 /// 10. scalar_item_info: ItemInfo, // for every scalar_cases_len
3311 /// 11. multi_items_info: { // for every multi_cases_len
3312 /// item_info: ItemInfo, // for each multi_case_items_len
3313 /// range_items_info: { // for each multi_case_ranges_len
3314 /// first_info: ItemInfo,
3315 /// last_info: ItemInfo,
3316 /// }
3317 /// }
3318 /// 12. non_err_body {
3319 /// body_inst: Index // for every non_err_info.body_len
33103320 /// }
3311 /// 5. multi_cases: { // for every `multi_cases_len`
3312 /// items_len: u32,
3313 /// ranges_len: u32,
3314 /// info: ProngInfo,
3315 /// item: Ref // for every `items_len`
3316 /// ranges: { // for every `ranges_len`
3317 /// item_first: Ref,
3318 /// item_last: Ref,
3321 /// 13. else_body: { // If has_else is set.
3322 /// body_inst: Inst.Index, // for every else_info.body_len
3323 /// }
3324 /// 14. scalar_bodies: { // for every scalar_cases_len
3325 /// prong_body: { // for each body_len in scalar_prong_info
3326 /// body_inst: Inst.Index, // for every body_len
3327 /// }
3328 /// item_body: { // for each body_len in scalar_item_info
3329 /// body_inst: Inst.Index, // for every body_len
33193330 /// }
3320 /// inst: Index // for every `info.body_len`
33213331 /// }
3322 ///
3323 /// When analyzing a case body, the switch instruction itself refers to the
3324 /// captured error, or to the success value in `non_err_body`. Whether this
3325 /// is captured by reference or by value depends on whether the `byref` bit
3326 /// is set for the corresponding body. `err_capture_inst` refers to the error
3327 /// capture outside of the `switch`, i.e. `err` in
3328 /// `x catch |err| switch (err) { ... }`.
3329 pub const SwitchBlockErrUnion = struct {
3330 operand: Ref,
3332 /// 15. multi_bodies: { // for each multi_items_info
3333 /// prong_body: {
3334 /// body_inst: Inst.Index, // for each multi_prong_info.body_len
3335 /// }
3336 /// item_body: { // for each item_info
3337 /// body_inst: Inst.Index, // for every item_info.body_len
3338 /// }
3339 /// range_bodies: { // for each .{first_info, last_info} in range_items_info
3340 /// first_body_inst: Inst.Index, // for every first_info.body_len
3341 /// last_body_inst: Inst.Index, // for every last_info.body_len
3342 /// }
3343 /// }
3344 pub const SwitchBlock = struct {
3345 /// Either `catch`/`if` or `switch` operand.
3346 raw_operand: Ref,
33313347 bits: Bits,
3332 main_src_node_offset: Ast.Node.Offset,
33333348
33343349 pub const Bits = packed struct(u32) {
33353350 /// If true, one or more prongs have multiple items.
33363351 has_multi_cases: bool,
3337 /// If true, there is an else prong. This is mutually exclusive with `has_under`.
3352 /// If true, one or more prongs have ranges.
3353 /// Only valid if `has_multi_cases` is also set.
3354 any_ranges: bool,
33383355 has_else: bool,
3339 any_uses_err_capture: bool,
3340 payload_is_ref: bool,
3356 has_under: bool,
3357 /// If true, at least one prong contains a `continue`.
3358 /// Only valid if `has_label` is set.
3359 has_continue: bool,
3360 // If true, at least one prong has a non-inline payload/tag capture.
3361 any_maybe_runtime_capture: bool,
3362 payload_capture_inst_is_placeholder: bool,
3363 tag_capture_inst_is_placeholder: bool,
33413364 scalar_cases_len: ScalarCasesLen,
33423365
3343 pub const ScalarCasesLen = u28;
3344 };
3345
3346 pub const MultiProng = struct {
3347 items: []const Ref,
3348 body: []const Index,
3366 // NOTE maybe don't steal any more bits from poor `scalar_cases_len`
3367 // and split `Bits` into two parts instead, `raw_operand` surely
3368 // wouldn't mind donating a couple of bits for that purpose...
3369 pub const ScalarCasesLen = u24;
33493370 };
3350 };
3351
3352 /// 0. multi_cases_len: u32 // If has_multi_cases is set.
3353 /// 1. tag_capture_inst: u32 // If any_has_tag_capture is set. Index of instruction prongs use to refer to the inline tag capture.
3354 /// 2. else_body { // If special_prong.hasElse() is set.
3355 /// info: ProngInfo,
3356 /// body member Index for every info.body_len
3357 /// }
3358 /// 3. under_body { // If special_prong.hasUnder() is set.
3359 /// item: Ref, // If special_prong.hasOneAdditionalItem() is set.
3360 /// items_len: u32, // If special_prong.hasManyAdditionalItems() is set.
3361 /// ranges_len: u32, // If special_prong.hasManyAdditionalItems() is set.
3362 /// info: ProngInfo,
3363 /// item: Ref, // for every items_len
3364 /// ranges: { // for every ranges_len
3365 /// item_first: Ref,
3366 /// item_last: Ref,
3367 /// }
3368 /// body member Index for every info.body_len
3369 /// }
3370 /// 4. scalar_cases: { // for every scalar_cases_len
3371 /// item: Ref,
3372 /// info: ProngInfo,
3373 /// body member Index for every info.body_len
3374 /// }
3375 /// 5. multi_cases: { // for every multi_cases_len
3376 /// items_len: u32,
3377 /// ranges_len: u32,
3378 /// info: ProngInfo,
3379 /// item: Ref, // for every items_len
3380 /// ranges: { // for every ranges_len
3381 /// item_first: Ref,
3382 /// item_last: Ref,
3383 /// }
3384 /// body member Index for every info.body_len
3385 /// }
3386 ///
3387 /// When analyzing a case body, the switch instruction itself refers to the
3388 /// captured payload. Whether this is captured by reference or by value
3389 /// depends on whether the `byref` bit is set for the corresponding body.
3390 pub const SwitchBlock = struct {
3391 /// The operand passed to the `switch` expression. If this is a
3392 /// `switch_block`, this is the operand value; if `switch_block_ref` it
3393 /// is a pointer to the operand. `switch_block_ref` is always used if
3394 /// any prong has a byref capture.
3395 operand: Ref,
3396 bits: Bits,
33973371
3398 /// These are stored in trailing data in `extra` for each prong.
33993372 pub const ProngInfo = packed struct(u32) {
3400 body_len: u28,
3373 body_len: u27,
34013374 capture: ProngInfo.Capture,
34023375 is_inline: bool,
34033376 has_tag_capture: bool,
3377 is_comptime_unreach: bool,
34043378
34053379 pub const Capture = enum(u2) {
34063380 none,
34073381 by_val,
34083382 by_ref,
34093383 };
3410 };
34113384
3412 pub const Bits = packed struct(u32) {
3413 /// If true, one or more prongs have multiple items.
3414 has_multi_cases: bool,
3415 /// Information about the special prong.
3416 special_prongs: SpecialProngs,
3417 /// If true, at least one prong has an inline tag capture.
3418 any_has_tag_capture: bool,
3419 /// If true, at least one prong has a capture which may not
3420 /// be comptime-known via `inline`.
3421 any_non_inline_capture: bool,
3422 /// If true, at least one prong contains a `continue`.
3423 has_continue: bool,
3424 scalar_cases_len: ScalarCasesLen,
3385 pub const NonErr = packed struct(u32) {
3386 body_len: u29,
3387 capture: ProngInfo.Capture,
3388 operand_is_ref: bool,
3389 };
34253390
3426 pub const ScalarCasesLen = u25;
3391 pub const Else = packed struct(u32) {
3392 body_len: u27,
3393 capture: ProngInfo.Capture,
3394 is_inline: bool,
3395 has_tag_capture: bool,
3396 is_simple_noreturn: bool,
3397 };
34273398 };
34283399
3429 pub const MultiProng = struct {
3430 items: []const Ref,
3431 body: []const Index,
3400 pub const ItemInfo = packed struct(u32) {
3401 kind: ItemInfo.Kind,
3402 data: u30,
3403
3404 pub const Kind = enum(u2) {
3405 enum_literal,
3406 error_value,
3407 body_len,
3408 under,
3409 };
3410
3411 pub const Unwrapped = union(ItemInfo.Kind) {
3412 enum_literal: Zir.NullTerminatedString,
3413 error_value: Zir.NullTerminatedString,
3414 body_len: u32,
3415 under,
3416 };
3417
3418 pub fn wrap(unwrapped: ItemInfo.Unwrapped) ItemInfo {
3419 const data_uncasted: u32 = switch (unwrapped) {
3420 .enum_literal => |str_index| @intFromEnum(str_index),
3421 .error_value => |str_index| @intFromEnum(str_index),
3422 .body_len => |body_len| body_len,
3423 .under => 0,
3424 };
3425 return .{ .kind = unwrapped, .data = @intCast(data_uncasted) };
3426 }
3427
3428 pub fn unwrap(item_info: ItemInfo) ItemInfo.Unwrapped {
3429 return switch (item_info.kind) {
3430 .enum_literal => .{ .enum_literal = @enumFromInt(item_info.data) },
3431 .error_value => .{ .error_value = @enumFromInt(item_info.data) },
3432 .body_len => .{ .body_len = item_info.data },
3433 .under => .under,
3434 };
3435 }
3436
3437 pub fn bodyLen(item_info: ItemInfo) ?u32 {
3438 return if (item_info.kind == .body_len) item_info.data else null;
3439 }
34323440 };
3441
3442 pub const Kind = enum { default, ref, err_union };
34333443 };
34343444
34353445 pub const ArrayInitRefTy = struct {
......@@ -4004,69 +4014,6 @@ pub const Inst = struct {
40044014 };
40054015};
40064016
4007pub const SpecialProngs = enum(u3) {
4008 none = 0b000,
4009 /// Simple `else` prong.
4010 /// `else => {},`
4011 @"else" = 0b001,
4012 /// Simple `_` prong.
4013 /// `_ => {},`
4014 under = 0b010,
4015 /// Both an `else` and a `_` prong.
4016 /// `else => {},`
4017 /// `_ => {},`
4018 under_and_else = 0b011,
4019 /// `_` prong with 1 additional item.
4020 /// `a, _ => {},`
4021 under_one_item = 0b100,
4022 /// Both an `else` and a `_` prong with 1 additional item.
4023 /// `else => {},`
4024 /// `a, _ => {},`
4025 under_one_item_and_else = 0b101,
4026 /// `_` prong with >1 additional items.
4027 /// `a, _, b => {},`
4028 under_many_items = 0b110,
4029 /// Both an `else` and a `_` prong with >1 additional items.
4030 /// `else => {},`
4031 /// `a, _, b => {},`
4032 under_many_items_and_else = 0b111,
4033
4034 pub const AdditionalItems = enum(u3) {
4035 none = @intFromEnum(SpecialProngs.under),
4036 one = @intFromEnum(SpecialProngs.under_one_item),
4037 many = @intFromEnum(SpecialProngs.under_many_items),
4038 };
4039
4040 pub fn init(has_else: bool, has_under: bool, additional_items: AdditionalItems) SpecialProngs {
4041 const else_bit: u3 = @intFromBool(has_else);
4042 const under_bits: u3 = if (has_under)
4043 @intFromEnum(additional_items)
4044 else
4045 @intFromEnum(SpecialProngs.none);
4046 return @enumFromInt(else_bit | under_bits);
4047 }
4048
4049 pub fn hasElse(special_prongs: SpecialProngs) bool {
4050 return (@intFromEnum(special_prongs) & 0b001) != 0;
4051 }
4052
4053 pub fn hasUnder(special_prongs: SpecialProngs) bool {
4054 return (@intFromEnum(special_prongs) & 0b110) != 0;
4055 }
4056
4057 pub fn hasAdditionalItems(special_prongs: SpecialProngs) bool {
4058 return (@intFromEnum(special_prongs) & 0b100) != 0;
4059 }
4060
4061 pub fn hasOneAdditionalItem(special_prongs: SpecialProngs) bool {
4062 return (@intFromEnum(special_prongs) & 0b110) == @intFromEnum(SpecialProngs.under_one_item);
4063 }
4064
4065 pub fn hasManyAdditionalItems(special_prongs: SpecialProngs) bool {
4066 return (@intFromEnum(special_prongs) & 0b110) == @intFromEnum(SpecialProngs.under_many_items);
4067 }
4068};
4069
40704017pub const DeclIterator = struct {
40714018 extra_index: u32,
40724019 decls_remaining: u32,
......@@ -4842,8 +4789,45 @@ fn findTrackableInner(
48424789 const body = zir.bodySlice(extra.end, extra.data.body_len);
48434790 try zir.findTrackableBody(gpa, contents, defers, body);
48444791 },
4845 .switch_block, .switch_block_ref => return zir.findTrackableSwitch(gpa, contents, defers, inst, .normal),
4846 .switch_block_err_union => return zir.findTrackableSwitch(gpa, contents, defers, inst, .err_union),
4792
4793 .switch_block,
4794 .switch_block_ref,
4795 .switch_block_err_union,
4796 => {
4797 const zir_switch = zir.getSwitchBlock(inst);
4798 if (zir_switch.non_err_case) |non_err_case| {
4799 try zir.findTrackableBody(gpa, contents, defers, non_err_case.body);
4800 }
4801 if (zir_switch.else_case) |else_case| {
4802 try zir.findTrackableBody(gpa, contents, defers, else_case.body);
4803 }
4804 var extra_index = zir_switch.end;
4805 var case_it = zir_switch.iterateCases();
4806 while (case_it.next()) |case| {
4807 const prong_body = zir.bodySlice(extra_index, case.prong_info.body_len);
4808 extra_index += prong_body.len;
4809 try zir.findTrackableBody(gpa, contents, defers, prong_body);
4810 for (case.item_infos) |item_info| {
4811 if (item_info.bodyLen()) |body_len| {
4812 const item_body = zir.bodySlice(extra_index, body_len);
4813 extra_index += item_body.len;
4814 try zir.findTrackableBody(gpa, contents, defers, item_body);
4815 }
4816 }
4817 for (case.range_infos) |range_info| {
4818 if (range_info[0].bodyLen()) |body_len| {
4819 const first_body = zir.bodySlice(extra_index, body_len);
4820 extra_index += first_body.len;
4821 try zir.findTrackableBody(gpa, contents, defers, first_body);
4822 }
4823 if (range_info[1].bodyLen()) |body_len| {
4824 const last_body = zir.bodySlice(extra_index, body_len);
4825 extra_index += last_body.len;
4826 try zir.findTrackableBody(gpa, contents, defers, last_body);
4827 }
4828 }
4829 }
4830 },
48474831
48484832 .suspend_block => @panic("TODO iterate suspend block"),
48494833
......@@ -4890,119 +4874,6 @@ fn findTrackableInner(
48904874 }
48914875}
48924876
4893fn findTrackableSwitch(
4894 zir: Zir,
4895 gpa: Allocator,
4896 contents: *DeclContents,
4897 defers: *std.AutoHashMapUnmanaged(u32, void),
4898 inst: Inst.Index,
4899 /// Distinguishes between `switch_block[_ref]` and `switch_block_err_union`.
4900 comptime kind: enum { normal, err_union },
4901) Allocator.Error!void {
4902 const inst_data = zir.instructions.items(.data)[@intFromEnum(inst)].pl_node;
4903 const extra = zir.extraData(switch (kind) {
4904 .normal => Inst.SwitchBlock,
4905 .err_union => Inst.SwitchBlockErrUnion,
4906 }, inst_data.payload_index);
4907
4908 var extra_index: usize = extra.end;
4909
4910 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
4911 const multi_cases_len = zir.extra[extra_index];
4912 extra_index += 1;
4913 break :blk multi_cases_len;
4914 } else 0;
4915
4916 if (switch (kind) {
4917 .normal => extra.data.bits.any_has_tag_capture,
4918 .err_union => extra.data.bits.any_uses_err_capture,
4919 }) {
4920 extra_index += 1;
4921 }
4922
4923 const has_special = switch (kind) {
4924 .normal => extra.data.bits.special_prongs != .none,
4925 .err_union => has_special: {
4926 // Handle `non_err_body` first.
4927 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
4928 extra_index += 1;
4929 const body = zir.bodySlice(extra_index, prong_info.body_len);
4930 extra_index += body.len;
4931
4932 try zir.findTrackableBody(gpa, contents, defers, body);
4933
4934 break :has_special extra.data.bits.has_else;
4935 },
4936 };
4937
4938 if (has_special) {
4939 const has_else = if (kind == .normal)
4940 extra.data.bits.special_prongs.hasElse()
4941 else
4942 true;
4943 if (has_else) {
4944 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
4945 extra_index += 1;
4946 const body = zir.bodySlice(extra_index, prong_info.body_len);
4947 extra_index += body.len;
4948
4949 try zir.findTrackableBody(gpa, contents, defers, body);
4950 }
4951 if (kind == .normal) {
4952 const special_prongs = extra.data.bits.special_prongs;
4953
4954 if (special_prongs.hasUnder()) {
4955 var trailing_items_len: u32 = 0;
4956 if (special_prongs.hasOneAdditionalItem()) {
4957 extra_index += 1;
4958 } else if (special_prongs.hasManyAdditionalItems()) {
4959 const items_len = zir.extra[extra_index];
4960 extra_index += 1;
4961 const ranges_len = zir.extra[extra_index];
4962 extra_index += 1;
4963 trailing_items_len = items_len + ranges_len * 2;
4964 }
4965 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
4966 extra_index += 1 + trailing_items_len;
4967 const body = zir.bodySlice(extra_index, prong_info.body_len);
4968 extra_index += body.len;
4969
4970 try zir.findTrackableBody(gpa, contents, defers, body);
4971 }
4972 }
4973 }
4974
4975 {
4976 const scalar_cases_len = extra.data.bits.scalar_cases_len;
4977 for (0..scalar_cases_len) |_| {
4978 extra_index += 1;
4979 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
4980 extra_index += 1;
4981 const body = zir.bodySlice(extra_index, prong_info.body_len);
4982 extra_index += body.len;
4983
4984 try zir.findTrackableBody(gpa, contents, defers, body);
4985 }
4986 }
4987 {
4988 for (0..multi_cases_len) |_| {
4989 const items_len = zir.extra[extra_index];
4990 extra_index += 1;
4991 const ranges_len = zir.extra[extra_index];
4992 extra_index += 1;
4993 const prong_info: Inst.SwitchBlock.ProngInfo = @bitCast(zir.extra[extra_index]);
4994 extra_index += 1;
4995
4996 extra_index += items_len + ranges_len * 2;
4997
4998 const body = zir.bodySlice(extra_index, prong_info.body_len);
4999 extra_index += body.len;
5000
5001 try zir.findTrackableBody(gpa, contents, defers, body);
5002 }
5003 }
5004}
5005
50064877fn findTrackableBody(
50074878 zir: Zir,
50084879 gpa: Allocator,
......@@ -5337,6 +5208,241 @@ pub fn getAssociatedSrcHash(zir: Zir, inst: Zir.Inst.Index) ?std.zig.SrcHash {
53375208 }
53385209}
53395210
5211pub fn getSwitchBlock(zir: *const Zir, switch_inst: Inst.Index) UnwrappedSwitchBlock {
5212 const has_non_err = switch (zir.instructions.items(.tag)[@intFromEnum(switch_inst)]) {
5213 .switch_block, .switch_block_ref => false,
5214 .switch_block_err_union => true,
5215 else => unreachable,
5216 };
5217 const inst_data = zir.instructions.items(.data)[@intFromEnum(switch_inst)].pl_node;
5218 const extra = zir.extraData(Inst.SwitchBlock, inst_data.payload_index);
5219 const bits = extra.data.bits;
5220 var extra_index = extra.end;
5221 const multi_cases_len = if (bits.has_multi_cases) len: {
5222 const multi_cases_len = zir.extra[extra_index];
5223 extra_index += 1;
5224 break :len multi_cases_len;
5225 } else 0;
5226 const payload_capture_placeholder: Inst.OptionalIndex = if (bits.payload_capture_inst_is_placeholder) inst: {
5227 const inst: Inst.Index = @enumFromInt(zir.extra[extra_index]);
5228 extra_index += 1;
5229 break :inst inst.toOptional();
5230 } else .none;
5231 const tag_capture_placeholder: Inst.OptionalIndex = if (bits.tag_capture_inst_is_placeholder) inst: {
5232 const inst: Inst.Index = @enumFromInt(zir.extra[extra_index]);
5233 extra_index += 1;
5234 break :inst inst.toOptional();
5235 } else .none;
5236 const catch_or_if_src_node_offset: Ast.Node.OptionalOffset = if (has_non_err) node_offset: {
5237 const node_offset: Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(zir.extra[extra_index])));
5238 extra_index += 1;
5239 break :node_offset node_offset.toOptional();
5240 } else .none;
5241 const non_err_info: Inst.SwitchBlock.ProngInfo.NonErr = if (has_non_err) non_err_info: {
5242 const non_err_info: Inst.SwitchBlock.ProngInfo.NonErr = @bitCast(zir.extra[extra_index]);
5243 extra_index += 1;
5244 break :non_err_info non_err_info;
5245 } else undefined;
5246 const else_info: Inst.SwitchBlock.ProngInfo.Else = if (bits.has_else) else_info: {
5247 const else_info: Inst.SwitchBlock.ProngInfo.Else = @bitCast(zir.extra[extra_index]);
5248 extra_index += 1;
5249 break :else_info else_info;
5250 } else undefined;
5251 const scalar_cases_len: u32 = bits.scalar_cases_len;
5252 const prong_infos: []const Inst.SwitchBlock.ProngInfo =
5253 @ptrCast(zir.extra[extra_index..][0 .. scalar_cases_len + multi_cases_len]);
5254 extra_index += prong_infos.len;
5255 const multi_case_items_lens = zir.extra[extra_index..][0..multi_cases_len];
5256 extra_index += multi_case_items_lens.len;
5257 const multi_case_ranges_lens: ?[]const u32 = if (bits.any_ranges) lens: {
5258 const multi_case_ranges_lens = zir.extra[extra_index..][0..multi_cases_len];
5259 extra_index += multi_case_ranges_lens.len;
5260 break :lens multi_case_ranges_lens;
5261 } else null;
5262 var total_items_len: usize = scalar_cases_len;
5263 for (multi_case_items_lens) |items_len| {
5264 total_items_len += items_len;
5265 }
5266 if (multi_case_ranges_lens) |ranges_lens| for (ranges_lens) |ranges_len| {
5267 total_items_len += 2 * ranges_len;
5268 };
5269 const item_infos: []const Inst.SwitchBlock.ItemInfo =
5270 @ptrCast(zir.extra[extra_index..][0..total_items_len]);
5271 extra_index += item_infos.len;
5272 const non_err_case: ?UnwrappedSwitchBlock.Case.NonErr = if (has_non_err) non_err_case: {
5273 const body = zir.bodySlice(extra_index, non_err_info.body_len);
5274 extra_index += body.len;
5275 break :non_err_case .{
5276 .body = body,
5277 .capture = non_err_info.capture,
5278 .operand_is_ref = non_err_info.operand_is_ref,
5279 };
5280 } else null;
5281 const else_case: ?UnwrappedSwitchBlock.Case.Else = if (bits.has_else) else_case: {
5282 const body = zir.bodySlice(extra_index, else_info.body_len);
5283 extra_index += body.len;
5284 break :else_case .{
5285 .index = .@"else",
5286 .body = body,
5287 .capture = else_info.capture,
5288 .is_inline = else_info.is_inline,
5289 .has_tag_capture = else_info.has_tag_capture,
5290 .is_simple_noreturn = else_info.is_simple_noreturn,
5291 };
5292 } else null;
5293 return .{
5294 .main_operand = extra.data.raw_operand,
5295 .switch_src_node_offset = inst_data.src_node,
5296 .catch_or_if_src_node_offset = catch_or_if_src_node_offset,
5297 .payload_capture_placeholder = payload_capture_placeholder,
5298 .tag_capture_placeholder = tag_capture_placeholder,
5299 .has_continue = bits.has_continue,
5300 .any_maybe_runtime_capture = bits.any_maybe_runtime_capture,
5301 .non_err_case = non_err_case,
5302 .else_case = else_case,
5303 .has_under = bits.has_under,
5304 .prong_infos = prong_infos,
5305 .multi_case_items_lens = multi_case_items_lens,
5306 .multi_case_ranges_lens = multi_case_ranges_lens,
5307 .item_infos = item_infos,
5308 .end = extra_index,
5309 };
5310}
5311
5312/// Trailing (starting at `end`):
5313/// 0. case_bodies: { // for each case in Case.Iterator.next()
5314/// prong_body: {
5315/// body_inst: Inst.Index, // for every case.prong_info.body_len,
5316/// }
5317/// item_body: { // for each body_len in case.item_infos
5318/// body_inst: Inst.Index, // for every body_len
5319/// }
5320/// range_bodies: { // for each .{first_info, last_info} in case.range_infos
5321/// first_body_inst: Inst.Index, // for every first_info.body_len
5322/// last_body_inst: Inst.Index, // for every last_info.body_len
5323/// }
5324/// }
5325pub const UnwrappedSwitchBlock = struct {
5326 /// Either `catch`/`if` or `switch` operand.
5327 main_operand: Inst.Ref,
5328 switch_src_node_offset: Ast.Node.Offset,
5329 catch_or_if_src_node_offset: Ast.Node.OptionalOffset,
5330 payload_capture_placeholder: Inst.OptionalIndex,
5331 tag_capture_placeholder: Inst.OptionalIndex,
5332 has_continue: bool,
5333 any_maybe_runtime_capture: bool,
5334 non_err_case: ?Case.NonErr,
5335 else_case: ?Case.Else,
5336 has_under: bool,
5337 // Refer to doc comment and `iterateCases` to access everything below correctly.
5338 prong_infos: []const Inst.SwitchBlock.ProngInfo,
5339 multi_case_items_lens: []const u32,
5340 multi_case_ranges_lens: ?[]const u32,
5341 item_infos: []const Inst.SwitchBlock.ItemInfo,
5342 end: usize,
5343
5344 pub fn anyRanges(unwrapped: *const UnwrappedSwitchBlock) bool {
5345 return unwrapped.multi_case_ranges_lens != null;
5346 }
5347
5348 pub fn scalarCasesLen(unwrapped: *const UnwrappedSwitchBlock) u32 {
5349 return @intCast(unwrapped.prong_infos.len - unwrapped.multi_case_items_lens.len);
5350 }
5351
5352 pub fn multiCasesLen(unwrapped: *const UnwrappedSwitchBlock) u32 {
5353 return @intCast(unwrapped.multi_case_items_lens.len);
5354 }
5355
5356 pub fn totalItemsLen(unwrapped: *const UnwrappedSwitchBlock) u32 {
5357 var total_items_len: u32 = @intCast(unwrapped.item_infos.len);
5358 if (unwrapped.multi_case_ranges_lens) |ranges_lens| {
5359 for (ranges_lens) |len| total_items_len -= len;
5360 }
5361 return total_items_len;
5362 }
5363
5364 pub const Case = struct {
5365 index: Case.Index,
5366 prong_info: Inst.SwitchBlock.ProngInfo,
5367 item_infos: []const Inst.SwitchBlock.ItemInfo,
5368 range_infos: []const [2]Inst.SwitchBlock.ItemInfo,
5369
5370 pub const Index = packed struct(u32) {
5371 kind: enum(u1) { scalar, multi },
5372 value: u31,
5373
5374 pub const @"else": Case.Index = .{
5375 .kind = .scalar,
5376 .value = std.math.maxInt(u31),
5377 };
5378 };
5379
5380 pub const NonErr = struct {
5381 body: []const Inst.Index,
5382 capture: Inst.SwitchBlock.ProngInfo.Capture,
5383 operand_is_ref: bool,
5384 };
5385
5386 pub const Else = struct {
5387 index: Case.Index,
5388 body: []const Inst.Index,
5389 capture: Inst.SwitchBlock.ProngInfo.Capture,
5390 is_inline: bool,
5391 has_tag_capture: bool,
5392 is_simple_noreturn: bool,
5393 };
5394
5395 pub const Iterator = struct {
5396 next_idx: u32,
5397 prong_infos: []const Inst.SwitchBlock.ProngInfo,
5398 multi_case_items_lens: []const u32,
5399 multi_case_ranges_lens: ?[]const u32,
5400 item_infos: []const Inst.SwitchBlock.ItemInfo,
5401
5402 pub fn next(it: *Iterator) ?Case {
5403 const idx = it.next_idx;
5404 if (idx == it.prong_infos.len) return null;
5405 it.next_idx += 1;
5406 const scalar_cases_len = it.prong_infos.len - it.multi_case_items_lens.len;
5407 return if (idx < scalar_cases_len) .{
5408 .index = .{
5409 .kind = .scalar,
5410 .value = @intCast(idx),
5411 },
5412 .prong_info = it.prong_infos[idx],
5413 .item_infos = it.itemInfos(1),
5414 .range_infos = &.{},
5415 } else .{
5416 .index = .{
5417 .kind = .multi,
5418 .value = @intCast(idx - scalar_cases_len),
5419 },
5420 .prong_info = it.prong_infos[idx],
5421 .item_infos = it.itemInfos(it.multi_case_items_lens[idx - scalar_cases_len]),
5422 .range_infos = if (it.multi_case_ranges_lens) |ranges_lens| b: {
5423 break :b @ptrCast(it.itemInfos(2 * ranges_lens[idx - scalar_cases_len]));
5424 } else &.{},
5425 };
5426 }
5427 fn itemInfos(it: *Iterator, count: u32) []const Inst.SwitchBlock.ItemInfo {
5428 const lens = it.item_infos[0..count];
5429 it.item_infos = it.item_infos[count..];
5430 return lens;
5431 }
5432 };
5433 };
5434
5435 pub fn iterateCases(unwrapped: UnwrappedSwitchBlock) Case.Iterator {
5436 return .{
5437 .next_idx = 0,
5438 .prong_infos = unwrapped.prong_infos,
5439 .multi_case_items_lens = unwrapped.multi_case_items_lens,
5440 .multi_case_ranges_lens = unwrapped.multi_case_ranges_lens,
5441 .item_infos = unwrapped.item_infos,
5442 };
5443 }
5444};
5445
53405446/// When the ZIR update tracking logic must be modified to consider new instructions,
53415447/// change this constant to trigger compile errors at all relevant locations.
53425448pub const inst_tracking_version = 0;
src/Air/Liveness.zig+23-23
......@@ -825,10 +825,10 @@ fn analyzeOperands(
825825
826826 // This logic must synchronize with `will_die_immediately` in `AnalyzeBigOperands.init`.
827827 const immediate_death = if (data.live_set.remove(inst)) blk: {
828 log.debug("[{}] %{d}: removed from live set", .{ pass, @intFromEnum(inst) });
828 log.debug("[{t}] {f}: removed from live set", .{ pass, inst });
829829 break :blk false;
830830 } else blk: {
831 log.debug("[{}] %{d}: immediate death", .{ pass, @intFromEnum(inst) });
831 log.debug("[{t}] {f}: immediate death", .{ pass, inst });
832832 break :blk true;
833833 };
834834
......@@ -849,7 +849,7 @@ fn analyzeOperands(
849849 const mask = @as(Bpi, 1) << @as(OperandInt, @intCast(i));
850850
851851 if ((try data.live_set.fetchPut(gpa, operand, {})) == null) {
852 log.debug("[{}] %{d}: added %{d} to live set (operand dies here)", .{ pass, @intFromEnum(inst), operand });
852 log.debug("[{t}] {f}: added {f} to live set (operand dies here)", .{ pass, inst, operand });
853853 tomb_bits |= mask;
854854 }
855855 }
......@@ -988,19 +988,19 @@ fn analyzeInstBlock(
988988 },
989989
990990 .main_analysis => {
991 log.debug("[{}] %{f}: block live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) });
991 log.debug("[{t}] {f}: block live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) });
992992 // We can move the live set because the body should have a noreturn
993993 // instruction which overrides the set.
994994 try data.block_scopes.put(gpa, inst, .{
995995 .live_set = data.live_set.move(),
996996 });
997997 defer {
998 log.debug("[{}] %{f}: popped block scope", .{ pass, inst });
998 log.debug("[{t}] {f}: popped block scope", .{ pass, inst });
999999 var scope = data.block_scopes.fetchRemove(inst).?.value;
10001000 scope.live_set.deinit(gpa);
10011001 }
10021002
1003 log.debug("[{}] %{f}: pushed new block scope", .{ pass, inst });
1003 log.debug("[{t}] {f}: pushed new block scope", .{ pass, inst });
10041004 try analyzeBody(a, pass, data, body);
10051005
10061006 // If the block is noreturn, block deaths not only aren't useful, they're impossible to
......@@ -1027,7 +1027,7 @@ fn analyzeInstBlock(
10271027 }
10281028 assert(measured_num == num_deaths); // post-live-set should be a subset of pre-live-set
10291029 try a.special.put(gpa, inst, extra_index);
1030 log.debug("[{}] %{f}: block deaths are {f}", .{
1030 log.debug("[{t}] {f}: block deaths are {f}", .{
10311031 pass,
10321032 inst,
10331033 fmtInstList(@ptrCast(a.extra.items[extra_index + 1 ..][0..num_deaths])),
......@@ -1064,7 +1064,7 @@ fn writeLoopInfo(
10641064 const block_inst = key.*;
10651065 a.extra.appendAssumeCapacity(@intFromEnum(block_inst));
10661066 }
1067 log.debug("[{}] %{f}: includes breaks to {f}", .{ LivenessPass.loop_analysis, inst, fmtInstSet(&data.breaks) });
1067 log.debug("[{t}] {f}: includes breaks to {f}", .{ LivenessPass.loop_analysis, inst, fmtInstSet(&data.breaks) });
10681068
10691069 // Now we put the live operands from the loop body in too
10701070 const num_live = data.live_set.count();
......@@ -1076,7 +1076,7 @@ fn writeLoopInfo(
10761076 const alive = key.*;
10771077 a.extra.appendAssumeCapacity(@intFromEnum(alive));
10781078 }
1079 log.debug("[{}] %{f}: maintain liveness of {f}", .{ LivenessPass.loop_analysis, inst, fmtInstSet(&data.live_set) });
1079 log.debug("[{t}] {f}: maintain liveness of {f}", .{ LivenessPass.loop_analysis, inst, fmtInstSet(&data.live_set) });
10801080
10811081 try a.special.put(gpa, inst, extra_index);
10821082
......@@ -1117,7 +1117,7 @@ fn resolveLoopLiveSet(
11171117 try data.live_set.ensureUnusedCapacity(gpa, @intCast(loop_live.len));
11181118 for (loop_live) |alive| data.live_set.putAssumeCapacity(alive, {});
11191119
1120 log.debug("[{}] %{f}: block live set is {f}", .{ LivenessPass.main_analysis, inst, fmtInstSet(&data.live_set) });
1120 log.debug("[{t}] {f}: block live set is {f}", .{ LivenessPass.main_analysis, inst, fmtInstSet(&data.live_set) });
11211121
11221122 for (breaks) |block_inst| {
11231123 // We might break to this block, so include every operand that the block needs alive
......@@ -1130,7 +1130,7 @@ fn resolveLoopLiveSet(
11301130 }
11311131 }
11321132
1133 log.debug("[{}] %{f}: loop live set is {f}", .{ LivenessPass.main_analysis, inst, fmtInstSet(&data.live_set) });
1133 log.debug("[{t}] {f}: loop live set is {f}", .{ LivenessPass.main_analysis, inst, fmtInstSet(&data.live_set) });
11341134}
11351135
11361136fn analyzeInstLoop(
......@@ -1168,7 +1168,7 @@ fn analyzeInstLoop(
11681168 .live_set = data.live_set.move(),
11691169 });
11701170 defer {
1171 log.debug("[{}] %{f}: popped loop block scop", .{ pass, inst });
1171 log.debug("[{t}] {f}: popped loop block scop", .{ pass, inst });
11721172 var scope = data.block_scopes.fetchRemove(inst).?.value;
11731173 scope.live_set.deinit(gpa);
11741174 }
......@@ -1269,13 +1269,13 @@ fn analyzeInstCondBr(
12691269 }
12701270 }
12711271
1272 log.debug("[{}] %{f}: 'then' branch mirrored deaths are {f}", .{ pass, inst, fmtInstList(then_mirrored_deaths.items) });
1273 log.debug("[{}] %{f}: 'else' branch mirrored deaths are {f}", .{ pass, inst, fmtInstList(else_mirrored_deaths.items) });
1272 log.debug("[{t}] {f}: 'then' branch mirrored deaths are {f}", .{ pass, inst, fmtInstList(then_mirrored_deaths.items) });
1273 log.debug("[{t}] {f}: 'else' branch mirrored deaths are {f}", .{ pass, inst, fmtInstList(else_mirrored_deaths.items) });
12741274
12751275 data.live_set.deinit(gpa);
12761276 data.live_set = then_live.move(); // Really the union of both live sets
12771277
1278 log.debug("[{}] %{f}: new live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) });
1278 log.debug("[{t}] {f}: new live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) });
12791279
12801280 // Write the mirrored deaths to `extra`
12811281 const then_death_count = @as(u32, @intCast(then_mirrored_deaths.items.len));
......@@ -1343,7 +1343,7 @@ fn analyzeInstSwitchBr(
13431343 });
13441344 }
13451345 defer if (is_dispatch_loop) {
1346 log.debug("[{}] %{f}: popped loop block scop", .{ pass, inst });
1346 log.debug("[{t}] {f}: popped loop block scope", .{ pass, inst });
13471347 var scope = data.block_scopes.fetchRemove(inst).?.value;
13481348 scope.live_set.deinit(gpa);
13491349 };
......@@ -1401,13 +1401,13 @@ fn analyzeInstSwitchBr(
14011401 }
14021402
14031403 for (mirrored_deaths, 0..) |mirrored, i| {
1404 log.debug("[{}] %{f}: case {} mirrored deaths are {f}", .{ pass, inst, i, fmtInstList(mirrored.items) });
1404 log.debug("[{t}] {f}: case {} mirrored deaths are {f}", .{ pass, inst, i, fmtInstList(mirrored.items) });
14051405 }
14061406
14071407 data.live_set.deinit(gpa);
14081408 data.live_set = all_alive.move();
14091409
1410 log.debug("[{}] %{f}: new live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) });
1410 log.debug("[{t}] {f}: new live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) });
14111411 }
14121412
14131413 const else_death_count = @as(u32, @intCast(mirrored_deaths[ncases].items.len));
......@@ -1506,7 +1506,7 @@ fn AnalyzeBigOperands(comptime pass: LivenessPass) type {
15061506
15071507 .main_analysis => {
15081508 if ((try big.data.live_set.fetchPut(gpa, operand, {})) == null) {
1509 log.debug("[{}] %{f}: added %{f} to live set (operand dies here)", .{ pass, big.inst, operand });
1509 log.debug("[{t}] {f}: added {f} to live set (operand dies here)", .{ pass, big.inst, operand });
15101510 big.extra_tombs[extra_byte] |= @as(u32, 1) << extra_bit;
15111511 }
15121512 },
......@@ -1568,9 +1568,9 @@ const FmtInstSet = struct {
15681568 return;
15691569 }
15701570 var it = val.set.keyIterator();
1571 try w.print("%{f}", .{it.next().?.*});
1571 try w.print("{f}", .{it.next().?.*});
15721572 while (it.next()) |key| {
1573 try w.print(" %{f}", .{key.*});
1573 try w.print(" {f}", .{key.*});
15741574 }
15751575 }
15761576};
......@@ -1587,9 +1587,9 @@ const FmtInstList = struct {
15871587 try w.writeAll("[no instructions]");
15881588 return;
15891589 }
1590 try w.print("%{f}", .{val.list[0]});
1590 try w.print("{f}", .{val.list[0]});
15911591 for (val.list[1..]) |inst| {
1592 try w.print(" %{f}", .{inst});
1592 try w.print(" {f}", .{inst});
15931593 }
15941594 }
15951595};
src/Air/Liveness/Verify.zig+16-11
......@@ -73,7 +73,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
7373 .trap, .unreach => {
7474 try self.verifyInstOperands(inst, .{ .none, .none, .none });
7575 // This instruction terminates the function, so everything should be dead
76 if (self.live.count() > 0) return invalid("%{f}: instructions still alive", .{inst});
76 if (self.live.count() > 0) return invalid("{f}: instructions still alive", .{inst});
7777 },
7878
7979 // unary
......@@ -166,7 +166,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
166166 const un_op = data[@intFromEnum(inst)].un_op;
167167 try self.verifyInstOperands(inst, .{ un_op, .none, .none });
168168 // This instruction terminates the function, so everything should be dead
169 if (self.live.count() > 0) return invalid("%{f}: instructions still alive", .{inst});
169 if (self.live.count() > 0) return invalid("{f}: instructions still alive", .{inst});
170170 },
171171 .dbg_var_ptr,
172172 .dbg_var_val,
......@@ -441,7 +441,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
441441 .repeat => {
442442 const repeat = data[@intFromEnum(inst)].repeat;
443443 const expected_live = self.loops.get(repeat.loop_inst) orelse
444 return invalid("%{d}: loop %{d} not in scope", .{ @intFromEnum(inst), @intFromEnum(repeat.loop_inst) });
444 return invalid("{f}: loop {f} not in scope", .{ inst, repeat.loop_inst });
445445
446446 try self.verifyMatchingLiveness(repeat.loop_inst, expected_live);
447447 },
......@@ -451,7 +451,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
451451 try self.verifyOperand(inst, br.operand, self.liveness.operandDies(inst, 0));
452452
453453 const expected_live = self.loops.get(br.block_inst) orelse
454 return invalid("%{d}: loop %{d} not in scope", .{ @intFromEnum(inst), @intFromEnum(br.block_inst) });
454 return invalid("{f}: loop {f} not in scope", .{ inst, br.block_inst });
455455
456456 try self.verifyMatchingLiveness(br.block_inst, expected_live);
457457 },
......@@ -487,7 +487,12 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
487487 if (ip.isNoReturn(block_ty.toIntern())) {
488488 assert(!self.blocks.contains(inst));
489489 } else {
490 var live = self.blocks.fetchRemove(inst).?.value;
490 var live = if (self.blocks.fetchRemove(inst)) |kv| kv.value else {
491 return invalid(
492 "{f}: block of type '{f}' not terminated correctly",
493 .{ inst, block_ty.fmtDebug() },
494 );
495 };
491496 defer live.deinit(self.gpa);
492497
493498 try self.verifyMatchingLiveness(inst, live);
......@@ -502,7 +507,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
502507
503508 // The same stuff should be alive after the loop as before it.
504509 const gop = try self.loops.getOrPut(self.gpa, inst);
505 if (gop.found_existing) return invalid("%{d}: loop already exists", .{@intFromEnum(inst)});
510 if (gop.found_existing) return invalid("{f}: loop already exists", .{inst});
506511 defer {
507512 var live = self.loops.fetchRemove(inst).?;
508513 live.value.deinit(self.gpa);
......@@ -551,7 +556,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
551556 // after the loop as before it.
552557 {
553558 const gop = try self.loops.getOrPut(self.gpa, inst);
554 if (gop.found_existing) return invalid("%{d}: loop already exists", .{@intFromEnum(inst)});
559 if (gop.found_existing) return invalid("{f}: loop already exists", .{inst});
555560 gop.value_ptr.* = self.live.move();
556561 }
557562 defer {
......@@ -606,11 +611,11 @@ fn verifyOperand(self: *Verify, inst: Air.Inst.Index, op_ref: Air.Inst.Ref, dies
606611 return;
607612 };
608613 if (dies) {
609 if (!self.live.remove(operand)) return invalid("%{f}: dead operand %{f} reused and killed again", .{
614 if (!self.live.remove(operand)) return invalid("{f}: dead operand {f} reused and killed again", .{
610615 inst, operand,
611616 });
612617 } else {
613 if (!self.live.contains(operand)) return invalid("%{f}: dead operand %{f} reused", .{ inst, operand });
618 if (!self.live.contains(operand)) return invalid("{f}: dead operand {f} reused", .{ inst, operand });
614619 }
615620}
616621
......@@ -635,9 +640,9 @@ fn verifyInst(self: *Verify, inst: Air.Inst.Index) Error!void {
635640}
636641
637642fn verifyMatchingLiveness(self: *Verify, block: Air.Inst.Index, live: LiveMap) Error!void {
638 if (self.live.count() != live.count()) return invalid("%{f}: different deaths across branches", .{block});
643 if (self.live.count() != live.count()) return invalid("{f}: different deaths across branches", .{block});
639644 var live_it = self.live.keyIterator();
640 while (live_it.next()) |live_inst| if (!live.contains(live_inst.*)) return invalid("%{f}: different deaths across branches", .{block});
645 while (live_it.next()) |live_inst| if (!live.contains(live_inst.*)) return invalid("{f}: different deaths across branches", .{block});
641646}
642647
643648fn invalid(comptime fmt: []const u8, args: anytype) error{LivenessInvalid} {
src/RangeSet.zig+66-66
......@@ -1,102 +1,92 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const Order = std.math.Order;
4
5const InternPool = @import("InternPool.zig");
6const Type = @import("Type.zig");
7const Value = @import("Value.zig");
8const Zcu = @import("Zcu.zig");
91const RangeSet = @This();
10const LazySrcLoc = Zcu.LazySrcLoc;
112
12zcu: *Zcu,
13ranges: std.array_list.Managed(Range),
3ranges: std.ArrayList(Range),
144
155pub const Range = struct {
16 first: InternPool.Index,
17 last: InternPool.Index,
6 first: Value,
7 last: Value,
188 src: LazySrcLoc,
199};
2010
21pub fn init(allocator: std.mem.Allocator, zcu: *Zcu) RangeSet {
22 return .{
23 .zcu = zcu,
24 .ranges = std.array_list.Managed(Range).init(allocator),
25 };
26}
11pub const empty: RangeSet = .{ .ranges = .empty };
2712
28pub fn deinit(self: *RangeSet) void {
29 self.ranges.deinit();
13pub fn deinit(self: *RangeSet, allocator: Allocator) void {
14 self.ranges.deinit(allocator);
15 self.* = undefined;
3016}
3117
32pub fn add(
33 self: *RangeSet,
34 first: InternPool.Index,
35 last: InternPool.Index,
36 src: LazySrcLoc,
37) !?LazySrcLoc {
38 const zcu = self.zcu;
39 const ip = &zcu.intern_pool;
40
41 const ty = ip.typeOf(first);
42 assert(ty == ip.typeOf(last));
18pub fn ensureUnusedCapacity(self: *RangeSet, allocator: Allocator, additional_count: usize) Allocator.Error!void {
19 return self.ranges.ensureUnusedCapacity(allocator, additional_count);
20}
4321
44 for (self.ranges.items) |range| {
45 assert(ty == ip.typeOf(range.first));
46 assert(ty == ip.typeOf(range.last));
22pub fn addAssumeCapacity(set: *RangeSet, new: Range, ty: Type, zcu: *Zcu) ?LazySrcLoc {
23 assert(new.first.typeOf(zcu).eql(ty, zcu));
24 assert(new.last.typeOf(zcu).eql(ty, zcu));
4725
48 if (Value.fromInterned(last).compareScalar(.gte, Value.fromInterned(range.first), Type.fromInterned(ty), zcu) and
49 Value.fromInterned(first).compareScalar(.lte, Value.fromInterned(range.last), Type.fromInterned(ty), zcu))
26 for (set.ranges.items) |range| {
27 if (new.last.compareScalar(.gte, range.first, ty, zcu) and
28 new.first.compareScalar(.lte, range.last, ty, zcu))
5029 {
5130 return range.src; // They overlap.
5231 }
5332 }
54
55 try self.ranges.append(.{
56 .first = first,
57 .last = last,
58 .src = src,
59 });
33 set.ranges.appendAssumeCapacity(new);
6034 return null;
6135}
6236
63/// Assumes a and b do not overlap
64fn lessThan(zcu: *Zcu, a: Range, b: Range) bool {
65 const ty = Type.fromInterned(zcu.intern_pool.typeOf(a.first));
66 return Value.fromInterned(a.first).compareScalar(.lt, Value.fromInterned(b.first), ty, zcu);
37pub fn add(set: *RangeSet, allocator: Allocator, new: Range, ty: Type, zcu: *Zcu) Allocator.Error!?LazySrcLoc {
38 try set.ensureUnusedCapacity(allocator, 1);
39 return set.addAssumeCapacity(new, ty, zcu);
6740}
6841
69pub fn spans(self: *RangeSet, first: InternPool.Index, last: InternPool.Index) !bool {
70 const zcu = self.zcu;
71 const ip = &zcu.intern_pool;
72 assert(ip.typeOf(first) == ip.typeOf(last));
73
74 if (self.ranges.items.len == 0)
75 return false;
76
77 std.mem.sort(Range, self.ranges.items, zcu, lessThan);
42const SortCtx = struct {
43 ty: Type,
44 zcu: *Zcu,
45};
46/// Assumes a and b do not overlap
47fn lessThan(ctx: SortCtx, a: Range, b: Range) bool {
48 return a.first.compareScalar(.lt, b.first, ctx.ty, ctx.zcu);
49}
7850
79 if (self.ranges.items[0].first != first or
80 self.ranges.items[self.ranges.items.len - 1].last != last)
51pub fn spans(
52 set: *RangeSet,
53 allocator: Allocator,
54 first: Value,
55 last: Value,
56 ty: Type,
57 zcu: *Zcu,
58) Allocator.Error!bool {
59 assert(first.typeOf(zcu).eql(ty, zcu));
60 assert(last.typeOf(zcu).eql(ty, zcu));
61 if (set.ranges.items.len == 0) return false;
62
63 std.mem.sort(Range, set.ranges.items, SortCtx{ .ty = ty, .zcu = zcu }, lessThan);
64
65 if (!set.ranges.items[0].first.eql(first, ty, zcu) or
66 !set.ranges.items[set.ranges.items.len - 1].last.eql(last, ty, zcu))
8167 {
8268 return false;
8369 }
8470
85 var space: InternPool.Key.Int.Storage.BigIntSpace = undefined;
71 const limbs = try allocator.alloc(
72 std.math.big.Limb,
73 std.math.big.int.calcTwosCompLimbCount(ty.intInfo(zcu).bits),
74 );
75 defer allocator.free(limbs);
76 var counter: std.math.big.int.Mutable = .init(limbs, 0);
8677
87 var counter = try std.math.big.int.Managed.init(self.ranges.allocator);
88 defer counter.deinit();
78 var space: InternPool.Key.Int.Storage.BigIntSpace = undefined;
8979
9080 // look for gaps
91 for (self.ranges.items[1..], 0..) |cur, i| {
81 for (set.ranges.items[1..], 0..) |cur, i| {
9282 // i starts counting from the second item.
93 const prev = self.ranges.items[i];
83 const prev = set.ranges.items[i];
9484
9585 // prev.last + 1 == cur.first
96 try counter.copy(Value.fromInterned(prev.last).toBigInt(&space, zcu));
97 try counter.addScalar(&counter, 1);
86 counter.copy(prev.last.toBigInt(&space, zcu));
87 counter.addScalar(counter.toConst(), 1);
9888
99 const cur_start_int = Value.fromInterned(cur.first).toBigInt(&space, zcu);
89 const cur_start_int = cur.first.toBigInt(&space, zcu);
10090 if (!cur_start_int.eql(counter.toConst())) {
10191 return false;
10292 }
......@@ -104,3 +94,13 @@ pub fn spans(self: *RangeSet, first: InternPool.Index, last: InternPool.Index) !
10494
10595 return true;
10696}
97
98const std = @import("std");
99const assert = std.debug.assert;
100const Allocator = std.mem.Allocator;
101
102const InternPool = @import("InternPool.zig");
103const Type = @import("Type.zig");
104const Value = @import("Value.zig");
105const Zcu = @import("Zcu.zig");
106const LazySrcLoc = Zcu.LazySrcLoc;
src/Sema.zig+2613-3049
......@@ -509,7 +509,7 @@ pub const Block = struct {
509509 .parent = parent,
510510 .sema = parent.sema,
511511 .namespace = parent.namespace,
512 .instructions = .{},
512 .instructions = .empty,
513513 .label = null,
514514 .inlining = parent.inlining,
515515 .comptime_reason = parent.comptime_reason,
......@@ -1927,9 +1927,8 @@ fn analyzeBodyInner(
19271927 break :msg msg;
19281928 });
19291929 }
1930 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
1931 assert(is_non_err != .none);
1932 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, null);
1930 const is_non_err_val = (try sema.resolveIsNonErrVal(block, operand_src, err_union)).?;
1931 if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, operand_src, null);
19331932 if (is_non_err_val.toBool()) {
19341933 break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);
19351934 }
......@@ -1945,9 +1944,8 @@ fn analyzeBodyInner(
19451944 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
19461945 const operand = try sema.resolveInst(extra.data.operand);
19471946 const err_union = try sema.analyzeLoad(block, src, operand, operand_src);
1948 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
1949 assert(is_non_err != .none);
1950 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, null);
1947 const is_non_err_val = (try sema.resolveIsNonErrVal(block, operand_src, err_union)).?;
1948 if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, operand_src, null);
19511949 if (is_non_err_val.toBool()) {
19521950 break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
19531951 }
......@@ -6498,26 +6496,23 @@ fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) Com
64986496
64996497 switch (sema.code.instructions.items(.tag)[@intFromEnum(switch_inst)]) {
65006498 .switch_block, .switch_block_ref => {},
6499 .switch_block_err_union => unreachable, // wrong code path!
65016500 else => unreachable, // assertion failure
65026501 }
65036502
6504 const switch_payload_index = sema.code.instructions.items(.data)[@intFromEnum(switch_inst)].pl_node.payload_index;
6505 const switch_operand_ref = sema.code.extraData(Zir.Inst.SwitchBlock, switch_payload_index).data.operand;
6506 const switch_operand_ty = sema.typeOf(try sema.resolveInst(switch_operand_ref));
6507
6508 const operand = try sema.coerce(start_block, switch_operand_ty, uncoerced_operand, operand_src);
6509
6503 const operand_ty = (try sema.resolveInst(switch_inst.toRef())).toType();
6504 const operand = try sema.coerce(start_block, operand_ty, uncoerced_operand, operand_src);
65106505 try sema.validateRuntimeValue(start_block, operand_src, operand);
65116506
65126507 // We want to generate a `switch_dispatch` instruction with the switch condition,
65136508 // possibly preceded by a store to the stack alloc containing the raw operand.
65146509 // However, to avoid too much special-case state in Sema, this is handled by the
6515 // `switch` lowering logic. As such, we will find the `Block` corresponding to the
6516 // parent `switch_block[_ref]` instruction, create a dummy `br`, and add a merge
6517 // to signal to the switch logic to rewrite this into an appropriate dispatch.
6510 // `switch` lowering logic. As such, we will find the `Block` corresponding to
6511 // the parent `switch_block[_ref]` instruction, create a dummy `br`, and add a
6512 // merge to signal to the switch logic to rewrite this into an appropriate dispatch.
65186513
65196514 var block = start_block;
6520 while (true) {
6515 while (true) : (block = block.parent.?) {
65216516 if (block.label) |label| {
65226517 if (label.zir_block == switch_inst) {
65236518 const br_ref = try start_block.addBr(label.merges.block_inst, operand);
......@@ -6531,7 +6526,6 @@ fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) Com
65316526 return;
65326527 }
65336528 }
6534 block = block.parent.?;
65356529 }
65366530}
65376531
......@@ -8485,8 +8479,20 @@ fn zirDeclLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index, do_coerce: b
84858479 sema.code.nullTerminatedString(extra.field_name_start),
84868480 .no_embedded_nulls,
84878481 );
8488
84898482 const orig_ty: Type = try sema.resolveTypeOrPoison(block, src, extra.lhs) orelse .generic_poison;
8483 return sema.analyzeDeclLiteral(block, src, name, orig_ty, do_coerce);
8484}
8485
8486fn analyzeDeclLiteral(
8487 sema: *Sema,
8488 block: *Block,
8489 src: LazySrcLoc,
8490 name: InternPool.NullTerminatedString,
8491 orig_ty: Type,
8492 do_coerce: bool,
8493) CompileError!Air.Inst.Ref {
8494 const pt = sema.pt;
8495 const zcu = pt.zcu;
84908496
84918497 const uncoerced_result = res: {
84928498 if (orig_ty.toIntern() == .generic_poison_type) {
......@@ -8960,6 +8966,7 @@ fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air
89608966 const result_ty = operand_ty.errorUnionSet(zcu);
89618967
89628968 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
8969 if (val.getErrorName(zcu) == .none) return .unreachable_value;
89638970 return Air.internedToRef((try pt.intern(.{ .err = .{
89648971 .ty = result_ty.toIntern(),
89658972 .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,
......@@ -8997,7 +9004,7 @@ fn analyzeErrUnionCodePtr(sema: *Sema, block: *Block, src: LazySrcLoc, operand:
89979004
89989005 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
89999006 if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| {
9000 assert(val.getErrorName(zcu) != .none);
9007 if (val.getErrorName(zcu) == .none) return .unreachable_value;
90019008 return Air.internedToRef((try pt.intern(.{ .err = .{
90029009 .ty = result_ty.toIntern(),
90039010 .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,
......@@ -10519,1284 +10526,1581 @@ fn zirSliceSentinelTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
1051910526 return Air.internedToRef(sentinel_ty.toIntern());
1052010527}
1052110528
10522/// Holds common data used when analyzing or resolving switch prong bodies,
10523/// including setting up captures.
10524const SwitchProngAnalysis = struct {
10525 sema: *Sema,
10526 /// The block containing the `switch_block` itself.
10527 parent_block: *Block,
10528 operand: Operand,
10529 /// If this switch is on an error set, this is the type to assign to the
10530 /// `else` prong. If `null`, the prong should be unreachable.
10531 else_error_ty: ?Type,
10532 /// The index of the `switch_block` instruction itself.
10533 switch_block_inst: Zir.Inst.Index,
10534 /// The dummy index into which inline tag captures should be placed. May be
10535 /// undefined if no prong has a tag capture.
10536 tag_capture_inst: Zir.Inst.Index,
10537
10538 const Operand = union(enum) {
10539 /// This switch will be dispatched only once, with the given operand.
10540 simple: struct {
10541 /// The raw switch operand value. Always defined.
10542 by_val: Air.Inst.Ref,
10543 /// The switch operand *pointer*. Defined only if there is a prong
10544 /// with a by-ref capture.
10545 by_ref: Air.Inst.Ref,
10546 /// The switch condition value. For unions, `operand` is the union
10547 /// and `cond` is its enum tag value.
10548 cond: Air.Inst.Ref,
10549 },
10550 /// This switch may be dispatched multiple times with `continue` syntax.
10551 /// As such, the operand is stored in an alloc if needed.
10552 loop: struct {
10553 /// The `alloc` containing the `switch` operand for the active dispatch.
10554 /// Each prong must load from this `alloc` to get captures.
10555 /// If there are no captures, this may be undefined.
10556 operand_alloc: Air.Inst.Ref,
10557 /// Whether `operand_alloc` contains a by-val operand or a by-ref
10558 /// operand.
10559 operand_is_ref: bool,
10560 /// The switch condition value for the *initial* dispatch. For
10561 /// unions, this is the enum tag value.
10562 init_cond: Air.Inst.Ref,
10563 },
10564 };
10565
10566 /// Resolve a switch prong which is determined at comptime to have no peers.
10567 /// Uses `resolveBlockBody`. Sets up captures as needed.
10568 fn resolveProngComptime(
10569 spa: SwitchProngAnalysis,
10570 child_block: *Block,
10571 prong_type: enum { normal, special },
10572 prong_body: []const Zir.Inst.Index,
10573 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
10574 /// Must use the `switch_capture` field in `offset`.
10575 capture_src: LazySrcLoc,
10576 /// The set of all values which can reach this prong. May be undefined
10577 /// if the prong is special or contains ranges.
10578 case_vals: []const Air.Inst.Ref,
10579 /// The inline capture of this prong. If this is not an inline prong,
10580 /// this is `.none`.
10581 inline_case_capture: Air.Inst.Ref,
10582 /// Whether this prong has an inline tag capture. If `true`, then
10583 /// `inline_case_capture` cannot be `.none`.
10584 has_tag_capture: bool,
10585 merges: *Block.Merges,
10586 ) CompileError!Air.Inst.Ref {
10587 const sema = spa.sema;
10588 const src = spa.parent_block.nodeOffset(
10589 sema.code.instructions.items(.data)[@intFromEnum(spa.switch_block_inst)].pl_node.src_node,
10590 );
10529fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
10530 const tracy = trace(@src());
10531 defer tracy.end();
1059110532
10592 // We can propagate `.cold` hints from this branch since it's comptime-known
10593 // to be taken from the parent branch.
10594 const parent_hint = sema.branch_hint;
10595 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
10533 const pt = sema.pt;
10534 const zcu = pt.zcu;
10535 const gpa = sema.gpa;
1059610536
10597 if (has_tag_capture) {
10598 const tag_ref = try spa.analyzeTagCapture(child_block, capture_src, inline_case_capture);
10599 sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref);
10600 }
10601 defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst));
10537 const zir_switch = sema.code.getSwitchBlock(inst);
10538 const src_node_offset = zir_switch.catch_or_if_src_node_offset.unwrap().?;
10539 const src = block.src(.{ .node_offset_main_token = src_node_offset });
10540 const operand_src = block.src(.{ .node_offset_if_cond = src_node_offset });
1060210541
10603 switch (capture) {
10604 .none => {
10605 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges);
10606 },
10542 assert(!zir_switch.has_continue); // wrong codepath!
1060710543
10608 .by_val, .by_ref => {
10609 const capture_ref = try spa.analyzeCapture(
10610 child_block,
10611 capture == .by_ref,
10612 prong_type == .special,
10613 capture_src,
10614 case_vals,
10615 inline_case_capture,
10616 );
10544 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
10545 try sema.air_instructions.append(gpa, .{
10546 .tag = .block,
10547 .data = undefined,
10548 });
10549 var label: Block.Label = .{
10550 .zir_block = inst,
10551 .merges = .{
10552 .src_locs = .{},
10553 .results = .{},
10554 .br_list = .{},
10555 .block_inst = block_inst,
10556 },
10557 };
10558 var child_block = block.makeSubBlock();
10559 child_block.label = &label;
10560 const merges = &child_block.label.?.merges;
10561 defer child_block.instructions.deinit(gpa);
10562 defer merges.deinit(gpa);
1061710563
10618 if (sema.typeOf(capture_ref).isNoReturn(sema.pt.zcu)) {
10619 // This prong should be unreachable!
10620 return .unreachable_value;
10621 }
10564 const non_err_case = zir_switch.non_err_case.?;
10565
10566 var non_err_block: Block = child_block.makeSubBlock();
10567 non_err_block.runtime_loop = null;
10568 non_err_block.runtime_cond = operand_src;
10569 non_err_block.runtime_index.increment();
10570 non_err_block.need_debug_scope = null;
10571 defer non_err_block.instructions.deinit(gpa);
10572
10573 var switch_block: Block = child_block.makeSubBlock();
10574 switch_block.runtime_loop = null;
10575 switch_block.runtime_cond = operand_src;
10576 switch_block.runtime_index.increment();
10577 switch_block.need_debug_scope = null;
10578 defer switch_block.instructions.deinit(gpa);
10579
10580 // We begin with unwrapping the error union we're switching on as necessary.
10581 // Then we analyze the non-error prong if it's not comptime-unreachable.
10582 // Lastly, we analyze the error prong(s) as a regular switch.
10583
10584 const raw_switch_operand, const non_err_cond, const non_err_hint = non_err: {
10585 const eu_maybe_ptr = try sema.resolveInst(zir_switch.main_operand);
10586 const err_union_ty: Type = err_union_ty: {
10587 const raw_operand_ty = sema.typeOf(eu_maybe_ptr);
10588 if (!non_err_case.operand_is_ref) break :err_union_ty raw_operand_ty;
10589 try sema.checkPtrOperand(block, operand_src, raw_operand_ty);
10590 break :err_union_ty raw_operand_ty.childType(zcu);
10591 };
10592 if (err_union_ty.zigTypeTag(zcu) != .error_union) {
10593 return sema.fail(block, operand_src, "expected error union type, found '{f}'", .{
10594 err_union_ty.fmt(pt),
10595 });
10596 }
1062210597
10623 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref);
10624 defer assert(sema.inst_map.remove(spa.switch_block_inst));
10598 const non_err_cond = if (non_err_case.operand_is_ref)
10599 try sema.analyzePtrIsNonErr(block, operand_src, eu_maybe_ptr)
10600 else
10601 try sema.analyzeIsNonErr(block, operand_src, eu_maybe_ptr);
1062510602
10626 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges);
10627 },
10628 }
10629 }
10603 const non_err_hint: std.builtin.BranchHint = hint: {
10604 // don't analyze the non-error body if it's unreachable
10605 if (non_err_cond == .bool_false) {
10606 break :hint undefined;
10607 }
1063010608
10631 /// Analyze a switch prong which may have peers at runtime.
10632 /// Uses `analyzeBodyRuntimeBreak`. Sets up captures as needed.
10633 /// Returns the `BranchHint` for the prong.
10634 fn analyzeProngRuntime(
10635 spa: SwitchProngAnalysis,
10636 case_block: *Block,
10637 prong_type: enum { normal, special },
10638 prong_body: []const Zir.Inst.Index,
10639 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
10640 /// Must use the `switch_capture` field in `offset`.
10641 capture_src: LazySrcLoc,
10642 /// The set of all values which can reach this prong. May be undefined
10643 /// if the prong is special or contains ranges.
10644 case_vals: []const Air.Inst.Ref,
10645 /// The inline capture of this prong. If this is not an inline prong,
10646 /// this is `.none`.
10647 inline_case_capture: Air.Inst.Ref,
10648 /// Whether this prong has an inline tag capture. If `true`, then
10649 /// `inline_case_capture` cannot be `.none`.
10650 has_tag_capture: bool,
10651 ) CompileError!std.builtin.BranchHint {
10652 const sema = spa.sema;
10609 const eu_payload: Air.Inst.Ref = switch (non_err_case.capture) {
10610 .by_val => try sema.analyzeErrUnionPayload(&non_err_block, src, err_union_ty, eu_maybe_ptr, operand_src, false),
10611 .by_ref => try sema.analyzeErrUnionPayloadPtr(&non_err_block, src, eu_maybe_ptr, false, false),
10612 .none => undefined,
10613 };
10614 if (non_err_case.capture != .none) sema.inst_map.putAssumeCapacity(inst, eu_payload);
10615 defer if (non_err_case.capture != .none) assert(sema.inst_map.remove(inst));
1065310616
10654 if (has_tag_capture) {
10655 const tag_ref = try spa.analyzeTagCapture(case_block, capture_src, inline_case_capture);
10656 sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref);
10657 }
10658 defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst));
10617 if (non_err_cond == .bool_true) {
10618 // Early return; we don't analyze the switch as it's unreachable.
10619 return sema.resolveBlockBody(block, src, &non_err_block, non_err_case.body, inst, merges);
10620 }
10621 break :hint try sema.analyzeBodyRuntimeBreak(&non_err_block, non_err_case.body);
10622 };
1065910623
10660 switch (capture) {
10661 .none => {
10662 return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
10663 },
10624 // Emit this into the switch block as it's our error case!
10625 const eu_code = if (non_err_case.operand_is_ref)
10626 try sema.analyzeErrUnionCodePtr(&switch_block, operand_src, eu_maybe_ptr)
10627 else
10628 try sema.analyzeErrUnionCode(&switch_block, operand_src, eu_maybe_ptr);
1066410629
10665 .by_val, .by_ref => {
10666 const capture_ref = try spa.analyzeCapture(
10667 case_block,
10668 capture == .by_ref,
10669 prong_type == .special,
10670 capture_src,
10671 case_vals,
10672 inline_case_capture,
10673 );
10630 break :non_err .{
10631 eu_code,
10632 non_err_cond,
10633 non_err_hint,
10634 };
10635 };
1067410636
10675 if (sema.typeOf(capture_ref).isNoReturn(sema.pt.zcu)) {
10676 // No need to analyze any further, the prong is unreachable
10677 return .none;
10678 }
10637 const validated_switch = try sema.validateSwitchBlock(block, raw_switch_operand, false, inst, &zir_switch);
1067910638
10680 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref);
10681 defer assert(sema.inst_map.remove(spa.switch_block_inst));
10639 const maybe_switch_ref: ?Air.Inst.Ref = ref: {
10640 // make err capture (i.e. switch operand) available to switch prong bodies
10641 sema.inst_map.putAssumeCapacityNoClobber(inst, raw_switch_operand);
10642 defer assert(sema.inst_map.remove(inst));
10643 break :ref try sema.analyzeSwitchBlock(block, &switch_block, raw_switch_operand, false, merges, inst, &zir_switch, &validated_switch);
10644 };
1068210645
10683 return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
10684 },
10685 }
10646 if (non_err_cond == .bool_false) {
10647 return maybe_switch_ref orelse {
10648 const switch_src = block.nodeOffset(zir_switch.switch_src_node_offset);
10649 return sema.resolveAnalyzedBlock(block, switch_src, &switch_block, merges, false);
10650 };
1068610651 }
1068710652
10688 fn analyzeTagCapture(
10689 spa: SwitchProngAnalysis,
10690 block: *Block,
10691 capture_src: LazySrcLoc,
10692 inline_case_capture: Air.Inst.Ref,
10693 ) CompileError!Air.Inst.Ref {
10694 const sema = spa.sema;
10695 const pt = sema.pt;
10696 const zcu = pt.zcu;
10697 const operand_ty = switch (spa.operand) {
10698 .simple => |s| sema.typeOf(s.by_val),
10699 .loop => |l| ty: {
10700 const alloc_ty = sema.typeOf(l.operand_alloc);
10701 const alloc_child = alloc_ty.childType(zcu);
10702 if (l.operand_is_ref) break :ty alloc_child.childType(zcu);
10703 break :ty alloc_child;
10704 },
10705 };
10706 if (operand_ty.zigTypeTag(zcu) != .@"union") {
10707 const tag_capture_src: LazySrcLoc = .{
10708 .base_node_inst = capture_src.base_node_inst,
10709 .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture },
10710 };
10711 return sema.fail(block, tag_capture_src, "cannot capture tag of non-union type '{f}'", .{
10712 operand_ty.fmt(pt),
10713 });
10653 if (maybe_switch_ref) |switch_ref| {
10654 if (sema.typeOf(switch_ref).isNoReturn(zcu)) {
10655 _ = try switch_block.addNoOp(.unreach);
10656 } else {
10657 const br_ref = try switch_block.addBr(merges.block_inst, switch_ref);
10658 try merges.results.append(gpa, switch_ref);
10659 try merges.br_list.append(gpa, br_ref.toIndex().?);
10660 try merges.src_locs.append(gpa, null);
1071410661 }
10715 assert(inline_case_capture != .none);
10716 return inline_case_capture;
1071710662 }
1071810663
10719 fn analyzeCapture(
10720 spa: SwitchProngAnalysis,
10721 block: *Block,
10722 capture_byref: bool,
10723 is_special_prong: bool,
10724 capture_src: LazySrcLoc,
10725 case_vals: []const Air.Inst.Ref,
10726 inline_case_capture: Air.Inst.Ref,
10727 ) CompileError!Air.Inst.Ref {
10728 const sema = spa.sema;
10729 const pt = sema.pt;
10730 const zcu = pt.zcu;
10731 const ip = &zcu.intern_pool;
10732
10733 const zir_datas = sema.code.instructions.items(.data);
10734 const switch_node_offset = zir_datas[@intFromEnum(spa.switch_block_inst)].pl_node.src_node;
10664 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
10665 non_err_block.instructions.items.len + switch_block.instructions.items.len);
10666 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
10667 .then_body_len = @intCast(non_err_block.instructions.items.len),
10668 .else_body_len = @intCast(switch_block.instructions.items.len),
10669 .branch_hints = .{
10670 .true = non_err_hint,
10671 .false = .unlikely, // errors are unlikely
10672 // Code coverage is desired for error handling.
10673 .then_cov = .poi,
10674 .else_cov = .poi,
10675 },
10676 });
10677 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(non_err_block.instructions.items));
10678 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(switch_block.instructions.items));
1073510679
10736 const operand_src = block.src(.{ .node_offset_switch_operand = switch_node_offset });
10680 _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
10681 .operand = non_err_cond,
10682 .payload = cond_br_payload,
10683 } } });
1073710684
10738 const operand_val, const operand_ptr = switch (spa.operand) {
10739 .simple => |s| .{ s.by_val, s.by_ref },
10740 .loop => |l| op: {
10741 const loaded = try sema.analyzeLoad(block, operand_src, l.operand_alloc, operand_src);
10742 if (l.operand_is_ref) {
10743 const by_val = try sema.analyzeLoad(block, operand_src, loaded, operand_src);
10744 break :op .{ by_val, loaded };
10745 } else {
10746 break :op .{ loaded, undefined };
10747 }
10748 },
10749 };
10685 return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false);
10686}
1075010687
10751 const operand_ty = sema.typeOf(operand_val);
10752 const operand_ptr_ty = if (capture_byref) sema.typeOf(operand_ptr) else undefined;
10688fn zirSwitchBlock(
10689 sema: *Sema,
10690 block: *Block,
10691 inst: Zir.Inst.Index,
10692 operand_is_ref: bool,
10693) CompileError!Air.Inst.Ref {
10694 const tracy = trace(@src());
10695 defer tracy.end();
10696 const zir_switch = sema.code.getSwitchBlock(inst);
1075310697
10754 if (inline_case_capture != .none) {
10755 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inline_case_capture, undefined) catch unreachable;
10756 if (operand_ty.zigTypeTag(zcu) == .@"union") {
10757 const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?);
10758 const union_obj = zcu.typeToUnion(operand_ty).?;
10759 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
10760 if (capture_byref) {
10761 const ptr_field_ty = try pt.ptrTypeSema(.{
10762 .child = field_ty.toIntern(),
10763 .flags = .{
10764 .is_const = !operand_ptr_ty.ptrIsMutable(zcu),
10765 .is_volatile = operand_ptr_ty.isVolatilePtr(zcu),
10766 .address_space = operand_ptr_ty.ptrAddressSpace(zcu),
10767 },
10768 });
10769 if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |union_ptr| {
10770 return Air.internedToRef((try union_ptr.ptrField(field_index, pt)).toIntern());
10771 }
10772 return block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty);
10773 } else {
10774 if (try sema.resolveDefinedValue(block, operand_src, operand_val)) |union_val| {
10775 const tag_and_val = ip.indexToKey(union_val.toIntern()).un;
10776 return Air.internedToRef(tag_and_val.val);
10777 }
10778 return block.addStructFieldVal(operand_val, field_index, field_ty);
10779 }
10780 } else if (capture_byref) {
10781 return sema.uavRef(item_val.toIntern());
10782 } else {
10783 return inline_case_capture;
10784 }
10785 }
10698 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
10699 try sema.air_instructions.append(sema.gpa, .{
10700 .tag = .block,
10701 .data = undefined,
10702 });
10703 var label: Block.Label = .{
10704 .zir_block = inst,
10705 .merges = .{
10706 .src_locs = .{},
10707 .results = .{},
10708 .br_list = .{},
10709 .block_inst = block_inst,
10710 },
10711 };
10712 var child_block = block.makeSubBlock();
10713 child_block.label = &label;
10714 const merges = &child_block.label.?.merges;
10715 defer child_block.instructions.deinit(sema.gpa);
10716 defer merges.deinit(sema.gpa);
1078610717
10787 if (is_special_prong) {
10788 if (capture_byref) {
10789 return operand_ptr;
10790 }
10718 const raw_operand = try sema.resolveInst(zir_switch.main_operand);
10719 const validated_switch = try sema.validateSwitchBlock(block, raw_operand, operand_is_ref, inst, &zir_switch);
10720 const maybe_ref = try sema.analyzeSwitchBlock(block, &child_block, raw_operand, operand_is_ref, merges, inst, &zir_switch, &validated_switch);
10721 return maybe_ref orelse {
10722 const src = block.nodeOffset(zir_switch.switch_src_node_offset);
10723 return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false);
10724 };
10725}
1079110726
10792 switch (operand_ty.zigTypeTag(zcu)) {
10793 .error_set => if (spa.else_error_ty) |ty| {
10794 return sema.bitCast(block, ty, operand_val, operand_src, null);
10795 } else {
10796 try sema.analyzeUnreachable(block, operand_src, false);
10797 return .unreachable_value;
10798 },
10799 else => return operand_val,
10800 }
10801 }
10727/// If the switch can be resolved to a value at comptime, this will return a `Ref`
10728/// that's never `.none`.
10729/// If not, this will return `null` and emit its instructions into `child_block`.
10730fn analyzeSwitchBlock(
10731 sema: *Sema,
10732 block: *Block,
10733 child_block: *Block,
10734 raw_operand: Air.Inst.Ref,
10735 operand_is_ref: bool,
10736 merges: *Block.Merges,
10737 switch_inst: Zir.Inst.Index,
10738 zir_switch: *const Zir.UnwrappedSwitchBlock,
10739 validated_switch: *const ValidatedSwitchBlock,
10740) CompileError!?Air.Inst.Ref {
10741 const pt = sema.pt;
10742 const zcu = pt.zcu;
10743 const gpa = sema.gpa;
1080210744
10803 switch (operand_ty.zigTypeTag(zcu)) {
10804 .@"union" => {
10805 const union_obj = zcu.typeToUnion(operand_ty).?;
10806 const first_item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, case_vals[0], undefined) catch unreachable;
10745 const src_node_offset = zir_switch.switch_src_node_offset;
10746 const src = block.nodeOffset(src_node_offset);
10747 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
1080710748
10808 const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?;
10809 const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]);
10749 const has_else = zir_switch.else_case != null;
10750 const has_under = zir_switch.has_under;
1081010751
10811 const field_indices = try sema.arena.alloc(u32, case_vals.len);
10812 for (case_vals, field_indices) |item, *field_idx| {
10813 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
10814 field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?;
10815 }
10752 const else_case = validated_switch.else_case;
1081610753
10817 // Fast path: if all the operands are the same type already, we don't need to hit
10818 // PTR! This will also allow us to emit simpler code.
10819 const same_types = for (field_indices[1..]) |field_idx| {
10820 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
10821 if (!field_ty.eql(first_field_ty, zcu)) break false;
10822 } else true;
10754 const operand: SwitchOperand, const operand_ty: Type, const maybe_operand_opv: ?Value, const item_ty: Type = operand: {
10755 const val, const ref = if (operand_is_ref)
10756 .{ try sema.analyzeLoad(block, src, raw_operand, operand_src), raw_operand }
10757 else
10758 .{ raw_operand, undefined };
1082310759
10824 const capture_ty = if (same_types) first_field_ty else capture_ty: {
10825 // We need values to run PTR on, so make a bunch of undef constants.
10826 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
10827 for (dummy_captures, field_indices) |*dummy, field_idx| {
10828 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
10829 dummy.* = try pt.undefRef(field_ty);
10830 }
10760 const operand_ty = sema.typeOf(val);
10761 const maybe_operand_opv = try sema.typeHasOnePossibleValue(operand_ty);
10762 const init_cond: Air.Inst.Ref, const item_ty: Type = switch (operand_ty.zigTypeTag(zcu)) {
10763 .@"union" => tag: {
10764 const tag_ty = operand_ty.unionTagType(zcu).?;
10765 const tag_val = try sema.unionToTag(block, tag_ty, val, operand_src);
10766 break :tag .{ tag_val, tag_ty };
10767 },
10768 else => .{
10769 if (maybe_operand_opv) |operand_opv| .fromValue(operand_opv) else val,
10770 operand_ty,
10771 },
10772 };
1083110773
10832 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
10833 for (case_srcs, 0..) |*case_src, i| {
10834 case_src.* = .{
10835 .base_node_inst = capture_src.base_node_inst,
10836 .offset = .{ .switch_case_item = .{
10837 .switch_node_offset = switch_node_offset,
10838 .case_idx = capture_src.offset.switch_capture.case_idx,
10839 .item_idx = .{ .kind = .single, .index = @intCast(i) },
10840 } },
10841 };
10842 }
10774 if (zir_switch.has_continue and !block.isComptime()) {
10775 const operand_alloc: Air.Inst.Ref = if (zir_switch.any_maybe_runtime_capture and
10776 maybe_operand_opv == null)
10777 alloc: {
10778 const operand_ptr_ty = try pt.singleMutPtrType(sema.typeOf(raw_operand));
10779 const operand_alloc = try block.addTy(.alloc, operand_ptr_ty);
10780 _ = try block.addBinOp(.store, operand_alloc, raw_operand);
10781 break :alloc operand_alloc;
10782 } else undefined;
10783 break :operand .{ .{ .loop = .{
10784 .operand_alloc = operand_alloc,
10785 .operand_is_ref = operand_is_ref,
10786 .init_cond = init_cond,
10787 } }, operand_ty, maybe_operand_opv, item_ty };
10788 } else {
10789 // We always use `simple` in the comptime/OPV case, because as far as the
10790 // dispatching logic is concerned, it really is dispatching a single prong.
10791 break :operand .{ .{ .simple = .{
10792 .by_val = val,
10793 .by_ref = ref,
10794 .cond = init_cond,
10795 } }, operand_ty, maybe_operand_opv, item_ty };
10796 }
10797 };
1084310798
10844 break :capture_ty sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) {
10845 error.AnalysisFail => {
10846 const msg = sema.err orelse return error.AnalysisFail;
10847 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{});
10848 return error.AnalysisFail;
10849 },
10850 else => |e| return e,
10851 };
10852 };
10799 const raw_operand_ty = sema.typeOf(raw_operand);
1085310800
10854 // By-reference captures have some further restrictions which make them easier to emit
10855 if (capture_byref) {
10856 const operand_ptr_info = operand_ptr_ty.ptrInfo(zcu);
10857 const capture_ptr_ty = resolve: {
10858 // By-ref captures of hetereogeneous types are only allowed if all field
10859 // pointer types are peer resolvable to each other.
10860 // We need values to run PTR on, so make a bunch of undef constants.
10861 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
10862 for (field_indices, dummy_captures) |field_idx, *dummy| {
10863 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
10864 const field_ptr_ty = try pt.ptrTypeSema(.{
10865 .child = field_ty.toIntern(),
10866 .flags = .{
10867 .is_const = operand_ptr_info.flags.is_const,
10868 .is_volatile = operand_ptr_info.flags.is_volatile,
10869 .address_space = operand_ptr_info.flags.address_space,
10870 .alignment = union_obj.fieldAlign(ip, field_idx),
10871 },
10872 });
10873 dummy.* = try pt.undefRef(field_ptr_ty);
10874 }
10875 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
10876 for (case_srcs, 0..) |*case_src, i| {
10877 case_src.* = .{
10878 .base_node_inst = capture_src.base_node_inst,
10879 .offset = .{ .switch_case_item = .{
10880 .switch_node_offset = switch_node_offset,
10881 .case_idx = capture_src.offset.switch_capture.case_idx,
10882 .item_idx = .{ .kind = .single, .index = @intCast(i) },
10883 } },
10884 };
10885 }
10801 const union_originally = operand_ty.zigTypeTag(zcu) == .@"union";
10802 const err_set = operand_ty.zigTypeTag(zcu) == .error_set;
1088610803
10887 break :resolve sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) {
10888 error.AnalysisFail => {
10889 const msg = sema.err orelse return error.AnalysisFail;
10890 try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{});
10891 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{});
10892 return error.AnalysisFail;
10893 },
10894 else => |e| return e,
10895 };
10896 };
10804 if (item_ty.zigTypeTag(zcu) == .@"enum" and
10805 validated_switch.seen_enum_fields.len == 0 and
10806 !operand_ty.isNonexhaustiveEnum(zcu))
10807 {
10808 return .void_value; // switch on empty enum/union
10809 }
1089710810
10898 if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |op_ptr_val| {
10899 if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty);
10900 const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt);
10901 return Air.internedToRef((try pt.getCoerced(field_ptr_val, capture_ptr_ty)).toIntern());
10902 }
10811 const cond_ref = switch (operand) {
10812 .simple => |s| s.cond,
10813 .loop => |l| l.init_cond,
10814 };
1090310815
10904 try sema.requireRuntimeBlock(block, operand_src, null);
10905 return block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty);
10906 }
10816 // We treat `else` and `_` the same, except if both are present.
10817 const else_is_named_only = has_else and has_under;
10818 const catch_all_case: CatchAllSwitchCase =
10819 if (has_under) .under else if (has_else) .@"else" else .none;
1090710820
10908 if (try sema.resolveDefinedValue(block, operand_src, operand_val)) |operand_val_val| {
10909 if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty);
10910 const union_val = ip.indexToKey(operand_val_val.toIntern()).un;
10911 if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty);
10912 const uncoerced = Air.internedToRef(union_val.val);
10913 return sema.coerce(block, capture_ty, uncoerced, operand_src);
10914 }
10821 resolve_at_comptime: {
10822 // always runtime; evaluation in comptime scope uses `simple`
10823 if (operand == .loop) break :resolve_at_comptime;
1091510824
10916 try sema.requireRuntimeBlock(block, operand_src, null);
10825 var cur_cond_val = try sema.resolveDefinedValue(child_block, src, cond_ref) orelse {
10826 break :resolve_at_comptime;
10827 };
10828 var cur_operand = operand;
1091710829
10918 if (same_types) {
10919 return block.addStructFieldVal(operand_val, first_field_index, capture_ty);
10920 }
10830 while (true) {
10831 if (sema.resolveSwitchBlock(
10832 block,
10833 child_block,
10834 cur_operand,
10835 raw_operand_ty,
10836 cur_cond_val,
10837 catch_all_case,
10838 else_is_named_only,
10839 merges,
10840 switch_inst,
10841 zir_switch,
10842 validated_switch,
10843 )) |result| {
10844 return result;
10845 } else |err| switch (err) {
10846 error.ComptimeBreak => {
10847 const break_inst = sema.code.instructions.get(@intFromEnum(sema.comptime_break_inst));
10848 if (break_inst.tag != .switch_continue) return error.ComptimeBreak;
10849 const extra = sema.code.extraData(Zir.Inst.Break, break_inst.data.@"break".payload_index).data;
10850 if (extra.block_inst != switch_inst) return error.ComptimeBreak;
10851 // This is a `switch_continue` targeting this block. Change the operand and start over.
10852 const new_operand_src = child_block.nodeOffset(extra.operand_src_node.unwrap().?);
10853 const new_operand_uncoerced = try sema.resolveInst(break_inst.data.@"break".operand);
10854 const new_operand = try sema.coerce(child_block, raw_operand_ty, new_operand_uncoerced, new_operand_src);
10855
10856 try sema.emitBackwardBranch(child_block, src);
10857
10858 const new_val, const new_ref = if (operand_is_ref)
10859 .{ try sema.analyzeLoad(child_block, src, new_operand, new_operand_src), new_operand }
10860 else
10861 .{ new_operand, undefined };
1092110862
10922 // We may have to emit a switch block which coerces the operand to the capture type.
10923 // If we can, try to avoid that using in-memory coercions.
10924 const first_non_imc = in_mem: {
10925 for (field_indices, 0..) |field_idx, i| {
10926 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
10927 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded, null)) {
10928 break :in_mem i;
10929 }
10930 }
10931 // All fields are in-memory coercible to the resolved type!
10932 // Just take the first field and bitcast the result.
10933 const uncoerced = try block.addStructFieldVal(operand_val, first_field_index, first_field_ty);
10934 return block.addBitCast(capture_ty, uncoerced);
10935 };
10863 const new_cond_ref = if (union_originally)
10864 try sema.unionToTag(child_block, item_ty, new_val, src)
10865 else
10866 new_val;
1093610867
10937 // By-val capture with heterogeneous types which are not all in-memory coercible to
10938 // the resolved capture type. We finally have to fall back to the ugly method.
10868 cur_cond_val = try sema.resolveConstDefinedValue(child_block, src, new_cond_ref, null);
10869 cur_operand = .{ .simple = .{
10870 .by_val = new_val,
10871 .by_ref = new_ref,
10872 .cond = new_cond_ref,
10873 } };
10874 },
10875 else => |e| return e,
10876 }
10877 }
10878 }
1093910879
10940 // However, let's first track which operands are in-memory coercible. There may well
10941 // be several, and we can squash all of these cases into the same switch prong using
10942 // a simple bitcast. We'll make this the 'else' prong.
10880 if (child_block.isComptime()) {
10881 _ = try sema.resolveConstDefinedValue(child_block, operand_src, operand.simple.cond, null);
10882 unreachable;
10883 }
1094310884
10944 var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_indices.len);
10945 in_mem_coercible.unset(first_non_imc);
10946 {
10947 const next = first_non_imc + 1;
10948 for (field_indices[next..], next..) |field_idx, i| {
10949 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
10950 if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded, null)) {
10951 in_mem_coercible.unset(i);
10885 if (try sema.typeHasOnePossibleValue(item_ty)) |item_opv| {
10886 // We simplify conditions with OPV to either a `loop` or a `block` since
10887 // we cannot switch on a value which doesn't exist at runtime.
10888 assert(operand == .loop); // `simple` should have already been comptime-resolved above!
10889
10890 var case_block = child_block.makeSubBlock();
10891 case_block.runtime_loop = null;
10892 case_block.runtime_cond = operand_src;
10893 case_block.runtime_index.increment();
10894 case_block.need_debug_scope = null; // this body is emitted regardless
10895 defer case_block.instructions.deinit(gpa);
10896
10897 const case_vals = validated_switch.case_vals;
10898
10899 const index, const body, const capture, const has_tag_capture, const is_inline, const is_special = find_prong: {
10900 var case_val_idx: usize = 0;
10901 var case_it = zir_switch.iterateCases();
10902 var extra_index = zir_switch.end;
10903 while (case_it.next()) |case| {
10904 const prong_info = case.prong_info;
10905 const prong_body = sema.code.bodySlice(extra_index, prong_info.body_len);
10906 extra_index += prong_body.len;
10907 skip_case: {
10908 if (!err_set) break :skip_case;
10909 // This case might consist of errors which are not in the set
10910 // we're switching on. If so we have to skip it!
10911 const item_refs = case_vals[case_val_idx..][0..case.item_infos.len];
10912 case_val_idx += item_refs.len;
10913 assert(case.range_infos.len == 0);
10914 for (case.item_infos, item_refs) |item_info, item_ref| {
10915 if (item_info.bodyLen()) |body_len| extra_index += body_len;
10916 if (sema.wantSwitchProngBodyAnalysis(block, item_ref, operand_ty, false, true, prong_info.is_comptime_unreach)) {
10917 break :skip_case;
1095210918 }
1095310919 }
10920 continue;
1095410921 }
10922 break :find_prong .{ case.index, prong_body, prong_info.capture, prong_info.has_tag_capture, prong_info.is_inline, false };
10923 }
10924 if (has_else) {
10925 // This *has* to be checked after iterating all regular cases because
10926 // we allow simple noreturn else prongs when switching on error sets!
10927 break :find_prong .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture, else_case.is_inline, true };
10928 }
10929 unreachable; // malformed validated switch
10930 };
1095510931
10956 const capture_block_inst = try block.addInstAsIndex(.{
10957 .tag = .block,
10958 .data = .{
10959 .ty_pl = .{
10960 .ty = Air.internedToRef(capture_ty.toIntern()),
10961 .payload = undefined, // updated below
10962 },
10963 },
10964 });
10965
10966 const prong_count = field_indices.len - in_mem_coercible.count();
10967
10968 const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints
10969 var cases_extra = try std.array_list.Managed(u32).initCapacity(sema.gpa, estimated_extra);
10970 defer cases_extra.deinit();
10971
10972 {
10973 // All branch hints are `.none`, so just add zero elems.
10974 comptime assert(@intFromEnum(std.builtin.BranchHint.none) == 0);
10975 const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable;
10976 try cases_extra.appendNTimes(0, need_elems);
10977 }
10978
10979 {
10980 // Non-bitcast cases
10981 var it = in_mem_coercible.iterator(.{ .kind = .unset });
10982 while (it.next()) |idx| {
10983 var coerce_block = block.makeSubBlock();
10984 defer coerce_block.instructions.deinit(sema.gpa);
10932 const analyze_body = sema.wantSwitchProngBodyAnalysis(block, .fromValue(item_opv), operand_ty, union_originally, err_set, false);
10933 if (!analyze_body) return .unreachable_value;
1098510934
10986 const case_src: LazySrcLoc = .{
10987 .base_node_inst = capture_src.base_node_inst,
10988 .offset = .{ .switch_case_item = .{
10989 .switch_node_offset = switch_node_offset,
10990 .case_idx = capture_src.offset.switch_capture.case_idx,
10991 .item_idx = .{ .kind = .single, .index = @intCast(idx) },
10992 } },
10993 };
10935 if (!(err_set and
10936 try sema.maybeErrorUnwrap(&case_block, body, cond_ref, operand_src, true)))
10937 {
10938 // Set up captures manually to avoid special cases in the main logic.
10939 const payload_inst: Zir.Inst.Index = if (capture != .none) inst: {
10940 const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst;
10941 const payload_ref: Air.Inst.Ref = payload_ref: {
10942 const item_val: InternPool.Index = switch (operand_ty.zigTypeTag(zcu)) {
10943 .@"union" => item_val: {
10944 if (maybe_operand_opv) |operand_opv| {
10945 break :item_val zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val;
10946 }
10947 assert(union_originally); // operand type must be union, otherwise it would be an OPV type here
10948 assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture
10949 const operand_val, const operand_ref = switch (operand) {
10950 .simple => unreachable,
10951 .loop => |l| load_operand: {
10952 const loaded = try sema.analyzeLoad(block, src, l.operand_alloc, src);
10953 if (l.operand_is_ref) {
10954 const by_val = try sema.analyzeLoad(block, src, loaded, src);
10955 break :load_operand .{ by_val, loaded };
10956 } else {
10957 break :load_operand .{ loaded, undefined };
10958 }
10959 },
10960 };
10961 break :payload_ref try sema.analyzeSwitchPayloadCapture(
10962 &case_block,
10963 operand,
10964 operand_val,
10965 operand_ref,
10966 operand_ty,
10967 operand_src,
10968 block.src(.{ .switch_capture = .{
10969 .switch_node_offset = src_node_offset,
10970 .case_idx = index,
10971 } }),
10972 capture == .by_ref,
10973 is_special,
10974 if (!is_special) case_vals else undefined,
10975 if (is_inline) .fromValue(item_opv) else .none,
10976 validated_switch.else_err_ty,
10977 );
10978 },
10979 else => item_opv.toIntern(),
10980 };
10981 break :payload_ref switch (capture) {
10982 .by_val => .fromIntern(item_val),
10983 .by_ref => try sema.uavRef(item_val),
10984 .none => unreachable,
10985 };
10986 };
10987 assert(!sema.typeOf(payload_ref).isNoReturn(sema.pt.zcu));
10988 sema.inst_map.putAssumeCapacity(payload_inst, payload_ref);
10989 break :inst payload_inst;
10990 } else undefined;
10991 defer if (capture != .none) assert(sema.inst_map.remove(payload_inst));
1099410992
10995 const field_idx = field_indices[idx];
10996 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
10997 const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty);
10998 const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src);
10999 _ = try coerce_block.addBr(capture_block_inst, coerced);
11000
11001 try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
11002 1 + // `item`, no ranges
11003 coerce_block.instructions.items.len);
11004 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
11005 .items_len = 1,
11006 .ranges_len = 0,
11007 .body_len = @intCast(coerce_block.instructions.items.len),
11008 }));
11009 cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item
11010 cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body
11011 }
11012 }
11013 const else_body_len = len: {
11014 // 'else' prong uses a bitcast
11015 var coerce_block = block.makeSubBlock();
11016 defer coerce_block.instructions.deinit(sema.gpa);
10993 const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: {
10994 const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst;
10995 sema.inst_map.putAssumeCapacity(tag_inst, .fromValue(item_opv));
10996 break :inst tag_inst;
10997 } else undefined;
10998 defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst));
1101710999
11018 const first_imc_item_idx = in_mem_coercible.findFirstSet().?;
11019 const first_imc_field_idx = field_indices[first_imc_item_idx];
11020 const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]);
11021 const uncoerced = try coerce_block.addStructFieldVal(operand_val, first_imc_field_idx, first_imc_field_ty);
11022 const coerced = try coerce_block.addBitCast(capture_ty, uncoerced);
11023 _ = try coerce_block.addBr(capture_block_inst, coerced);
11000 if (zir_switch.has_continue) sema.inst_map.putAssumeCapacity(switch_inst, .fromType(raw_operand_ty));
11001 defer if (zir_switch.has_continue) assert(sema.inst_map.remove(switch_inst));
1102411002
11025 try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items));
11026 break :len coerce_block.instructions.items.len;
11027 };
11003 _ = try sema.analyzeBodyRuntimeBreak(&case_block, body);
11004 }
1102811005
11029 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len +
11030 cases_extra.items.len +
11031 @typeInfo(Air.Block).@"struct".fields.len +
11032 1);
11033
11034 const switch_br_inst: u32 = @intCast(sema.air_instructions.len);
11035 try sema.air_instructions.append(sema.gpa, .{
11036 .tag = .switch_br,
11037 .data = .{
11038 .pl_op = .{
11039 .operand = undefined, // set by switch below
11040 .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{
11041 .cases_len = @intCast(prong_count),
11042 .else_body_len = @intCast(else_body_len),
11043 }),
11044 },
11045 },
11046 });
11047 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
11048
11049 // Set up block body
11050 switch (spa.operand) {
11051 .simple => |s| {
11052 const air_datas = sema.air_instructions.items(.data);
11053 air_datas[switch_br_inst].pl_op.operand = s.cond;
11054 air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{
11055 .body_len = 1,
11056 });
11057 sema.air_extra.appendAssumeCapacity(switch_br_inst);
11058 },
11059 .loop => {
11060 // The block must first extract the tag from the loaded union.
11061 const tag_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
11062 try sema.air_instructions.append(sema.gpa, .{
11063 .tag = .get_union_tag,
11064 .data = .{ .ty_op = .{
11065 .ty = Air.internedToRef(union_obj.enum_tag_ty),
11066 .operand = operand_val,
11067 } },
11068 });
11069 const air_datas = sema.air_instructions.items(.data);
11070 air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef();
11071 air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{
11072 .body_len = 2,
11073 });
11074 sema.air_extra.appendAssumeCapacity(@intFromEnum(tag_inst));
11075 sema.air_extra.appendAssumeCapacity(switch_br_inst);
11076 },
11077 }
11006 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len +
11007 case_block.instructions.items.len);
11008 const payload_index = sema.addExtraAssumeCapacity(Air.Block{
11009 .body_len = @intCast(case_block.instructions.items.len),
11010 });
11011 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
1107811012
11079 return capture_block_inst.toRef();
11080 },
11081 .error_set => {
11082 if (capture_byref) {
11083 return sema.fail(
11084 block,
11085 capture_src,
11086 "error set cannot be captured by reference",
11087 .{},
11088 );
11089 }
11013 const air_tag: Air.Inst.Tag = if (merges.extra_insts.items.len > 0)
11014 .loop
11015 else
11016 .block;
11017 const air_loop_ref = try child_block.addInst(.{
11018 .tag = air_tag,
11019 .data = .{ .ty_pl = .{
11020 .ty = .noreturn_type,
11021 .payload = payload_index,
11022 } },
11023 });
11024 try sema.fixupSwitchContinues(
11025 block,
11026 src,
11027 air_loop_ref,
11028 operand,
11029 operand_is_ref,
11030 item_ty,
11031 .opv,
11032 zir_switch.any_maybe_runtime_capture,
11033 merges,
11034 );
11035 return null;
11036 }
1109011037
11091 if (case_vals.len == 1) {
11092 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, case_vals[0], undefined) catch unreachable;
11093 const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?);
11094 return sema.bitCast(block, item_ty, operand_val, operand_src, null);
11095 }
11038 assert(maybe_operand_opv == null); // `operand_ty` can only be an OPV type if `item_ty` is one too!
1109611039
11097 var names: InferredErrorSet.NameMap = .{};
11098 try names.ensureUnusedCapacity(sema.arena, case_vals.len);
11099 for (case_vals) |err| {
11100 const err_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, err, undefined) catch unreachable;
11101 names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {});
11102 }
11103 const error_ty = try pt.errorSetFromUnsortedNames(names.keys());
11104 return sema.bitCast(block, error_ty, operand_val, operand_src, null);
11105 },
11106 else => {
11107 // In this case the capture value is just the passed-through value
11108 // of the switch condition.
11109 if (capture_byref) {
11110 return operand_ptr;
11111 } else {
11112 return operand_val;
11113 }
11114 },
11115 }
11116 }
11117};
11040 try sema.finishSwitchBr(
11041 block,
11042 child_block,
11043 operand,
11044 raw_operand_ty,
11045 operand_is_ref,
11046 merges,
11047 switch_inst,
11048 zir_switch,
11049 validated_switch,
11050 );
11051 return null;
11052}
1111811053
11119fn switchCond(
11054fn finishSwitchBr(
1112011055 sema: *Sema,
1112111056 block: *Block,
11122 src: LazySrcLoc,
11123 operand: Air.Inst.Ref,
11124) CompileError!Air.Inst.Ref {
11057 child_block: *Block,
11058 operand: SwitchOperand,
11059 raw_operand_ty: Type,
11060 operand_is_ref: bool,
11061 merges: *Block.Merges,
11062 switch_inst: Zir.Inst.Index,
11063 zir_switch: *const Zir.UnwrappedSwitchBlock,
11064 validated_switch: *const ValidatedSwitchBlock,
11065) CompileError!void {
1112511066 const pt = sema.pt;
1112611067 const zcu = pt.zcu;
11127 const operand_ty = sema.typeOf(operand);
11128 switch (operand_ty.zigTypeTag(zcu)) {
11129 .type,
11130 .void,
11131 .bool,
11132 .int,
11133 .float,
11134 .comptime_float,
11135 .comptime_int,
11136 .enum_literal,
11137 .pointer,
11138 .@"fn",
11139 .error_set,
11140 .@"enum",
11141 => {
11142 if (operand_ty.isSlice(zcu)) {
11143 return sema.fail(block, src, "switch on type '{f}'", .{operand_ty.fmt(pt)});
11144 }
11145 if ((try sema.typeHasOnePossibleValue(operand_ty))) |opv| {
11146 return Air.internedToRef(opv.toIntern());
11147 }
11148 return operand;
11149 },
11068 const ip = &zcu.intern_pool;
11069 const gpa = sema.gpa;
1115011070
11151 .@"union" => {
11152 try operand_ty.resolveFields(pt);
11153 const enum_ty = operand_ty.unionTagType(zcu) orelse {
11154 const msg = msg: {
11155 const msg = try sema.errMsg(src, "switch on union with no attached enum", .{});
11156 errdefer msg.destroy(sema.gpa);
11157 if (operand_ty.srcLocOrNull(zcu)) |union_src| {
11158 try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{});
11159 }
11160 break :msg msg;
11161 };
11162 return sema.failWithOwnedErrorMsg(block, msg);
11163 };
11164 return sema.unionToTag(block, enum_ty, operand, src);
11165 },
11071 const src_node_offset = zir_switch.switch_src_node_offset;
11072 const src = block.nodeOffset(src_node_offset);
11073 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
1116611074
11167 .error_union,
11168 .noreturn,
11169 .array,
11170 .@"struct",
11171 .undefined,
11172 .null,
11173 .optional,
11174 .@"opaque",
11175 .vector,
11176 .frame,
11177 .@"anyframe",
11178 => return sema.fail(block, src, "switch on type '{f}'", .{operand_ty.fmt(pt)}),
11179 }
11180}
11075 const has_else = zir_switch.else_case != null;
11076 const has_under = zir_switch.has_under;
1118111077
11182const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, LazySrcLoc);
11078 const else_case = validated_switch.else_case;
1118311079
11184fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
11185 const tracy = trace(@src());
11186 defer tracy.end();
11080 const scalar_cases_len = zir_switch.scalarCasesLen();
11081 const multi_cases_len = zir_switch.multiCasesLen();
1118711082
11188 const pt = sema.pt;
11189 const zcu = pt.zcu;
11190 const gpa = sema.gpa;
11191 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
11192 const switch_src = block.nodeOffset(inst_data.src_node);
11193 const switch_src_node_offset = inst_data.src_node;
11194 const switch_operand_src = block.src(.{ .node_offset_switch_operand = switch_src_node_offset });
11195 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = switch_src_node_offset });
11196 const extra = sema.code.extraData(Zir.Inst.SwitchBlockErrUnion, inst_data.payload_index);
11197 const main_operand_src = block.src(.{ .node_offset_if_cond = extra.data.main_src_node_offset });
11198 const main_src = block.src(.{ .node_offset_main_token = extra.data.main_src_node_offset });
11083 const operand_ty = if (operand_is_ref)
11084 raw_operand_ty.childType(zcu)
11085 else
11086 raw_operand_ty;
1119911087
11200 const raw_operand_val = try sema.resolveInst(extra.data.operand);
11088 const cond_ref = switch (operand) {
11089 .simple => |s| s.cond,
11090 .loop => |l| l.init_cond,
11091 };
1120111092
1120211093 // AstGen guarantees that the instruction immediately preceding
11203 // switch_block_err_union is a dbg_stmt
11204 const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1);
11205
11206 var header_extra_index: usize = extra.end;
11094 // switch_block[_ref]/switch_block_err_union is a dbg_stmt.
11095 const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(switch_inst) - 1);
1120711096
11208 const scalar_cases_len = extra.data.bits.scalar_cases_len;
11209 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
11210 const multi_cases_len = sema.code.extra[header_extra_index];
11211 header_extra_index += 1;
11212 break :blk multi_cases_len;
11213 } else 0;
11097 const else_is_named_only = has_else and has_under;
11098 const catch_all_case: CatchAllSwitchCase =
11099 if (has_under) .under else if (has_else) .@"else" else .none;
1121411100
11215 const err_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_uses_err_capture) blk: {
11216 const err_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]);
11217 header_extra_index += 1;
11218 // SwitchProngAnalysis wants inst_map to have space for the tag capture.
11219 // Note that the normal capture is referred to via the switch block
11220 // index, which there is already necessarily space for.
11221 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{err_capture_inst});
11222 break :blk err_capture_inst;
11223 } else undefined;
11101 const item_ty = switch (operand_ty.zigTypeTag(zcu)) {
11102 .@"union" => operand_ty.unionTagType(zcu).?,
11103 else => operand_ty,
11104 };
11105 const union_originally = operand_ty.zigTypeTag(zcu) == .@"union";
11106 const err_set = operand_ty.zigTypeTag(zcu) == .error_set;
1122411107
11225 var case_vals = try std.ArrayList(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
11226 defer case_vals.deinit(gpa);
11108 const estimated_cases_len: u32 = scalar_cases_len + multi_cases_len +
11109 @intFromBool(has_else or has_under);
1122711110
11228 const NonError = struct {
11229 body: []const Zir.Inst.Index,
11230 end: usize,
11231 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
11111 const BranchHints = struct {
11112 bags: std.ArrayList(u32),
11113 count: u32,
11114 const hints_per_bag = 10;
11115 fn ensureUnusedCapacity(hints: *@This(), gpa_inner: Allocator, additional_count: u32) Allocator.Error!void {
11116 const unused_hints = hints.bags.capacity * hints_per_bag - hints.count;
11117 if (unused_hints >= additional_count) return;
11118 const bags_required = std.math.divCeil(u32, hints.count + additional_count, hints_per_bag) catch unreachable;
11119 return hints.bags.ensureUnusedCapacity(gpa_inner, bags_required);
11120 }
11121 fn appendAssumeCapacity(hints: *@This(), hint: std.builtin.BranchHint) void {
11122 const idx_in_bag = hints.count % hints_per_bag;
11123 var bag: u32 = if (idx_in_bag > 0) hints.bags.pop().? else 0;
11124 bag |= @as(u32, @intFromEnum(hint)) << @intCast(@bitSizeOf(std.builtin.BranchHint) * idx_in_bag);
11125 hints.count += 1;
11126 return hints.bags.appendAssumeCapacity(bag);
11127 }
11128 fn append(hints: *@This(), gpa_inner: Allocator, hint: std.builtin.BranchHint) Allocator.Error!void {
11129 try hints.ensureUnusedCapacity(gpa_inner, 1);
11130 return hints.appendAssumeCapacity(hint);
11131 }
1123211132 };
11233
11234 const non_error_case: NonError = non_error: {
11235 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);
11236 const extra_body_start = header_extra_index + 1;
11237 break :non_error .{
11238 .body = sema.code.bodySlice(extra_body_start, info.body_len),
11239 .end = extra_body_start + info.body_len,
11240 .capture = info.capture,
11241 };
11133 var branch_hints: BranchHints = hints: {
11134 const num_bags = std.math.divCeil(u32, estimated_cases_len, BranchHints.hints_per_bag) catch unreachable;
11135 break :hints .{ .bags = try .initCapacity(gpa, num_bags), .count = 0 };
1124211136 };
11137 defer branch_hints.bags.deinit(gpa);
1124311138
11244 const Else = struct {
11245 body: []const Zir.Inst.Index,
11246 end: usize,
11247 is_inline: bool,
11248 has_capture: bool,
11249 };
11250
11251 const else_case: Else = if (!extra.data.bits.has_else) .{
11252 .body = &.{},
11253 .end = non_error_case.end,
11254 .is_inline = false,
11255 .has_capture = false,
11256 } else special: {
11257 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[non_error_case.end]);
11258 const extra_body_start = non_error_case.end + 1;
11259 assert(info.capture != .by_ref);
11260 assert(!info.has_tag_capture);
11261 break :special .{
11262 .body = sema.code.bodySlice(extra_body_start, info.body_len),
11263 .end = extra_body_start + info.body_len,
11264 .is_inline = info.is_inline,
11265 .has_capture = info.capture != .none,
11266 };
11267 };
11139 var cases_extra: std.ArrayList(u32) = try .initCapacity(gpa, estimated_cases_len *
11140 @typeInfo(Air.SwitchBr.Case).@"struct".fields.len);
11141 defer cases_extra.deinit(gpa);
1126811142
11269 var seen_errors = SwitchErrorSet.init(gpa);
11270 defer seen_errors.deinit();
11143 // We will reuse this block for each case.
11144 var case_block = child_block.makeSubBlock();
11145 case_block.runtime_loop = null;
11146 case_block.runtime_cond = operand_src;
11147 case_block.runtime_index.increment();
11148 case_block.need_debug_scope = null; // this body is emitted regardless
11149 defer case_block.instructions.deinit(gpa);
1127111150
11272 const operand_ty = sema.typeOf(raw_operand_val);
11273 const operand_err_set = if (extra.data.bits.payload_is_ref)
11274 operand_ty.childType(zcu)
11275 else
11276 operand_ty;
11151 const case_vals = validated_switch.case_vals;
11152 var case_val_idx: usize = 0;
11153 var case_it = zir_switch.iterateCases();
11154 var extra_index = zir_switch.end;
1127711155
11278 if (operand_err_set.zigTypeTag(zcu) != .error_union) {
11279 return sema.fail(block, switch_src, "expected error union type, found '{f}'", .{
11280 operand_ty.fmt(pt),
11281 });
11282 }
11156 var under_prong: ?struct {
11157 index: Zir.UnwrappedSwitchBlock.Case.Index,
11158 body: []const Zir.Inst.Index,
11159 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
11160 has_tag_capture: bool,
11161 } = null;
1128311162
11284 const operand_err_set_ty = operand_err_set.errorUnionSet(zcu);
11163 var cases_len: u32 = 0;
11164 while (case_it.next()) |case| {
11165 const item_refs = case_vals[case_val_idx..][0..case.item_infos.len];
11166 case_val_idx += item_refs.len;
11167 const range_refs: []const [2]Air.Inst.Ref =
11168 @ptrCast(case_vals[case_val_idx..][0 .. 2 * case.range_infos.len]);
11169 case_val_idx += 2 * range_refs.len;
1128511170
11286 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
11287 try sema.air_instructions.append(gpa, .{
11288 .tag = .block,
11289 .data = undefined,
11290 });
11291 var label: Block.Label = .{
11292 .zir_block = inst,
11293 .merges = .{
11294 .src_locs = .{},
11295 .results = .{},
11296 .br_list = .{},
11297 .block_inst = block_inst,
11298 },
11299 };
11171 const prong_info = case.prong_info;
11172 const prong_body = sema.code.bodySlice(extra_index, prong_info.body_len);
11173 extra_index += prong_body.len;
1130011174
11301 var child_block: Block = .{
11302 .parent = block,
11303 .sema = sema,
11304 .namespace = block.namespace,
11305 .instructions = .{},
11306 .label = &label,
11307 .inlining = block.inlining,
11308 .comptime_reason = block.comptime_reason,
11309 .is_typeof = block.is_typeof,
11310 .c_import_buf = block.c_import_buf,
11311 .runtime_cond = block.runtime_cond,
11312 .runtime_loop = block.runtime_loop,
11313 .runtime_index = block.runtime_index,
11314 .error_return_trace_index = block.error_return_trace_index,
11315 .want_safety = block.want_safety,
11316 .src_base_inst = block.src_base_inst,
11317 .type_name_ctx = block.type_name_ctx,
11318 };
11319 const merges = &child_block.label.?.merges;
11320 defer child_block.instructions.deinit(gpa);
11321 defer merges.deinit(gpa);
11175 // Enough capacity for inlining regular items, we can't really predict
11176 // how many range items we will end up with (at least not in a safe and
11177 // cheap manner) so we allocate on demand for those.
11178 if (prong_info.is_inline) {
11179 try branch_hints.ensureUnusedCapacity(gpa, @intCast(case.item_infos.len));
11180 }
1132211181
11323 const resolved_err_set = try sema.resolveInferredErrorSetTy(block, main_src, operand_err_set_ty.toIntern());
11324 if (Type.fromInterned(resolved_err_set).errorSetIsEmpty(zcu)) {
11325 return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges);
11326 }
11182 var emit_bb = false;
11183 var any_analyze_body = false;
11184 var is_under_prong = false;
11185 for (case.item_infos, item_refs, 0..) |item_info, item_ref, item_i| {
11186 if (item_ref == .none) is_under_prong = true;
11187 if (item_info.bodyLen()) |body_len| extra_index += body_len;
1132711188
11328 const else_error_ty: ?Type = try validateErrSetSwitch(
11329 sema,
11330 block,
11331 &seen_errors,
11332 &case_vals,
11333 operand_err_set_ty,
11334 inst_data,
11335 scalar_cases_len,
11336 multi_cases_len,
11337 .{ .body = else_case.body, .end = else_case.end, .src = else_prong_src },
11338 extra.data.bits.has_else,
11339 );
11189 const analyze_body = sema.wantSwitchProngBodyAnalysis(block, item_ref, operand_ty, union_originally, err_set, prong_info.is_comptime_unreach);
11190 if (analyze_body) any_analyze_body = true;
1134011191
11341 var spa: SwitchProngAnalysis = .{
11342 .sema = sema,
11343 .parent_block = block,
11344 .operand = .{
11345 .simple = .{
11346 .by_val = undefined, // must be set to the unwrapped error code before use
11347 .by_ref = undefined,
11348 .cond = raw_operand_val,
11349 },
11350 },
11351 .else_error_ty = else_error_ty,
11352 .switch_block_inst = inst,
11353 .tag_capture_inst = undefined,
11354 };
11192 if (prong_info.is_inline) {
11193 cases_len += 1;
11194 case_block.instructions.clearRetainingCapacity();
11195 case_block.error_return_trace_index = child_block.error_return_trace_index;
1135511196
11356 if (try sema.resolveDefinedValue(&child_block, main_src, raw_operand_val)) |ov| {
11357 const operand_val = if (extra.data.bits.payload_is_ref)
11358 (try sema.pointerDeref(&child_block, main_src, ov, operand_ty)).?
11359 else
11360 ov;
11197 if (emit_bb) {
11198 const bb_src = block.src(.{ .switch_case_item = .{
11199 .switch_node_offset = src_node_offset,
11200 .case_idx = case.index,
11201 .item_idx = .{ .kind = .single, .value = @intCast(item_i) },
11202 } });
11203 try sema.emitBackwardBranch(block, bb_src);
11204 }
11205 emit_bb = true;
1136111206
11362 if (operand_val.errorUnionIsPayload(zcu)) {
11363 return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges);
11364 } else {
11365 const err_val = Value.fromInterned(try pt.intern(.{
11366 .err = .{
11367 .ty = operand_err_set_ty.toIntern(),
11368 .name = operand_val.getErrorName(zcu).unwrap().?,
11369 },
11370 }));
11371 spa.operand.simple.by_val = if (extra.data.bits.payload_is_ref)
11372 try sema.analyzeErrUnionCodePtr(block, switch_operand_src, raw_operand_val)
11373 else
11374 try sema.analyzeErrUnionCode(block, switch_operand_src, raw_operand_val);
11207 const prong_hint: std.builtin.BranchHint = hint: {
11208 if (analyze_body) break :hint try sema.analyzeSwitchProng(
11209 &case_block,
11210 operand,
11211 operand_ty,
11212 raw_operand_ty,
11213 prong_body,
11214 block.src(.{ .switch_capture = .{
11215 .switch_node_offset = src_node_offset,
11216 .case_idx = case.index,
11217 } }),
11218 prong_info.capture,
11219 prong_info.has_tag_capture,
11220 item_ref,
11221 .{ .item_refs = &.{item_ref} },
11222 validated_switch.else_err_ty,
11223 switch_inst,
11224 zir_switch,
11225 );
11226 _ = try case_block.addNoOp(.unreach);
11227 break :hint .cold; // unreachable branches are cold
11228 };
11229 branch_hints.appendAssumeCapacity(prong_hint);
1137511230
11376 if (extra.data.bits.any_uses_err_capture) {
11377 sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand.simple.by_val);
11231 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
11232 1 + // `item`, no ranges
11233 case_block.instructions.items.len);
11234 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
11235 .items_len = 1,
11236 .ranges_len = 0,
11237 .body_len = @intCast(case_block.instructions.items.len),
11238 }));
11239 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
11240 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
1137811241 }
11379 defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst));
11380
11381 return resolveSwitchComptime(
11382 sema,
11383 spa,
11384 &child_block,
11385 try sema.switchCond(block, switch_operand_src, spa.operand.simple.by_val),
11386 err_val,
11387 operand_err_set_ty,
11388 switch_src_node_offset,
11389 null,
11390 .{
11391 .body = else_case.body,
11392 .end = else_case.end,
11393 .capture = if (else_case.has_capture) .by_val else .none,
11394 .is_inline = else_case.is_inline,
11395 .has_tag_capture = false,
11396 },
11397 false,
11398 case_vals,
11399 scalar_cases_len,
11400 multi_cases_len,
11401 true,
11402 false,
11403 );
1140411242 }
11405 }
11243 for (case.range_infos, range_refs, 0..) |range_info, range_ref, range_i| {
11244 if (range_info[0].bodyLen()) |body_len| extra_index += body_len;
11245 if (range_info[1].bodyLen()) |body_len| extra_index += body_len;
1140611246
11407 if (scalar_cases_len + multi_cases_len == 0) {
11408 if (else_error_ty) |ty| if (ty.errorSetIsEmpty(zcu)) {
11409 return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges);
11410 };
11411 }
11412
11413 if (child_block.isComptime()) {
11414 _ = try sema.resolveConstDefinedValue(&child_block, main_operand_src, raw_operand_val, null);
11415 unreachable;
11416 }
11247 any_analyze_body = true; // always an integer range, always needs analysis
1141711248
11418 const cond = if (extra.data.bits.payload_is_ref) blk: {
11419 try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val).elemType2(zcu));
11420 const loaded = try sema.analyzeLoad(block, main_src, raw_operand_val, main_src);
11421 break :blk try sema.analyzeIsNonErr(block, main_src, loaded);
11422 } else blk: {
11423 try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val));
11424 break :blk try sema.analyzeIsNonErr(block, main_src, raw_operand_val);
11425 };
11249 if (prong_info.is_inline) {
11250 var item = sema.resolveConstDefinedValue(block, .unneeded, range_ref[0], undefined) catch unreachable;
11251 const item_last = sema.resolveConstDefinedValue(block, .unneeded, range_ref[1], undefined) catch unreachable;
1142611252
11427 var sub_block = child_block.makeSubBlock();
11428 sub_block.runtime_loop = null;
11429 sub_block.runtime_cond = main_operand_src;
11430 sub_block.runtime_index.increment();
11431 sub_block.need_debug_scope = null; // this body is emitted regardless
11432 defer sub_block.instructions.deinit(gpa);
11253 if (try item.getUnsignedIntSema(pt)) |first_int| {
11254 if (try item_last.getUnsignedIntSema(pt)) |last_int| {
11255 if (std.math.cast(u32, last_int - first_int)) |range_len| {
11256 try branch_hints.ensureUnusedCapacity(gpa, range_len);
11257 }
11258 }
11259 }
1143311260
11434 const non_error_hint = try sema.analyzeBodyRuntimeBreak(&sub_block, non_error_case.body);
11435 const true_instructions = try sub_block.instructions.toOwnedSlice(gpa);
11436 defer gpa.free(true_instructions);
11261 var prev_result_overflowed = false;
11262 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({
11263 // Previous validation has resolved any possible lazy values.
11264 const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) {
11265 .int => .{ item, operand_ty },
11266 .@"enum" => b: {
11267 const int_val: Value = .fromInterned(ip.indexToKey(item.toIntern()).enum_tag.int);
11268 break :b .{ int_val, int_val.typeOf(zcu) };
11269 },
11270 else => unreachable,
11271 };
11272 assert(!prev_result_overflowed);
11273 const result = try arith.incrementDefinedInt(sema, int_ty, int_val);
11274 prev_result_overflowed = result.overflow;
11275 item = switch (operand_ty.zigTypeTag(zcu)) {
11276 .int => result.val,
11277 .@"enum" => .fromInterned(try pt.intern(.{ .enum_tag = .{
11278 .ty = operand_ty.toIntern(),
11279 .int = result.val.toIntern(),
11280 } })),
11281 else => unreachable,
11282 };
11283 }) {
11284 cases_len += 1;
11285 case_block.instructions.clearRetainingCapacity();
11286 case_block.error_return_trace_index = child_block.error_return_trace_index;
1143711287
11438 spa.operand.simple.by_val = if (extra.data.bits.payload_is_ref)
11439 try sema.analyzeErrUnionCodePtr(&sub_block, switch_operand_src, raw_operand_val)
11440 else
11441 try sema.analyzeErrUnionCode(&sub_block, switch_operand_src, raw_operand_val);
11442
11443 if (extra.data.bits.any_uses_err_capture) {
11444 sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand.simple.by_val);
11445 }
11446 defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst));
11447 _ = try sema.analyzeSwitchRuntimeBlock(
11448 spa,
11449 &sub_block,
11450 switch_src,
11451 try sema.switchCond(block, switch_operand_src, spa.operand.simple.by_val),
11452 operand_err_set_ty,
11453 switch_operand_src,
11454 case_vals,
11455 .{
11456 .body = else_case.body,
11457 .end = else_case.end,
11458 .capture = if (else_case.has_capture) .by_val else .none,
11459 .is_inline = else_case.is_inline,
11460 .has_tag_capture = false,
11461 },
11462 scalar_cases_len,
11463 multi_cases_len,
11464 false,
11465 undefined,
11466 true,
11467 switch_src_node_offset,
11468 else_prong_src,
11469 false,
11470 undefined,
11471 seen_errors,
11472 undefined,
11473 undefined,
11474 undefined,
11475 cond_dbg_node_index,
11476 true,
11477 null,
11478 undefined,
11479 &.{},
11480 &.{},
11481 );
11288 const item_ref: Air.Inst.Ref = .fromValue(item);
1148211289
11483 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
11484 true_instructions.len + sub_block.instructions.items.len);
11290 if (emit_bb) {
11291 const bb_src = block.src(.{ .switch_case_item = .{
11292 .switch_node_offset = src_node_offset,
11293 .case_idx = case.index,
11294 .item_idx = .{ .kind = .range, .value = @intCast(range_i) },
11295 } });
11296 try sema.emitBackwardBranch(block, bb_src);
11297 }
11298 emit_bb = true;
1148511299
11486 _ = try child_block.addInst(.{
11487 .tag = .cond_br,
11488 .data = .{
11489 .pl_op = .{
11490 .operand = cond,
11491 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
11492 .then_body_len = @intCast(true_instructions.len),
11493 .else_body_len = @intCast(sub_block.instructions.items.len),
11494 .branch_hints = .{
11495 .true = non_error_hint,
11496 .false = .none,
11497 // Code coverage is desired for error handling.
11498 .then_cov = .poi,
11499 .else_cov = .poi,
11500 },
11501 }),
11502 },
11503 },
11504 });
11505 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions));
11506 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
11300 const prong_hint = try sema.analyzeSwitchProng(
11301 &case_block,
11302 operand,
11303 operand_ty,
11304 raw_operand_ty,
11305 prong_body,
11306 block.src(.{ .switch_capture = .{
11307 .switch_node_offset = src_node_offset,
11308 .case_idx = case.index,
11309 } }),
11310 prong_info.capture,
11311 prong_info.has_tag_capture,
11312 item_ref,
11313 .has_ranges,
11314 validated_switch.else_err_ty,
11315 switch_inst,
11316 zir_switch,
11317 );
11318 try branch_hints.append(gpa, prong_hint);
1150711319
11508 return sema.resolveAnalyzedBlock(block, main_src, &child_block, merges, false);
11509}
11320 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
11321 1 + // `item`, no ranges
11322 case_block.instructions.items.len);
11323 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
11324 .items_len = 1,
11325 .ranges_len = 0,
11326 .body_len = @intCast(case_block.instructions.items.len),
11327 }));
11328 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
11329 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
11330 }
11331 }
11332 }
1151011333
11511fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref {
11512 const tracy = trace(@src());
11513 defer tracy.end();
11334 if (prong_info.is_inline) continue; // handled above
1151411335
11515 const pt = sema.pt;
11516 const zcu = pt.zcu;
11517 const ip = &zcu.intern_pool;
11518 const gpa = sema.gpa;
11519 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
11520 const src = block.nodeOffset(inst_data.src_node);
11521 const src_node_offset = inst_data.src_node;
11522 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
11523 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset });
11524 const under_prong_src = block.src(.{ .node_offset_switch_under_prong = src_node_offset });
11525 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
11336 if (is_under_prong) {
11337 under_prong = .{
11338 .index = case.index,
11339 .body = prong_body,
11340 .capture = case.prong_info.capture,
11341 .has_tag_capture = case.prong_info.has_tag_capture,
11342 };
11343 continue;
11344 }
1152611345
11527 const operand: SwitchProngAnalysis.Operand, const raw_operand_ty: Type = op: {
11528 const maybe_ptr = try sema.resolveInst(extra.data.operand);
11529 const val, const ref = if (operand_is_ref)
11530 .{ try sema.analyzeLoad(block, src, maybe_ptr, operand_src), maybe_ptr }
11531 else
11532 .{ maybe_ptr, undefined };
11346 cases_len += 1;
11347 case_block.instructions.clearRetainingCapacity();
11348 case_block.error_return_trace_index = child_block.error_return_trace_index;
1153311349
11534 const init_cond = try sema.switchCond(block, operand_src, val);
11350 const prong_hint: std.builtin.BranchHint = hint: {
11351 if (any_analyze_body) break :hint try sema.analyzeSwitchProng(
11352 &case_block,
11353 operand,
11354 operand_ty,
11355 raw_operand_ty,
11356 prong_body,
11357 block.src(.{ .switch_capture = .{
11358 .switch_node_offset = src_node_offset,
11359 .case_idx = case.index,
11360 } }),
11361 prong_info.capture,
11362 prong_info.has_tag_capture,
11363 .none,
11364 .{ .item_refs = item_refs },
11365 validated_switch.else_err_ty,
11366 switch_inst,
11367 zir_switch,
11368 );
11369 _ = try case_block.addNoOp(.unreach);
11370 break :hint .cold; // unreachable branches are cold
11371 };
11372 try branch_hints.append(gpa, prong_hint);
1153511373
11536 const operand_ty = sema.typeOf(val);
11374 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
11375 item_refs.len +
11376 2 * range_refs.len +
11377 case_block.instructions.items.len);
11378 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
11379 .items_len = @intCast(item_refs.len),
11380 .ranges_len = @intCast(range_refs.len),
11381 .body_len = @intCast(case_block.instructions.items.len),
11382 }));
11383 cases_extra.appendSliceAssumeCapacity(@ptrCast(item_refs));
11384 cases_extra.appendSliceAssumeCapacity(@ptrCast(range_refs));
11385 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
11386 }
1153711387
11538 if (extra.data.bits.has_continue and !block.isComptime()) {
11539 // Even if the operand is comptime-known, this `switch` is runtime.
11540 if (try operand_ty.comptimeOnlySema(pt)) {
11541 return sema.failWithOwnedErrorMsg(block, msg: {
11542 const msg = try sema.errMsg(operand_src, "operand of switch loop has comptime-only type '{f}'", .{operand_ty.fmt(pt)});
11543 errdefer msg.destroy(gpa);
11544 try sema.errNote(operand_src, msg, "switch loops are evaluated at runtime outside of comptime scopes", .{});
11545 break :msg msg;
11388 const catch_all_extra: []const u32 = catch_all_extra: {
11389 if (catch_all_case == .none and !case_block.wantSafety()) {
11390 try branch_hints.append(gpa, .none);
11391 break :catch_all_extra &.{};
11392 }
11393 var emit_bb = false;
11394 if (has_else and else_case.is_inline) {
11395 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset });
11396 var error_names: InternPool.NullTerminatedString.Slice = undefined;
11397 var min_int: Value = undefined;
11398 check_enumerable: {
11399 switch (item_ty.zigTypeTag(zcu)) {
11400 .@"union" => unreachable,
11401 .@"enum" => if (else_is_named_only or
11402 !item_ty.isNonexhaustiveEnum(zcu) or union_originally)
11403 {
11404 try branch_hints.ensureUnusedCapacity(gpa, @intCast(validated_switch.seen_enum_fields.len));
11405 break :check_enumerable;
11406 },
11407 .error_set => if (!operand_ty.isAnyError(zcu)) {
11408 error_names = item_ty.errorSetNames(zcu);
11409 try branch_hints.ensureUnusedCapacity(gpa, error_names.len);
11410 break :check_enumerable;
11411 },
11412 .int => {
11413 min_int = try item_ty.minInt(pt, item_ty);
11414 break :check_enumerable;
11415 },
11416 .bool, .void => break :check_enumerable,
11417 else => {},
11418 }
11419 return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
11420 item_ty.fmt(pt),
1154611421 });
1154711422 }
11548 try sema.validateRuntimeValue(block, operand_src, maybe_ptr);
11549 const operand_alloc = if (extra.data.bits.any_non_inline_capture) a: {
11550 const operand_ptr_ty = try pt.singleMutPtrType(sema.typeOf(maybe_ptr));
11551 const operand_alloc = try block.addTy(.alloc, operand_ptr_ty);
11552 _ = try block.addBinOp(.store, operand_alloc, maybe_ptr);
11553 break :a operand_alloc;
11554 } else undefined;
11555 break :op .{
11556 .{ .loop = .{
11557 .operand_alloc = operand_alloc,
11558 .operand_is_ref = operand_is_ref,
11559 .init_cond = init_cond,
11560 } },
11561 operand_ty,
11562 };
11563 }
11423 var unhandled_it = validated_switch.iterateUnhandledItems(error_names, min_int);
11424 while (try unhandled_it.next(sema, item_ty)) |item_val| {
11425 cases_len += 1;
11426 case_block.instructions.clearRetainingCapacity();
11427 case_block.error_return_trace_index = child_block.error_return_trace_index;
1156411428
11565 // We always use `simple` in the comptime case, because as far as the dispatching logic
11566 // is concerned, it really is dispatching a single prong. `resolveSwitchComptime` will
11567 // be resposible for recursively resolving different prongs as needed.
11568 break :op .{
11569 .{ .simple = .{
11570 .by_val = val,
11571 .by_ref = ref,
11572 .cond = init_cond,
11573 } },
11574 operand_ty,
11575 };
11576 };
11429 const item_ref: Air.Inst.Ref = .fromValue(item_val);
1157711430
11578 const union_originally = raw_operand_ty.zigTypeTag(zcu) == .@"union";
11579 const err_set = raw_operand_ty.zigTypeTag(zcu) == .error_set;
11580 const cond_ty = switch (raw_operand_ty.zigTypeTag(zcu)) {
11581 .@"union" => raw_operand_ty.unionTagType(zcu).?, // validated by `switchCond` above
11582 else => raw_operand_ty,
11583 };
11431 const analyze_body = sema.wantSwitchProngBodyAnalysis(block, item_ref, operand_ty, union_originally, err_set, false);
1158411432
11585 // AstGen guarantees that the instruction immediately preceding
11586 // switch_block(_ref) is a dbg_stmt
11587 const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1);
11433 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
11434 emit_bb = true;
1158811435
11589 var header_extra_index: usize = extra.end;
11436 const prong_hint: std.builtin.BranchHint = hint: {
11437 if (analyze_body) break :hint try sema.analyzeSwitchProng(
11438 &case_block,
11439 operand,
11440 operand_ty,
11441 raw_operand_ty,
11442 else_case.body,
11443 block.src(.{ .switch_capture = .{
11444 .switch_node_offset = src_node_offset,
11445 .case_idx = else_case.index,
11446 } }),
11447 else_case.capture,
11448 else_case.has_tag_capture,
11449 item_ref,
11450 .special,
11451 validated_switch.else_err_ty,
11452 switch_inst,
11453 zir_switch,
11454 );
11455 _ = try case_block.addNoOp(.unreach);
11456 break :hint .cold; // unreachable branches are cold
11457 };
11458 try branch_hints.append(gpa, prong_hint);
1159011459
11591 const scalar_cases_len = extra.data.bits.scalar_cases_len;
11592 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
11593 const multi_cases_len = sema.code.extra[header_extra_index];
11594 header_extra_index += 1;
11595 break :blk multi_cases_len;
11596 } else 0;
11460 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
11461 1 + // `item`, no ranges
11462 case_block.instructions.items.len);
11463 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
11464 .items_len = 1,
11465 .ranges_len = 0,
11466 .body_len = @intCast(case_block.instructions.items.len),
11467 }));
11468 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
11469 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
11470 }
11471 }
1159711472
11598 const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: {
11599 const tag_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]);
11600 header_extra_index += 1;
11601 // SwitchProngAnalysis wants inst_map to have space for the tag capture.
11602 // Note that the normal capture is referred to via the switch block
11603 // index, which there is already necessarily space for.
11604 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst});
11605 break :blk tag_capture_inst;
11606 } else undefined;
11473 case_block.instructions.clearRetainingCapacity();
11474 case_block.error_return_trace_index = child_block.error_return_trace_index;
1160711475
11608 var case_vals = try std.ArrayList(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
11609 defer case_vals.deinit(gpa);
11610
11611 var single_absorbed_item: Zir.Inst.Ref = .none;
11612 var absorbed_items: []const Zir.Inst.Ref = &.{};
11613 var absorbed_ranges: []const Zir.Inst.Ref = &.{};
11614
11615 const special_prongs = extra.data.bits.special_prongs;
11616 const has_else = special_prongs.hasElse();
11617 const has_under = special_prongs.hasUnder();
11618 const special_else: SpecialProng = if (has_else) blk: {
11619 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);
11620 const extra_body_start = header_extra_index + 1;
11621 break :blk .{
11622 .body = sema.code.bodySlice(extra_body_start, info.body_len),
11623 .end = extra_body_start + info.body_len,
11624 .capture = info.capture,
11625 .is_inline = info.is_inline,
11626 .has_tag_capture = info.has_tag_capture,
11627 };
11628 } else .{
11629 .body = &.{},
11630 .end = header_extra_index,
11631 .capture = .none,
11632 .is_inline = false,
11633 .has_tag_capture = false,
11634 };
11635 const special_under: SpecialProng = if (has_under) blk: {
11636 var extra_index = special_else.end;
11637 var trailing_items_len: usize = 0;
11638 if (special_prongs.hasOneAdditionalItem()) {
11639 single_absorbed_item = @enumFromInt(sema.code.extra[extra_index]);
11640 extra_index += 1;
11641 absorbed_items = @ptrCast(&single_absorbed_item);
11642 } else if (special_prongs.hasManyAdditionalItems()) {
11643 const items_len = sema.code.extra[extra_index];
11644 extra_index += 1;
11645 const ranges_len = sema.code.extra[extra_index];
11646 extra_index += 1;
11647 absorbed_items = sema.code.refSlice(extra_index + 1, items_len);
11648 absorbed_ranges = sema.code.refSlice(extra_index + 1 + items_len, ranges_len * 2);
11649 trailing_items_len = items_len + ranges_len * 2;
11650 }
11651 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11652 extra_index += 1 + trailing_items_len;
11653 break :blk .{
11654 .body = sema.code.bodySlice(extra_index, info.body_len),
11655 .end = extra_index + info.body_len,
11656 .capture = info.capture,
11657 .is_inline = info.is_inline,
11658 .has_tag_capture = info.has_tag_capture,
11659 };
11660 } else .{
11661 .body = &.{},
11662 .end = special_else.end,
11663 .capture = .none,
11664 .is_inline = false,
11665 .has_tag_capture = false,
11666 };
11667 const special_end = special_under.end;
11476 if (zcu.backendSupportsFeature(.is_named_enum_value) and
11477 catch_all_case != .none and block.wantSafety() and
11478 item_ty.zigTypeTag(zcu) == .@"enum" and
11479 (!operand_ty.isNonexhaustiveEnum(zcu) or union_originally))
11480 {
11481 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
11482 const ok = try case_block.addUnOp(.is_named_enum_value, cond_ref);
11483 if (else_is_named_only) {} else {
11484 try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch);
11485 }
11486 }
1166811487
11669 // Duplicate checking variables later also used for `inline else`.
11670 var seen_enum_fields: []?LazySrcLoc = &.{};
11671 var seen_errors = SwitchErrorSet.init(gpa);
11672 var range_set = RangeSet.init(gpa, zcu);
11673 var true_count: u8 = 0;
11674 var false_count: u8 = 0;
11488 if (else_is_named_only and !else_case.is_inline) {
11489 // If we have both an `else` and an `_` prong, all named values go
11490 // into the `else` prong and all unnamed ones go into the `_` prong.
11491 // We will manually enumerate all named values which haven't been
11492 // encountered yet and create an extra prong for them, which will
11493 // evaulate to the `else` body.
1167511494
11676 defer {
11677 range_set.deinit();
11678 gpa.free(seen_enum_fields);
11679 seen_errors.deinit();
11680 }
11495 assert(operand_ty.isNonexhaustiveEnum(zcu));
1168111496
11682 var empty_enum = false;
11497 cases_len += 1;
1168311498
11684 var else_error_ty: ?Type = null;
11499 const prong_hint: std.builtin.BranchHint = hint: {
11500 if (!else_case.is_inline) break :hint try sema.analyzeSwitchProng(
11501 &case_block,
11502 operand,
11503 operand_ty,
11504 raw_operand_ty,
11505 else_case.body,
11506 block.src(.{ .switch_capture = .{
11507 .switch_node_offset = src_node_offset,
11508 .case_idx = else_case.index,
11509 } }),
11510 else_case.capture,
11511 else_case.has_tag_capture,
11512 .none,
11513 .special,
11514 validated_switch.else_err_ty,
11515 switch_inst,
11516 zir_switch,
11517 );
11518 _ = try case_block.addNoOp(.unreach);
11519 break :hint .cold; // unreachable branches are cold
11520 };
11521 try branch_hints.append(gpa, prong_hint);
1168511522
11686 // Validate usage of '_' prongs.
11687 if (has_under and !raw_operand_ty.isNonexhaustiveEnum(zcu)) {
11688 const msg = msg: {
11689 const msg = try sema.errMsg(
11690 src,
11691 "'_' prong only allowed when switching on non-exhaustive enums",
11692 .{},
11693 );
11694 errdefer msg.destroy(gpa);
11695 try sema.errNote(
11696 under_prong_src,
11697 msg,
11698 "'_' prong here",
11699 .{},
11700 );
11701 try sema.errNote(
11702 src,
11703 msg,
11704 "consider using 'else'",
11705 .{},
11523 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
11524 (validated_switch.seen_enum_fields.len + 1 - zir_switch.totalItemsLen()) + // +1 because totalItemsLen includes the _
11525 case_block.instructions.items.len);
11526 const extra_case = cases_extra.addManyAsArrayAssumeCapacity(
11527 @typeInfo(Air.SwitchBr.Case).@"struct".fields.len,
1170611528 );
11707 break :msg msg;
11708 };
11709 return sema.failWithOwnedErrorMsg(block, msg);
11710 }
11529 var items_len: u32 = 0;
11530 for (validated_switch.seen_enum_fields, 0..) |seen_field, field_i| {
11531 if (seen_field != null) continue;
11532 const item_val = try pt.enumValueFieldIndex(item_ty, @intCast(field_i));
11533 const item_ref: Air.Inst.Ref = .fromValue(item_val);
11534 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
11535 items_len += 1;
11536 }
11537 assert(items_len > 0); // `else` must be reachable at this point
11538 extra_case.* = payloadToExtraItems(Air.SwitchBr.Case{
11539 .items_len = items_len,
11540 .ranges_len = 0,
11541 .body_len = @intCast(case_block.instructions.items.len),
11542 });
11543 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
1171111544
11712 // Validate for duplicate items, missing else prong, and invalid range.
11713 switch (cond_ty.zigTypeTag(zcu)) {
11714 .@"union" => unreachable, // handled in `switchCond`
11715 .@"enum" => {
11716 seen_enum_fields = try gpa.alloc(?LazySrcLoc, cond_ty.enumFieldCount(zcu));
11717 empty_enum = seen_enum_fields.len == 0 and !cond_ty.isNonexhaustiveEnum(zcu);
11718 @memset(seen_enum_fields, null);
11719 // `range_set` is used for non-exhaustive enum values that do not correspond to any tags.
11545 // We fall through to the regular catch-all prong generation.
1172011546
11721 for (absorbed_items, 0..) |item_ref, item_i| {
11722 _ = try sema.validateSwitchItemEnum(
11723 block,
11724 seen_enum_fields,
11725 &range_set,
11726 item_ref,
11727 cond_ty,
11728 block.src(.{ .switch_case_item = .{
11729 .switch_node_offset = src_node_offset,
11730 .case_idx = .special_under,
11731 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
11732 } }),
11733 );
11734 }
11735 try sema.validateSwitchNoRange(block, @intCast(absorbed_ranges.len), cond_ty, src_node_offset);
11547 case_block.instructions.clearRetainingCapacity();
11548 case_block.error_return_trace_index = child_block.error_return_trace_index;
11549 }
1173611550
11737 var extra_index: usize = special_end;
11738 {
11739 var scalar_i: u32 = 0;
11740 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
11741 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
11742 extra_index += 1;
11743 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11744 extra_index += 1 + info.body_len;
11745
11746 case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum(
11747 block,
11748 seen_enum_fields,
11749 &range_set,
11750 item_ref,
11751 cond_ty,
11752 block.src(.{ .switch_case_item = .{
11753 .switch_node_offset = src_node_offset,
11754 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
11755 .item_idx = .{ .kind = .single, .index = 0 },
11756 } }),
11757 ));
11758 }
11551 const analyze_catch_all_body = analyze_body: {
11552 switch (catch_all_case) {
11553 .none => break :analyze_body false, // we still may want a safety check!
11554 .under => break :analyze_body true, // can't be a union anyway
11555 .@"else" => if (else_case.is_inline) break :analyze_body false,
1175911556 }
11760 {
11761 var multi_i: u32 = 0;
11762 while (multi_i < multi_cases_len) : (multi_i += 1) {
11763 const items_len = sema.code.extra[extra_index];
11764 extra_index += 1;
11765 const ranges_len = sema.code.extra[extra_index];
11766 extra_index += 1;
11767 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11768 extra_index += 1;
11769 const items = sema.code.refSlice(extra_index, items_len);
11770 extra_index += items_len + info.body_len;
11771
11772 try case_vals.ensureUnusedCapacity(gpa, items.len);
11773 for (items, 0..) |item_ref, item_i| {
11774 case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum(
11775 block,
11776 seen_enum_fields,
11777 &range_set,
11778 item_ref,
11779 cond_ty,
11780 block.src(.{ .switch_case_item = .{
11781 .switch_node_offset = src_node_offset,
11782 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
11783 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
11784 } }),
11785 ));
11786 }
11787
11788 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);
11557 if (union_originally) {
11558 const union_obj = zcu.typeToUnion(operand_ty).?;
11559 for (validated_switch.seen_enum_fields, 0..) |seen_field, field_i| {
11560 if (seen_field != null) continue;
11561 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_i]);
11562 if (!field_ty.isNoReturn(zcu)) break :analyze_body true;
1178911563 }
11564 break :analyze_body false;
1179011565 }
11791 const all_tags_handled = for (seen_enum_fields) |seen_src| {
11792 if (seen_src == null) break false;
11793 } else true;
11566 if (err_set) {
11567 const else_err_ty = validated_switch.else_err_ty orelse {
11568 assert(else_case.is_simple_noreturn);
11569 break :analyze_body false;
11570 };
11571 if (else_err_ty.errorSetIsEmpty(zcu)) break :analyze_body false;
11572 }
11573 break :analyze_body true;
11574 };
1179411575
11795 if (has_else) {
11796 if (all_tags_handled) {
11797 if (cond_ty.isNonexhaustiveEnum(zcu)) {
11798 if (has_under) return sema.fail(
11799 block,
11576 const catch_all_hint = hint: {
11577 if (analyze_catch_all_body) {
11578 const index, const body, const capture, const has_tag_capture = switch (catch_all_case) {
11579 .@"else" => .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture },
11580 .under => .{ under_prong.?.index, under_prong.?.body, under_prong.?.capture, under_prong.?.has_tag_capture },
11581 .none => unreachable,
11582 };
11583 break :hint try sema.analyzeSwitchProng(
11584 &case_block,
11585 operand,
11586 operand_ty,
11587 raw_operand_ty,
11588 body,
11589 block.src(.{ .switch_capture = .{
11590 .switch_node_offset = src_node_offset,
11591 .case_idx = index,
11592 } }),
11593 capture,
11594 has_tag_capture,
11595 .none,
11596 .special,
11597 validated_switch.else_err_ty,
11598 switch_inst,
11599 zir_switch,
11600 );
11601 }
11602 // We still need a terminator in this block, but we have proven
11603 // that it is unreachable.
11604 if (case_block.wantSafety()) {
11605 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
11606 try sema.safetyPanic(&case_block, src, .corrupt_switch);
11607 } else {
11608 _ = try case_block.addNoOp(.unreach);
11609 }
11610 break :hint .cold; // Safety check / unreachable branches are cold.
11611 };
11612 try branch_hints.append(gpa, catch_all_hint);
11613 break :catch_all_extra @ptrCast(case_block.instructions.items);
11614 };
11615
11616 assert(branch_hints.count == cases_len + 1); // +1 for catch-all hint
11617
11618 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len +
11619 branch_hints.bags.items.len +
11620 cases_extra.items.len +
11621 catch_all_extra.len);
11622 const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{
11623 .cases_len = @intCast(cases_len),
11624 .else_body_len = @intCast(catch_all_extra.len),
11625 });
11626 sema.air_extra.appendSliceAssumeCapacity(branch_hints.bags.items);
11627 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
11628 sema.air_extra.appendSliceAssumeCapacity(catch_all_extra);
11629
11630 const air_tag: Air.Inst.Tag = if (operand == .loop and merges.extra_insts.items.len > 0)
11631 .loop_switch_br
11632 else
11633 .switch_br;
11634 const air_switch_ref = try child_block.addInst(.{
11635 .tag = air_tag,
11636 .data = .{ .pl_op = .{
11637 .operand = cond_ref,
11638 .payload = payload_index,
11639 } },
11640 });
11641 try sema.fixupSwitchContinues(
11642 block,
11643 src,
11644 air_switch_ref,
11645 operand,
11646 operand_is_ref,
11647 item_ty,
11648 .normal,
11649 zir_switch.any_maybe_runtime_capture,
11650 merges,
11651 );
11652}
11653
11654/// This is the counterpart to `zirSwitchContinue`; replaces placeholder `br` insts
11655/// with their respective finalized inst pointing back at `switch_ref`.
11656fn fixupSwitchContinues(
11657 sema: *Sema,
11658 block: *Block,
11659 switch_src: LazySrcLoc,
11660 switch_ref: Air.Inst.Ref,
11661 operand: SwitchOperand,
11662 operand_is_ref: bool,
11663 item_ty: Type,
11664 mode: enum { normal, opv },
11665 any_non_inline_capture: bool,
11666 merges: *const Block.Merges,
11667) CompileError!void {
11668 const pt = sema.pt;
11669 const zcu = pt.zcu;
11670 const gpa = sema.gpa;
11671
11672 const air_tag = sema.air_instructions.items(.tag)[@intFromEnum(switch_ref.toIndex().?)];
11673 switch (air_tag) {
11674 .loop_switch_br, .switch_br => assert(mode == .normal),
11675 .loop, .block => assert(mode == .opv),
11676 else => unreachable,
11677 }
11678 switch (air_tag) {
11679 .loop_switch_br, .loop => assert(merges.extra_insts.items.len > 0),
11680 .switch_br, .block => assert(merges.extra_insts.items.len == 0),
11681 else => unreachable,
11682 }
11683
11684 for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| {
11685 var replacement_block = block.makeSubBlock();
11686 defer replacement_block.instructions.deinit(gpa);
11687
11688 assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .br);
11689 const new_operand_maybe_ref = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].br.operand;
11690
11691 if (any_non_inline_capture and mode != .opv) {
11692 _ = try replacement_block.addBinOp(.store, operand.loop.operand_alloc, new_operand_maybe_ref);
11693 }
11694
11695 const new_operand_val = if (operand_is_ref)
11696 try sema.analyzeLoad(&replacement_block, dispatch_src, new_operand_maybe_ref, dispatch_src)
11697 else
11698 new_operand_maybe_ref;
11699
11700 const new_cond = try sema.coerce(&replacement_block, item_ty, new_operand_val, dispatch_src);
11701
11702 if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and
11703 item_ty.zigTypeTag(zcu) == .@"enum" and !item_ty.isNonexhaustiveEnum(zcu) and
11704 mode == .normal and !try sema.isComptimeKnown(new_cond))
11705 {
11706 const ok = try replacement_block.addUnOp(.is_named_enum_value, new_cond);
11707 try sema.addSafetyCheck(&replacement_block, switch_src, ok, .corrupt_switch);
11708 }
11709
11710 switch (mode) {
11711 .normal => {
11712 _ = try replacement_block.addInst(.{
11713 .tag = .switch_dispatch,
11714 .data = .{ .br = .{
11715 .block_inst = switch_ref.toIndex().?,
11716 .operand = new_cond,
11717 } },
11718 });
11719 },
11720 .opv => {
11721 _ = try replacement_block.addInst(.{
11722 .tag = .repeat,
11723 .data = .{ .repeat = .{
11724 .loop_inst = switch_ref.toIndex().?,
11725 } },
11726 });
11727 },
11728 }
11729
11730 if (replacement_block.instructions.items.len == 1) {
11731 // Optimization: we don't need a block!
11732 sema.air_instructions.set(
11733 @intFromEnum(placeholder_inst),
11734 sema.air_instructions.get(@intFromEnum(replacement_block.instructions.items[0])),
11735 );
11736 continue;
11737 }
11738
11739 // Replace placeholder with a block.
11740 // No `br` is needed as the block is a switch dispatch so necessarily `noreturn`.
11741 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len +
11742 replacement_block.instructions.items.len);
11743 sema.air_instructions.set(@intFromEnum(placeholder_inst), .{
11744 .tag = .block,
11745 .data = .{ .ty_pl = .{
11746 .ty = .noreturn_type,
11747 .payload = sema.addExtraAssumeCapacity(Air.Block{
11748 .body_len = @intCast(replacement_block.instructions.items.len),
11749 }),
11750 } },
11751 });
11752 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items));
11753 }
11754}
11755
11756const ValidatedSwitchBlock = struct {
11757 seen_enum_fields: []const ?LazySrcLoc,
11758 seen_errors: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc),
11759 seen_ranges: []const RangeSet.Range,
11760 true_src: ?LazySrcLoc,
11761 false_src: ?LazySrcLoc,
11762 void_src: ?LazySrcLoc,
11763
11764 case_vals: []const Air.Inst.Ref,
11765 else_case: Zir.UnwrappedSwitchBlock.Case.Else,
11766 else_err_ty: ?Type,
11767
11768 fn iterateUnhandledItems(
11769 validated_switch: *const ValidatedSwitchBlock,
11770 /// May be `undefined` if `item_ty` isn't an `error_set`.
11771 error_names: InternPool.NullTerminatedString.Slice,
11772 /// May be `undefined` if `item_ty` isn't an `int`.
11773 min_int: Value,
11774 ) UnhandledIterator {
11775 return .{
11776 .next_idx = 0,
11777 .next_val = min_int,
11778 .error_names = error_names,
11779 .seen_enum_fields = validated_switch.seen_enum_fields,
11780 .seen_errors = &validated_switch.seen_errors,
11781 .seen_ranges = validated_switch.seen_ranges,
11782 .seen_true = validated_switch.true_src != null,
11783 .seen_false = validated_switch.false_src != null,
11784 .seen_void = validated_switch.void_src != null,
11785 };
11786 }
11787
11788 const UnhandledIterator = struct {
11789 next_idx: u32,
11790 next_val: ?Value,
11791 error_names: InternPool.NullTerminatedString.Slice,
11792 seen_enum_fields: []const ?LazySrcLoc,
11793 seen_errors: *const std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc),
11794 seen_ranges: []const RangeSet.Range,
11795 seen_true: bool,
11796 seen_false: bool,
11797 seen_void: bool,
11798
11799 fn next(it: *UnhandledIterator, sema: *Sema, item_ty: Type) CompileError!?Value {
11800 const pt = sema.pt;
11801 const zcu = pt.zcu;
11802 const ip = &zcu.intern_pool;
11803 switch (item_ty.zigTypeTag(zcu)) {
11804 .@"union" => unreachable,
11805 .@"enum" => {
11806 for (it.seen_enum_fields[it.next_idx..], it.next_idx..) |seen_field, field_i| {
11807 if (seen_field != null) continue;
11808 it.next_idx = @intCast(field_i + 1);
11809 return try pt.enumValueFieldIndex(item_ty, @intCast(field_i));
11810 }
11811 return null;
11812 },
11813 .error_set => {
11814 for (it.error_names.get(ip)[it.next_idx..], it.next_idx..) |err_name, name_i| {
11815 if (it.seen_errors.contains(err_name)) continue;
11816 it.next_idx = @intCast(name_i + 1);
11817 return .fromInterned(try pt.intern(.{ .err = .{
11818 .ty = item_ty.toIntern(),
11819 .name = err_name,
11820 } }));
11821 }
11822 return null;
11823 },
11824 .int => {
11825 var cur = it.next_val orelse return null;
11826 while (it.next_idx < it.seen_ranges.len and
11827 cur.eql(it.seen_ranges[it.next_idx].first, item_ty, zcu))
11828 {
11829 defer it.next_idx += 1;
11830 const incr = try arith.incrementDefinedInt(
11831 sema,
11832 item_ty,
11833 it.seen_ranges[it.next_idx].last,
11834 );
11835 if (incr.overflow) {
11836 it.next_val = null;
11837 return null;
11838 }
11839 cur = incr.val;
11840 }
11841 const incr = try arith.incrementDefinedInt(sema, item_ty, cur);
11842 it.next_val = if (incr.overflow) null else incr.val;
11843 return cur;
11844 },
11845 .bool => {
11846 if (!it.seen_true) {
11847 it.seen_true = true;
11848 return .true;
11849 }
11850 if (!it.seen_false) {
11851 it.seen_false = true;
11852 return .false;
11853 }
11854 return null;
11855 },
11856 .void => {
11857 if (!it.seen_void) {
11858 it.seen_void = true;
11859 return .void;
11860 }
11861 return null;
11862 },
11863 else => unreachable, // item type is not enumerable
11864 }
11865 }
11866 };
11867};
11868
11869/// Validates operand type and `else`/`_` prong usage, resolves all prong items
11870/// and checks them for duplicates/invalid ranges. Does not emit into `block`.
11871/// Reserves inst map space for all placeholders associated with `zir_switch`.
11872/// Contents of returned `ValidatedSwitchBlock` belong to `sema.arena`.
11873fn validateSwitchBlock(
11874 sema: *Sema,
11875 block: *Block,
11876 raw_operand: Air.Inst.Ref,
11877 operand_is_ref: bool,
11878 switch_inst: Zir.Inst.Index,
11879 zir_switch: *const Zir.UnwrappedSwitchBlock,
11880) CompileError!ValidatedSwitchBlock {
11881 const pt = sema.pt;
11882 const zcu = pt.zcu;
11883 const ip = &zcu.intern_pool;
11884 const gpa = sema.gpa;
11885 const arena = sema.arena;
11886
11887 const src_node_offset = zir_switch.switch_src_node_offset;
11888 const src = block.nodeOffset(src_node_offset);
11889 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
11890 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset });
11891 var extra_index = zir_switch.end;
11892
11893 // We want to map values to our placeholders later on.
11894 if (zir_switch.payload_capture_placeholder.unwrap()) |payload_capture_inst| {
11895 assert(payload_capture_inst != switch_inst); // malformed zir
11896 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{payload_capture_inst});
11897 }
11898 if (zir_switch.tag_capture_placeholder.unwrap()) |tag_capture_inst| {
11899 assert(tag_capture_inst != switch_inst); // malformed zir
11900 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst});
11901 }
11902
11903 const operand_ty: Type, const item_ty: Type = check_operand: {
11904 const operand_ty = operand_ty: {
11905 const raw_operand_ty = sema.typeOf(raw_operand);
11906 if (operand_is_ref) {
11907 try sema.checkPtrType(block, operand_src, raw_operand_ty, false);
11908 break :operand_ty raw_operand_ty.childType(zcu);
11909 }
11910 break :operand_ty raw_operand_ty;
11911 };
11912
11913 const item_ty: Type = item_ty: {
11914 switch (operand_ty.zigTypeTag(zcu)) {
11915 .@"enum",
11916 .error_set,
11917 .int,
11918 .comptime_int,
11919 .type,
11920 .enum_literal,
11921 .@"fn",
11922 .bool,
11923 .void,
11924 => break :item_ty operand_ty,
11925
11926 .@"union" => {
11927 try operand_ty.resolveFields(pt);
11928 const enum_ty = operand_ty.unionTagType(zcu) orelse {
11929 return sema.failWithOwnedErrorMsg(block, msg: {
11930 const msg = try sema.errMsg(operand_src, "switch on union with no attached enum", .{});
11931 errdefer msg.destroy(sema.gpa);
11932 if (operand_ty.srcLocOrNull(zcu)) |union_src| {
11933 try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{});
11934 }
11935 break :msg msg;
11936 });
11937 };
11938 break :item_ty enum_ty;
11939 },
11940
11941 .pointer => {
11942 if (!operand_ty.isSlice(zcu)) {
11943 break :item_ty operand_ty;
11944 }
11945 },
11946
11947 else => {},
11948 }
11949 return sema.fail(block, operand_src, "switch on type '{f}'", .{operand_ty.fmt(pt)});
11950 };
11951
11952 if (zir_switch.has_continue and !block.isComptime()) {
11953 if (try operand_ty.comptimeOnlySema(pt)) {
11954 // Even if the operand is comptime-known, this `switch` is runtime.
11955 return sema.failWithOwnedErrorMsg(block, msg: {
11956 const msg = try sema.errMsg(operand_src, "operand of switch loop has comptime-only type '{f}'", .{operand_ty.fmt(pt)});
11957 errdefer msg.destroy(gpa);
11958 try sema.errNote(operand_src, msg, "switch loops are evaluated at runtime outside of comptime scopes", .{});
11959 try sema.explainWhyTypeIsComptime(msg, operand_src, operand_ty);
11960 break :msg msg;
11961 });
11962 }
11963 try sema.validateRuntimeValue(block, operand_src, raw_operand);
11964 }
11965
11966 break :check_operand .{ operand_ty, item_ty };
11967 };
11968
11969 const has_else = zir_switch.else_case != null;
11970 const has_under = zir_switch.has_under;
11971
11972 var case_vals: std.ArrayList(Air.Inst.Ref) = .empty;
11973 try case_vals.ensureUnusedCapacity(arena, zir_switch.item_infos.len);
11974
11975 // Duplicate checking variables later also used for `inline else`.
11976 var seen_enum_fields: []?LazySrcLoc = &.{};
11977 var seen_errors: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc) = .empty;
11978 var seen_sparse_values: std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc) = .empty;
11979 var range_set: RangeSet = .empty;
11980 var true_src: ?LazySrcLoc = null;
11981 var false_src: ?LazySrcLoc = null;
11982 var void_src: ?LazySrcLoc = null;
11983
11984 var else_err_ty: ?Type = null;
11985
11986 const else_case = zir_switch.else_case orelse undefined;
11987
11988 switch (item_ty.zigTypeTag(zcu)) {
11989 .@"union" => unreachable,
11990 .@"enum" => {
11991 seen_enum_fields = try arena.alloc(?LazySrcLoc, item_ty.enumFieldCount(zcu));
11992 @memset(seen_enum_fields, null);
11993 // `range_set` is used for non-exhaustive enum values that do not
11994 // correspond to any tags. Since this is rare, we only allocate on
11995 // demand in `validateSwitchItem`.
11996 },
11997 .error_set => {
11998 try seen_errors.ensureUnusedCapacity(arena, zir_switch.totalItemsLen());
11999 },
12000 .int, .comptime_int => {
12001 try range_set.ensureUnusedCapacity(arena, zir_switch.totalItemsLen());
12002 },
12003 .enum_literal, .@"fn", .pointer, .type => {
12004 try seen_sparse_values.ensureUnusedCapacity(arena, zir_switch.totalItemsLen());
12005 },
12006 .bool, .void => {},
12007
12008 else => unreachable,
12009 }
12010
12011 // Validate for duplicate items and invalid ranges.
12012 var case_it = zir_switch.iterateCases();
12013 while (case_it.next()) |case| {
12014 const prong_info = case.prong_info;
12015 extra_index += prong_info.body_len;
12016 for (case.item_infos, 0..) |item_info, item_i| {
12017 const item_src = block.src(.{ .switch_case_item = .{
12018 .switch_node_offset = src_node_offset,
12019 .case_idx = case.index,
12020 .item_idx = .{ .kind = .single, .value = @intCast(item_i) },
12021 } });
12022 if (item_info.unwrap() == .under) {
12023 if (!operand_ty.isNonexhaustiveEnum(zcu)) return sema.failWithOwnedErrorMsg(block, msg: {
12024 const msg = try sema.errMsg(
12025 src,
12026 "'_' prong only allowed when switching on non-exhaustive enums",
12027 .{},
12028 );
12029 errdefer msg.destroy(gpa);
12030 try sema.errNote(
12031 item_src,
12032 msg,
12033 "'_' prong here",
12034 .{},
12035 );
12036 try sema.errNote(
12037 src,
12038 msg,
12039 "consider using 'else'",
12040 .{},
12041 );
12042 break :msg msg;
12043 });
12044 case_vals.appendAssumeCapacity(.none);
12045 } else {
12046 const item, extra_index = try sema.resolveSwitchItem(block, item_src, item_ty, item_info, extra_index, switch_inst, prong_info.is_comptime_unreach);
12047 try sema.validateSwitchItemOrRange(block, item_src, item.val, null, item_ty, seen_enum_fields, &seen_errors, &seen_sparse_values, &range_set, &true_src, &false_src, &void_src);
12048 case_vals.appendAssumeCapacity(item.ref);
12049 }
12050 }
12051 for (case.range_infos, 0..) |range_info, range_i| {
12052 const range_offset: LazySrcLoc.Offset.SwitchItem = .{
12053 .switch_node_offset = src_node_offset,
12054 .case_idx = case.index,
12055 .item_idx = .{ .kind = .range, .value = @intCast(range_i) },
12056 };
12057 const range_src = block.src(.{ .switch_case_item = range_offset });
12058 const first_src = block.src(.{ .switch_case_item_range_first = range_offset });
12059 const last_src = block.src(.{ .switch_case_item_range_last = range_offset });
12060 const first_item, extra_index = try sema.resolveSwitchItem(block, first_src, item_ty, range_info[0], extra_index, switch_inst, prong_info.is_comptime_unreach);
12061 const last_item, extra_index = try sema.resolveSwitchItem(block, last_src, item_ty, range_info[1], extra_index, switch_inst, prong_info.is_comptime_unreach);
12062 try sema.validateSwitchItemOrRange(block, range_src, first_item.val, last_item.val, item_ty, seen_enum_fields, &seen_errors, &seen_sparse_values, &range_set, &true_src, &false_src, &void_src);
12063 case_vals.appendSliceAssumeCapacity(&.{ first_item.ref, last_item.ref });
12064 }
12065 }
12066
12067 switch (item_ty.zigTypeTag(zcu)) {
12068 .@"union" => unreachable,
12069 .int, .comptime_int => {},
12070 else => if (zir_switch.anyRanges()) {
12071 const range_src = block.src(.{ .node_offset_switch_range = src_node_offset });
12072 const msg = msg: {
12073 const msg = try sema.errMsg(
12074 operand_src,
12075 "ranges not allowed when switching on type '{f}'",
12076 .{operand_ty.fmt(sema.pt)},
12077 );
12078 errdefer msg.destroy(sema.gpa);
12079 try sema.errNote(
12080 range_src,
12081 msg,
12082 "range here",
12083 .{},
12084 );
12085 break :msg msg;
12086 };
12087 return sema.failWithOwnedErrorMsg(block, msg);
12088 },
12089 }
12090
12091 // Validate for missing special prongs.
12092 switch (item_ty.zigTypeTag(zcu)) {
12093 .@"union" => unreachable,
12094 .@"enum" => {
12095 const all_tags_handled = for (seen_enum_fields) |seen_src| {
12096 if (seen_src == null) break false;
12097 } else true;
12098
12099 if (has_else) {
12100 if (all_tags_handled) {
12101 if (item_ty.isNonexhaustiveEnum(zcu)) {
12102 if (has_under) return sema.fail(
12103 block,
1180012104 else_prong_src,
1180112105 "unreachable else prong; all explicit cases already handled",
1180212106 .{},
......@@ -11819,9 +12123,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1181912123 for (seen_enum_fields, 0..) |seen_src, i| {
1182012124 if (seen_src != null) continue;
1182112125
11822 const field_name = cond_ty.enumFieldName(i, zcu);
12126 const field_name = item_ty.enumFieldName(i, zcu);
1182312127 try sema.addFieldErrNote(
11824 cond_ty,
12128 item_ty,
1182512129 i,
1182612130 msg,
1182712131 "unhandled enumeration value: '{f}'",
......@@ -11829,15 +12133,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1182912133 );
1183012134 }
1183112135 try sema.errNote(
11832 cond_ty.srcLoc(zcu),
12136 item_ty.srcLoc(zcu),
1183312137 msg,
1183412138 "enum '{f}' declared here",
11835 .{cond_ty.fmt(pt)},
12139 .{item_ty.fmt(pt)},
1183612140 );
1183712141 break :msg msg;
1183812142 };
1183912143 return sema.failWithOwnedErrorMsg(block, msg);
11840 } else if (special_prongs == .none and cond_ty.isNonexhaustiveEnum(zcu) and !union_originally) {
12144 } else if (!has_else and !has_under and
12145 item_ty.isNonexhaustiveEnum(zcu) and operand_ty.zigTypeTag(zcu) != .@"union")
12146 {
1184112147 return sema.fail(
1184212148 block,
1184312149 src,
......@@ -11846,101 +12152,83 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1184612152 );
1184712153 }
1184812154 },
11849 .error_set => else_error_ty = try validateErrSetSwitch(
11850 sema,
11851 block,
11852 &seen_errors,
11853 &case_vals,
11854 cond_ty,
11855 inst_data,
11856 scalar_cases_len,
11857 multi_cases_len,
11858 .{ .body = special_else.body, .end = special_else.end, .src = else_prong_src },
11859 has_else,
11860 ),
11861 .int, .comptime_int => {
11862 var extra_index: usize = special_end;
11863 {
11864 var scalar_i: u32 = 0;
11865 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
11866 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
11867 extra_index += 1;
11868 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11869 extra_index += 1 + info.body_len;
11870
11871 case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt(
11872 block,
11873 &range_set,
11874 item_ref,
11875 cond_ty,
11876 block.src(.{ .switch_case_item = .{
11877 .switch_node_offset = src_node_offset,
11878 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
11879 .item_idx = .{ .kind = .single, .index = 0 },
11880 } }),
11881 ));
11882 }
11883 }
11884 {
11885 var multi_i: u32 = 0;
11886 while (multi_i < multi_cases_len) : (multi_i += 1) {
11887 const items_len = sema.code.extra[extra_index];
11888 extra_index += 1;
11889 const ranges_len = sema.code.extra[extra_index];
11890 extra_index += 1;
11891 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11892 extra_index += 1;
11893 const items = sema.code.refSlice(extra_index, items_len);
11894 extra_index += items_len;
11895
11896 try case_vals.ensureUnusedCapacity(gpa, items.len);
11897 for (items, 0..) |item_ref, item_i| {
11898 case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt(
12155 .error_set => {
12156 else_err_ty = ty: switch (try sema.resolveInferredErrorSetTy(block, src, item_ty.toIntern())) {
12157 .anyerror_type => {
12158 if (!has_else) {
12159 return sema.fail(
1189912160 block,
11900 &range_set,
11901 item_ref,
11902 cond_ty,
11903 block.src(.{ .switch_case_item = .{
11904 .switch_node_offset = src_node_offset,
11905 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
11906 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
11907 } }),
11908 ));
12161 src,
12162 "else prong required when switching on type 'anyerror'",
12163 .{},
12164 );
12165 }
12166 break :ty .anyerror;
12167 },
12168 else => |err_set_ty_index| {
12169 const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names;
12170 var maybe_msg: ?*Zcu.ErrorMsg = null;
12171 errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa);
12172
12173 var seen_errors_from_set: u32 = 0;
12174 for (error_names.get(ip)) |error_name| {
12175 if (seen_errors.contains(error_name)) {
12176 seen_errors_from_set += 1;
12177 } else if (!has_else) {
12178 const msg = maybe_msg orelse blk: {
12179 maybe_msg = try sema.errMsg(
12180 src,
12181 "switch must handle all possibilities",
12182 .{},
12183 );
12184 break :blk maybe_msg.?;
12185 };
12186
12187 try sema.errNote(
12188 src,
12189 msg,
12190 "unhandled error value: 'error.{f}'",
12191 .{error_name.fmt(ip)},
12192 );
12193 }
1190912194 }
1191012195
11911 try case_vals.ensureUnusedCapacity(gpa, 2 * ranges_len);
11912 var range_i: u32 = 0;
11913 while (range_i < ranges_len) : (range_i += 1) {
11914 const item_first: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
11915 extra_index += 1;
11916 const item_last: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
11917 extra_index += 1;
12196 if (maybe_msg) |msg| {
12197 maybe_msg = null;
12198 try sema.addDeclaredHereNote(msg, operand_ty);
12199 return sema.failWithOwnedErrorMsg(block, msg);
12200 }
1191812201
11919 const vals = try sema.validateSwitchRange(
12202 if (has_else and seen_errors_from_set == error_names.len) {
12203 // This prong is unreachable anyway so we don't need its
12204 // error set type, but we still allow it to exist.
12205 if (else_case.is_simple_noreturn) break :ty null;
12206 return sema.fail(
1192012207 block,
11921 &range_set,
11922 item_first,
11923 item_last,
11924 cond_ty,
11925 block.src(.{ .switch_case_item = .{
11926 .switch_node_offset = src_node_offset,
11927 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
11928 .item_idx = .{ .kind = .range, .index = @intCast(range_i) },
11929 } }),
12208 else_prong_src,
12209 "unreachable else prong; all cases already handled",
12210 .{},
1193012211 );
11931 case_vals.appendAssumeCapacity(vals[0]);
11932 case_vals.appendAssumeCapacity(vals[1]);
1193312212 }
1193412213
11935 extra_index += info.body_len;
11936 }
11937 }
11938
12214 var names: InferredErrorSet.NameMap = .{};
12215 try names.ensureUnusedCapacity(sema.arena, error_names.len);
12216 for (error_names.get(ip)) |error_name| {
12217 if (seen_errors.contains(error_name)) continue;
12218 names.putAssumeCapacityNoClobber(error_name, {});
12219 }
12220 // No need to keep the hash map metadata correct; here we
12221 // extract the (sorted) keys only.
12222 break :ty try pt.errorSetFromUnsortedNames(names.keys());
12223 },
12224 };
12225 },
12226 .int, .comptime_int => |type_tag| {
1193912227 check_range: {
11940 if (cond_ty.zigTypeTag(zcu) == .int) {
11941 const min_int = try cond_ty.minInt(pt, cond_ty);
11942 const max_int = try cond_ty.maxInt(pt, cond_ty);
11943 if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) {
12228 if (type_tag == .int) {
12229 const min_int = try item_ty.minInt(pt, item_ty);
12230 const max_int = try item_ty.maxInt(pt, item_ty);
12231 if (try range_set.spans(arena, min_int, max_int, item_ty, zcu)) {
1194412232 if (has_else) {
1194512233 return sema.fail(
1194612234 block,
......@@ -11952,7 +12240,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1195212240 break :check_range;
1195312241 }
1195412242 }
11955 if (special_prongs == .none) {
12243 if (!has_else) {
1195612244 return sema.fail(
1195712245 block,
1195812246 src,
......@@ -11962,61 +12250,24 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1196212250 }
1196312251 }
1196412252 },
11965 .bool => {
11966 var extra_index: usize = special_end;
11967 {
11968 var scalar_i: u32 = 0;
11969 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
11970 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
11971 extra_index += 1;
11972 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11973 extra_index += 1 + info.body_len;
11974
11975 case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool(
11976 block,
11977 &true_count,
11978 &false_count,
11979 item_ref,
11980 block.src(.{ .switch_case_item = .{
11981 .switch_node_offset = src_node_offset,
11982 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
11983 .item_idx = .{ .kind = .single, .index = 0 },
11984 } }),
11985 ));
11986 }
11987 }
11988 {
11989 var multi_i: u32 = 0;
11990 while (multi_i < multi_cases_len) : (multi_i += 1) {
11991 const items_len = sema.code.extra[extra_index];
11992 extra_index += 1;
11993 const ranges_len = sema.code.extra[extra_index];
11994 extra_index += 1;
11995 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
11996 extra_index += 1;
11997 const items = sema.code.refSlice(extra_index, items_len);
11998 extra_index += items_len + info.body_len;
11999
12000 try case_vals.ensureUnusedCapacity(gpa, items.len);
12001 for (items, 0..) |item_ref, item_i| {
12002 case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool(
12003 block,
12004 &true_count,
12005 &false_count,
12006 item_ref,
12007 block.src(.{ .switch_case_item = .{
12008 .switch_node_offset = src_node_offset,
12009 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12010 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
12011 } }),
12012 ));
12013 }
12014
12015 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);
12016 }
12253 .enum_literal, .@"fn", .pointer, .type => {
12254 if (!has_else) {
12255 return sema.fail(
12256 block,
12257 src,
12258 "else prong required when switching on type '{f}'",
12259 .{item_ty.fmt(pt)},
12260 );
1201712261 }
12262 },
12263 .bool, .void => |type_tag| {
12264 const all_values_handled = switch (type_tag) {
12265 .bool => true_src != null and false_src != null,
12266 .void => void_src != null,
12267 else => unreachable,
12268 };
1201812269 if (has_else) {
12019 if (true_count + false_count == 2) {
12270 if (all_values_handled) {
1202012271 return sema.fail(
1202112272 block,
1202212273 else_prong_src,
......@@ -12025,7 +12276,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1202512276 );
1202612277 }
1202712278 } else {
12028 if (true_count + false_count < 2) {
12279 if (!all_values_handled) {
1202912280 return sema.fail(
1203012281 block,
1203112282 src,
......@@ -12035,1775 +12286,1086 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1203512286 }
1203612287 }
1203712288 },
12038 .enum_literal, .void, .@"fn", .pointer, .type => {
12039 if (!has_else) {
12040 return sema.fail(
12041 block,
12042 src,
12043 "else prong required when switching on type '{f}'",
12044 .{cond_ty.fmt(pt)},
12045 );
12046 }
12047
12048 var seen_values = ValueSrcMap{};
12049 defer seen_values.deinit(gpa);
12050
12051 var extra_index: usize = special_end;
12052 {
12053 var scalar_i: u32 = 0;
12054 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
12055 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
12056 extra_index += 1;
12057 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
12058 extra_index += 1;
12059 extra_index += info.body_len;
12060
12061 case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse(
12062 block,
12063 &seen_values,
12064 item_ref,
12065 cond_ty,
12066 block.src(.{ .switch_case_item = .{
12067 .switch_node_offset = src_node_offset,
12068 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
12069 .item_idx = .{ .kind = .single, .index = 0 },
12070 } }),
12071 ));
12072 }
12073 }
12074 {
12075 var multi_i: u32 = 0;
12076 while (multi_i < multi_cases_len) : (multi_i += 1) {
12077 const items_len = sema.code.extra[extra_index];
12078 extra_index += 1;
12079 const ranges_len = sema.code.extra[extra_index];
12080 extra_index += 1;
12081 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
12082 extra_index += 1;
12083 const items = sema.code.refSlice(extra_index, items_len);
12084 extra_index += items_len + info.body_len;
12085
12086 try case_vals.ensureUnusedCapacity(gpa, items.len);
12087 for (items, 0..) |item_ref, item_i| {
12088 case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse(
12089 block,
12090 &seen_values,
12091 item_ref,
12092 cond_ty,
12093 block.src(.{ .switch_case_item = .{
12094 .switch_node_offset = src_node_offset,
12095 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12096 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
12097 } }),
12098 ));
12099 }
12100
12101 try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);
12102 }
12103 }
12104 },
12105
12106 .error_union,
12107 .noreturn,
12108 .array,
12109 .@"struct",
12110 .undefined,
12111 .null,
12112 .optional,
12113 .@"opaque",
12114 .vector,
12115 .frame,
12116 .@"anyframe",
12117 .comptime_float,
12118 .float,
12119 => return sema.fail(block, operand_src, "invalid switch operand type '{f}'", .{
12120 raw_operand_ty.fmt(pt),
12121 }),
12289 else => unreachable,
1212212290 }
1212312291
12124 var special_members_only: ?SpecialProng = null;
12125 var special_members_only_src: LazySrcLoc = undefined;
12126 const special_generic, const special_generic_src = if (has_under) b: {
12127 if (has_else) {
12128 special_members_only = special_else;
12129 special_members_only_src = else_prong_src;
12130 }
12131 break :b .{ special_under, under_prong_src };
12132 } else .{ special_else, else_prong_src };
12133
12134 const spa: SwitchProngAnalysis = .{
12135 .sema = sema,
12136 .parent_block = block,
12137 .operand = operand,
12138 .else_error_ty = else_error_ty,
12139 .switch_block_inst = inst,
12140 .tag_capture_inst = tag_capture_inst,
12141 };
12142
12143 const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
12144 try sema.air_instructions.append(gpa, .{
12145 .tag = .block,
12146 .data = undefined,
12147 });
12148 var label: Block.Label = .{
12149 .zir_block = inst,
12150 .merges = .{
12151 .src_locs = .{},
12152 .results = .{},
12153 .br_list = .{},
12154 .block_inst = block_inst,
12155 },
12156 };
12292 return .{
12293 .seen_enum_fields = seen_enum_fields,
12294 .seen_errors = seen_errors,
12295 .seen_ranges = range_set.ranges.items,
12296 .true_src = true_src,
12297 .false_src = false_src,
12298 .void_src = void_src,
1215712299
12158 var child_block: Block = .{
12159 .parent = block,
12160 .sema = sema,
12161 .namespace = block.namespace,
12162 .instructions = .{},
12163 .label = &label,
12164 .inlining = block.inlining,
12165 .comptime_reason = block.comptime_reason,
12166 .is_typeof = block.is_typeof,
12167 .c_import_buf = block.c_import_buf,
12168 .runtime_cond = block.runtime_cond,
12169 .runtime_loop = block.runtime_loop,
12170 .runtime_index = block.runtime_index,
12171 .want_safety = block.want_safety,
12172 .error_return_trace_index = block.error_return_trace_index,
12173 .src_base_inst = block.src_base_inst,
12174 .type_name_ctx = block.type_name_ctx,
12300 .case_vals = case_vals.items,
12301 .else_case = else_case,
12302 .else_err_ty = else_err_ty,
1217512303 };
12176 const merges = &child_block.label.?.merges;
12177 defer child_block.instructions.deinit(gpa);
12178 defer merges.deinit(gpa);
12179
12180 if (scalar_cases_len + multi_cases_len == 0 and
12181 special_members_only == null and
12182 !special_generic.is_inline)
12183 {
12184 if (empty_enum) {
12185 return .void_value;
12186 }
12187 if (special_prongs == .none) {
12188 return sema.fail(block, src, "switch must handle all possibilities", .{});
12189 }
12190 const init_cond = switch (operand) {
12191 .simple => |s| s.cond,
12192 .loop => |l| l.init_cond,
12193 };
12194 if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and
12195 raw_operand_ty.zigTypeTag(zcu) == .@"enum" and !raw_operand_ty.isNonexhaustiveEnum(zcu))
12196 {
12197 try sema.zirDbgStmt(block, cond_dbg_node_index);
12198 const ok = try block.addUnOp(.is_named_enum_value, init_cond);
12199 try sema.addSafetyCheck(block, src, ok, .corrupt_switch);
12200 }
12201 if (err_set and try sema.maybeErrorUnwrap(block, special_generic.body, init_cond, operand_src, false)) {
12202 return .unreachable_value;
12203 }
12204 }
12205
12206 switch (operand) {
12207 .loop => {}, // always runtime; evaluation in comptime scope uses `simple`
12208 .simple => |s| {
12209 if (try sema.resolveDefinedValue(&child_block, src, s.cond)) |cond_val| {
12210 return resolveSwitchComptimeLoop(
12211 sema,
12212 spa,
12213 &child_block,
12214 if (operand_is_ref)
12215 sema.typeOf(s.by_ref)
12216 else
12217 raw_operand_ty,
12218 cond_ty,
12219 cond_val,
12220 src_node_offset,
12221 special_members_only,
12222 special_generic,
12223 has_under,
12224 case_vals,
12225 scalar_cases_len,
12226 multi_cases_len,
12227 err_set,
12228 empty_enum,
12229 operand_is_ref,
12230 );
12231 }
12232
12233 if (scalar_cases_len + multi_cases_len == 0 and
12234 special_members_only == null and
12235 !special_generic.is_inline and
12236 !extra.data.bits.has_continue)
12237 {
12238 return spa.resolveProngComptime(
12239 &child_block,
12240 .special,
12241 special_generic.body,
12242 special_generic.capture,
12243 block.src(.{ .switch_capture = .{
12244 .switch_node_offset = src_node_offset,
12245 .case_idx = if (has_under) .special_under else .special_else,
12246 } }),
12247 undefined, // case_vals may be undefined for special prongs
12248 .none,
12249 false,
12250 merges,
12251 );
12252 }
12253 },
12254 }
12255
12256 if (child_block.isComptime()) {
12257 _ = try sema.resolveConstDefinedValue(&child_block, operand_src, operand.simple.cond, null);
12258 unreachable;
12259 }
12260
12261 var extra_case_vals: struct {
12262 items: std.ArrayList(Air.Inst.Ref),
12263 ranges: std.ArrayList([2]Air.Inst.Ref),
12264 } = .{ .items = .empty, .ranges = .empty };
12265 defer {
12266 extra_case_vals.items.deinit(gpa);
12267 extra_case_vals.ranges.deinit(gpa);
12268 }
12269
12270 // Runtime switch, if we have a special_members_only prong we need to unroll
12271 // it to a prong with explicit items.
12272 // Although this is potentially the same as `inline else` it does not count
12273 // towards the backward branch quota because it's an implementation detail.
12274 if (special_members_only != null) gen: {
12275 assert(cond_ty.isNonexhaustiveEnum(zcu));
12276
12277 var min_i: usize = math.maxInt(usize);
12278 var max_i: usize = 0;
12279 var seen_field_count: usize = 0;
12280 for (seen_enum_fields, 0..) |seen, enum_i| {
12281 if (seen != null) {
12282 seen_field_count += 1;
12283 } else {
12284 min_i = @min(min_i, enum_i);
12285 max_i = @max(max_i, enum_i);
12286 }
12287 }
12288 if (min_i == max_i) {
12289 seen_enum_fields[min_i] = special_members_only_src;
12290 const item_val = try pt.enumValueFieldIndex(cond_ty, @intCast(min_i));
12291 const item_ref = Air.internedToRef(item_val.toIntern());
12292 try extra_case_vals.items.append(gpa, item_ref);
12293 break :gen;
12294 }
12295 const missing_field_count = seen_enum_fields.len - seen_field_count;
12296
12297 extra_case_vals.items = try .initCapacity(gpa, missing_field_count / 2);
12298 extra_case_vals.ranges = try .initCapacity(gpa, missing_field_count / 4);
12299 const int_ty = cond_ty.intTagType(zcu);
12300
12301 var last_val = try pt.enumValueFieldIndex(cond_ty, @intCast(min_i));
12302 var first_ref = Air.internedToRef(last_val.toIntern());
12303 seen_enum_fields[min_i] = special_members_only_src;
12304 for (seen_enum_fields[(min_i + 1)..(max_i + 1)], (min_i + 1)..) |seen, enum_i| {
12305 if (seen != null) continue;
12306 seen_enum_fields[enum_i] = special_members_only_src;
12307
12308 const item_val = try pt.enumValueFieldIndex(cond_ty, @intCast(enum_i));
12309 const item_ref = Air.internedToRef(item_val.toIntern());
12310
12311 const is_next = is_next: {
12312 const prev_int = ip.indexToKey(last_val.toIntern()).enum_tag.int;
12313
12314 const result = try arith.incrementDefinedInt(sema, int_ty, .fromInterned(prev_int));
12315 if (result.overflow) break :is_next false;
12316
12317 const item_int = ip.indexToKey(item_val.toIntern()).enum_tag.int;
12318 break :is_next try sema.valuesEqual(.fromInterned(item_int), result.val, int_ty);
12319 };
12320
12321 if (is_next) {
12322 last_val = item_val;
12323 } else {
12324 const last_ref = Air.internedToRef(last_val.toIntern());
12325 if (first_ref == last_ref) {
12326 try extra_case_vals.items.append(gpa, first_ref);
12327 } else {
12328 try extra_case_vals.ranges.append(gpa, .{ first_ref, last_ref });
12329 }
12330 first_ref = item_ref;
12331 last_val = item_val;
12332 }
12333 }
12334 const last_ref = Air.internedToRef(last_val.toIntern());
12335 if (first_ref == last_ref) {
12336 try extra_case_vals.items.append(gpa, first_ref);
12337 } else {
12338 try extra_case_vals.ranges.append(gpa, .{ first_ref, last_ref });
12339 }
12340 }
12341
12342 const air_switch_ref = try sema.analyzeSwitchRuntimeBlock(
12343 spa,
12344 &child_block,
12345 src,
12346 switch (operand) {
12347 .simple => |s| s.cond,
12348 .loop => |l| l.init_cond,
12349 },
12350 cond_ty,
12351 operand_src,
12352 case_vals,
12353 special_generic,
12354 scalar_cases_len,
12355 multi_cases_len,
12356 union_originally,
12357 raw_operand_ty,
12358 err_set,
12359 src_node_offset,
12360 special_generic_src,
12361 has_under,
12362 seen_enum_fields,
12363 seen_errors,
12364 range_set,
12365 true_count,
12366 false_count,
12367 cond_dbg_node_index,
12368 false,
12369 special_members_only,
12370 special_members_only_src,
12371 extra_case_vals.items.items,
12372 extra_case_vals.ranges.items,
12373 );
12374
12375 for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| {
12376 var replacement_block = block.makeSubBlock();
12377 defer replacement_block.instructions.deinit(gpa);
12378
12379 assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .br);
12380 const new_operand_maybe_ref = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].br.operand;
12381
12382 if (extra.data.bits.any_non_inline_capture) {
12383 _ = try replacement_block.addBinOp(.store, operand.loop.operand_alloc, new_operand_maybe_ref);
12384 }
12385
12386 const new_operand_val = if (operand_is_ref)
12387 try sema.analyzeLoad(&replacement_block, dispatch_src, new_operand_maybe_ref, dispatch_src)
12388 else
12389 new_operand_maybe_ref;
12390
12391 const new_cond = try sema.switchCond(&replacement_block, dispatch_src, new_operand_val);
12392
12393 if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and
12394 cond_ty.zigTypeTag(zcu) == .@"enum" and !cond_ty.isNonexhaustiveEnum(zcu) and
12395 !try sema.isComptimeKnown(new_cond))
12396 {
12397 const ok = try replacement_block.addUnOp(.is_named_enum_value, new_cond);
12398 try sema.addSafetyCheck(&replacement_block, src, ok, .corrupt_switch);
12399 }
12400
12401 _ = try replacement_block.addInst(.{
12402 .tag = .switch_dispatch,
12403 .data = .{ .br = .{
12404 .block_inst = air_switch_ref.toIndex().?,
12405 .operand = new_cond,
12406 } },
12407 });
12408
12409 if (replacement_block.instructions.items.len == 1) {
12410 // Optimization: we don't need a block!
12411 sema.air_instructions.set(
12412 @intFromEnum(placeholder_inst),
12413 sema.air_instructions.get(@intFromEnum(replacement_block.instructions.items[0])),
12414 );
12415 continue;
12416 }
12304}
1241712305
12418 // Replace placeholder with a block.
12419 // No `br` is needed as the block is a switch dispatch so necessarily `noreturn`.
12420 try sema.air_extra.ensureUnusedCapacity(
12421 gpa,
12422 @typeInfo(Air.Block).@"struct".fields.len + replacement_block.instructions.items.len,
12423 );
12424 sema.air_instructions.set(@intFromEnum(placeholder_inst), .{
12425 .tag = .block,
12426 .data = .{ .ty_pl = .{
12427 .ty = .noreturn_type,
12428 .payload = sema.addExtraAssumeCapacity(Air.Block{
12429 .body_len = @intCast(replacement_block.instructions.items.len),
12430 }),
12431 } },
12432 });
12433 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items));
12434 }
12435
12436 return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false);
12437}
12438
12439const SpecialProng = struct {
12440 body: []const Zir.Inst.Index,
12441 end: usize,
12442 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
12443 is_inline: bool,
12444 has_tag_capture: bool,
12445};
12446
12447fn analyzeSwitchRuntimeBlock(
12306fn resolveSwitchBlock(
1244812307 sema: *Sema,
12449 spa: SwitchProngAnalysis,
12450 child_block: *Block,
12451 src: LazySrcLoc,
12452 operand: Air.Inst.Ref,
12453 operand_ty: Type,
12454 operand_src: LazySrcLoc,
12455 case_vals: std.ArrayList(Air.Inst.Ref),
12456 else_prong: SpecialProng,
12457 scalar_cases_len: usize,
12458 multi_cases_len: usize,
12459 union_originally: bool,
12460 maybe_union_ty: Type,
12461 err_set: bool,
12462 switch_node_offset: std.zig.Ast.Node.Offset,
12463 else_prong_src: LazySrcLoc,
12464 else_prong_is_underscore: bool,
12465 seen_enum_fields: []?LazySrcLoc,
12466 seen_errors: SwitchErrorSet,
12467 range_set: RangeSet,
12468 true_count: u8,
12469 false_count: u8,
12470 cond_dbg_node_index: Zir.Inst.Index,
12471 allow_err_code_unwrap: bool,
12472 extra_prong: ?SpecialProng,
12473 /// May be `undefined` if `extra_prong` is `null`
12474 extra_prong_src: LazySrcLoc,
12475 extra_prong_items: []const Air.Inst.Ref,
12476 extra_prong_ranges: []const [2]Air.Inst.Ref,
12477) CompileError!Air.Inst.Ref {
12478 const pt = sema.pt;
12479 const zcu = pt.zcu;
12480 const gpa = sema.gpa;
12481 const ip = &zcu.intern_pool;
12482
12483 const block = child_block.parent.?;
12484
12485 const estimated_cases_extra = (scalar_cases_len + multi_cases_len) *
12486 @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 2;
12487 var cases_extra = try std.ArrayList(u32).initCapacity(gpa, estimated_cases_extra);
12488 defer cases_extra.deinit(gpa);
12489
12490 var branch_hints = try std.ArrayList(std.builtin.BranchHint).initCapacity(gpa, scalar_cases_len);
12491 defer branch_hints.deinit(gpa);
12492
12493 var case_block = child_block.makeSubBlock();
12494 case_block.runtime_loop = null;
12495 case_block.runtime_cond = operand_src;
12496 case_block.runtime_index.increment();
12497 case_block.need_debug_scope = null; // this body is emitted regardless
12498 defer case_block.instructions.deinit(gpa);
12499
12500 var extra_index: usize = else_prong.end;
12501
12502 var scalar_i: usize = 0;
12503 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
12504 extra_index += 1;
12505 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
12506 extra_index += 1;
12507 const body = sema.code.bodySlice(extra_index, info.body_len);
12508 extra_index += info.body_len;
12509
12510 case_block.instructions.shrinkRetainingCapacity(0);
12511 case_block.error_return_trace_index = child_block.error_return_trace_index;
12512
12513 const item = case_vals.items[scalar_i];
12514 // `item` is already guaranteed to be constant known.
12515
12516 const analyze_body = if (union_originally) blk: {
12517 const unresolved_item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
12518 const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable;
12519 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
12520 break :blk field_ty.zigTypeTag(zcu) != .noreturn;
12521 } else true;
12522
12523 const prong_hint: std.builtin.BranchHint = if (err_set and
12524 try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap))
12525 h: {
12526 // nothing to do here. weight against error branch
12527 break :h .unlikely;
12528 } else if (analyze_body) h: {
12529 break :h try spa.analyzeProngRuntime(
12530 &case_block,
12531 .normal,
12532 body,
12533 info.capture,
12534 child_block.src(.{ .switch_capture = .{
12535 .switch_node_offset = switch_node_offset,
12536 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
12537 } }),
12538 &.{item},
12539 if (info.is_inline) item else .none,
12540 info.has_tag_capture,
12541 );
12542 } else h: {
12543 _ = try case_block.addNoOp(.unreach);
12544 break :h .none;
12545 };
12546
12547 try branch_hints.append(gpa, prong_hint);
12548 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12549 1 + // `item`, no ranges
12550 case_block.instructions.items.len);
12551 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12552 .items_len = 1,
12553 .ranges_len = 0,
12554 .body_len = @intCast(case_block.instructions.items.len),
12555 }));
12556 cases_extra.appendAssumeCapacity(@intFromEnum(item));
12557 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12558 }
12559
12560 var cases_len = scalar_cases_len;
12561 var case_val_idx: usize = scalar_cases_len;
12562 const multi_cases_len_with_extra_prong = multi_cases_len + @intFromBool(extra_prong != null);
12563 var multi_i: u32 = 0;
12564 while (multi_i < multi_cases_len_with_extra_prong) : (multi_i += 1) {
12565 const is_extra_prong = multi_i == multi_cases_len;
12566 var items: []const Air.Inst.Ref = undefined;
12567 var info: Zir.Inst.SwitchBlock.ProngInfo = undefined;
12568 var ranges: []const [2]Air.Inst.Ref = undefined;
12569 var body: []const Zir.Inst.Index = undefined;
12570 if (is_extra_prong) {
12571 const prong = extra_prong.?;
12572 items = extra_prong_items;
12573 ranges = extra_prong_ranges;
12574 body = prong.body;
12575 info = .{
12576 .body_len = undefined,
12577 .capture = prong.capture,
12578 .is_inline = prong.is_inline,
12579 .has_tag_capture = prong.has_tag_capture,
12580 };
12581 } else {
12582 @branchHint(.likely);
12583 const items_len = sema.code.extra[extra_index];
12584 extra_index += 1;
12585 const ranges_len = sema.code.extra[extra_index];
12586 extra_index += 1;
12587 info = @bitCast(sema.code.extra[extra_index]);
12588 extra_index += 1 + items_len + ranges_len * 2;
12589
12590 items = case_vals.items[case_val_idx..][0..items_len];
12591 case_val_idx += items_len;
12592 ranges = @ptrCast(case_vals.items[case_val_idx..][0 .. ranges_len * 2]);
12593 case_val_idx += ranges_len * 2;
12594
12595 body = sema.code.bodySlice(extra_index, info.body_len);
12596 extra_index += info.body_len;
12597 }
12598
12599 case_block.instructions.shrinkRetainingCapacity(0);
12600 case_block.error_return_trace_index = child_block.error_return_trace_index;
12601
12602 // Generate all possible cases as scalar prongs.
12603 if (info.is_inline) {
12604 var emit_bb = false;
12605
12606 for (ranges, 0..) |range_items, range_i| {
12607 var item = sema.resolveConstDefinedValue(block, .unneeded, range_items[0], undefined) catch unreachable;
12608 const item_last = sema.resolveConstDefinedValue(block, .unneeded, range_items[1], undefined) catch unreachable;
12609
12610 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({
12611 // Previous validation has resolved any possible lazy values.
12612 const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) {
12613 .int => .{ item, operand_ty },
12614 .@"enum" => b: {
12615 const int_val = Value.fromInterned(ip.indexToKey(item.toIntern()).enum_tag.int);
12616 break :b .{ int_val, int_val.typeOf(zcu) };
12617 },
12618 else => unreachable,
12619 };
12620 const result = try arith.incrementDefinedInt(sema, int_ty, int_val);
12621 assert(!result.overflow);
12622 item = switch (operand_ty.zigTypeTag(zcu)) {
12623 .int => result.val,
12624 .@"enum" => .fromInterned(try pt.intern(.{ .enum_tag = .{
12625 .ty = operand_ty.toIntern(),
12626 .int = result.val.toIntern(),
12627 } })),
12628 else => unreachable,
12629 };
12630 }) {
12631 cases_len += 1;
12632
12633 const item_ref = Air.internedToRef(item.toIntern());
12634
12635 case_block.instructions.shrinkRetainingCapacity(0);
12636 case_block.error_return_trace_index = child_block.error_return_trace_index;
12637
12638 if (emit_bb) {
12639 const bb_src = if (is_extra_prong) extra_prong_src else block.src(.{ .switch_case_item = .{
12640 .switch_node_offset = switch_node_offset,
12641 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12642 .item_idx = .{ .kind = .range, .index = @intCast(range_i) },
12643 } });
12644 try sema.emitBackwardBranch(block, bb_src);
12645 }
12646 emit_bb = true;
12647
12648 const prong_hint = try spa.analyzeProngRuntime(
12649 &case_block,
12650 .normal,
12651 body,
12652 info.capture,
12653 child_block.src(.{ .switch_capture = .{
12654 .switch_node_offset = switch_node_offset,
12655 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12656 } }),
12657 undefined, // case_vals may be undefined for ranges
12658 item_ref,
12659 info.has_tag_capture,
12660 );
12661 try branch_hints.append(gpa, prong_hint);
12662
12663 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12664 1 + // `item`, no ranges
12665 case_block.instructions.items.len);
12666 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12667 .items_len = 1,
12668 .ranges_len = 0,
12669 .body_len = @intCast(case_block.instructions.items.len),
12670 }));
12671 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
12672 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12673
12674 if (item.compareScalar(.eq, item_last, operand_ty, zcu)) break;
12675 }
12676 }
12677
12678 for (items, 0..) |item, item_i| {
12679 cases_len += 1;
12680
12681 case_block.instructions.shrinkRetainingCapacity(0);
12682 case_block.error_return_trace_index = child_block.error_return_trace_index;
12683
12684 const analyze_body = if (union_originally) blk: {
12685 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
12686 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
12687 break :blk field_ty.zigTypeTag(zcu) != .noreturn;
12688 } else true;
12689
12690 if (emit_bb) {
12691 const bb_src = if (is_extra_prong) extra_prong_src else block.src(.{ .switch_case_item = .{
12692 .switch_node_offset = switch_node_offset,
12693 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12694 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
12695 } });
12696 try sema.emitBackwardBranch(block, bb_src);
12697 }
12698 emit_bb = true;
12699
12700 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
12701 break :h try spa.analyzeProngRuntime(
12702 &case_block,
12703 .normal,
12704 body,
12705 info.capture,
12706 child_block.src(.{ .switch_capture = .{
12707 .switch_node_offset = switch_node_offset,
12708 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12709 } }),
12710 &.{item},
12711 item,
12712 info.has_tag_capture,
12713 );
12714 } else h: {
12715 _ = try case_block.addNoOp(.unreach);
12716 break :h .none;
12717 };
12718 try branch_hints.append(gpa, prong_hint);
12719
12720 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12721 1 + // `item`, no ranges
12722 case_block.instructions.items.len);
12723 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12724 .items_len = 1,
12725 .ranges_len = 0,
12726 .body_len = @intCast(case_block.instructions.items.len),
12727 }));
12728 cases_extra.appendAssumeCapacity(@intFromEnum(item));
12729 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12730 }
12731
12732 continue;
12733 }
12734
12735 cases_len += 1;
12736
12737 const analyze_body = if (union_originally)
12738 for (items) |item| {
12739 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
12740 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
12741 if (field_ty.zigTypeTag(zcu) != .noreturn) break true;
12742 } else false
12743 else
12744 true;
12745
12746 const prong_hint: std.builtin.BranchHint = if (err_set and
12747 try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap))
12748 h: {
12749 // nothing to do here. weight against error branch
12750 break :h .unlikely;
12751 } else if (analyze_body) h: {
12752 break :h try spa.analyzeProngRuntime(
12753 &case_block,
12754 .normal,
12755 body,
12756 info.capture,
12757 child_block.src(.{ .switch_capture = .{
12758 .switch_node_offset = switch_node_offset,
12759 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
12760 } }),
12761 items,
12762 .none,
12763 false,
12764 );
12765 } else h: {
12766 _ = try case_block.addNoOp(.unreach);
12767 break :h .none;
12768 };
12769
12770 try branch_hints.append(gpa, prong_hint);
12771
12772 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12773 items.len + ranges.len * 2 +
12774 case_block.instructions.items.len);
12775 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12776 .items_len = @intCast(items.len),
12777 .ranges_len = @intCast(ranges.len),
12778 .body_len = @intCast(case_block.instructions.items.len),
12779 }));
12780
12781 for (items) |item| {
12782 cases_extra.appendAssumeCapacity(@intFromEnum(item));
12783 }
12784 for (ranges) |range| {
12785 cases_extra.appendSliceAssumeCapacity(&.{
12786 @intFromEnum(range[0]),
12787 @intFromEnum(range[1]),
12788 });
12789 }
12790
12791 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12792 }
12793
12794 const else_body: []const Air.Inst.Index = if (else_prong.body.len != 0 or case_block.wantSafety()) else_body: {
12795 var emit_bb = false;
12796 // If this is true we must have a 'true' else prong and not an underscore because
12797 // underscore prongs can never be inlined. We've already checked for this.
12798 if (else_prong.is_inline) switch (operand_ty.zigTypeTag(zcu)) {
12799 .@"enum" => {
12800 if (operand_ty.isNonexhaustiveEnum(zcu) and !union_originally) {
12801 return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
12802 operand_ty.fmt(pt),
12803 });
12804 }
12805 for (seen_enum_fields, 0..) |f, i| {
12806 if (f != null) continue;
12807 cases_len += 1;
12808
12809 const item_val = try pt.enumValueFieldIndex(operand_ty, @intCast(i));
12810 const item_ref = Air.internedToRef(item_val.toIntern());
12811
12812 case_block.instructions.shrinkRetainingCapacity(0);
12813 case_block.error_return_trace_index = child_block.error_return_trace_index;
12814
12815 const analyze_body = if (union_originally) blk: {
12816 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
12817 break :blk field_ty.zigTypeTag(zcu) != .noreturn;
12818 } else true;
12819
12820 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12821 emit_bb = true;
12822
12823 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
12824 break :h try spa.analyzeProngRuntime(
12825 &case_block,
12826 .special,
12827 else_prong.body,
12828 else_prong.capture,
12829 child_block.src(.{ .switch_capture = .{
12830 .switch_node_offset = switch_node_offset,
12831 .case_idx = .special_else,
12832 } }),
12833 &.{item_ref},
12834 item_ref,
12835 else_prong.has_tag_capture,
12836 );
12837 } else h: {
12838 _ = try case_block.addNoOp(.unreach);
12839 break :h .none;
12840 };
12841 try branch_hints.append(gpa, prong_hint);
12842
12843 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12844 1 + // `item`, no ranges
12845 case_block.instructions.items.len);
12846 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12847 .items_len = 1,
12848 .ranges_len = 0,
12849 .body_len = @intCast(case_block.instructions.items.len),
12850 }));
12851 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
12852 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12853 }
12854 },
12855 .error_set => {
12856 if (operand_ty.isAnyError(zcu)) {
12857 return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
12858 operand_ty.fmt(pt),
12859 });
12860 }
12861 const error_names = operand_ty.errorSetNames(zcu);
12862 for (0..error_names.len) |name_index| {
12863 const error_name = error_names.get(ip)[name_index];
12864 if (seen_errors.contains(error_name)) continue;
12865 cases_len += 1;
12866
12867 const item_val = try pt.intern(.{ .err = .{
12868 .ty = operand_ty.toIntern(),
12869 .name = error_name,
12870 } });
12871 const item_ref = Air.internedToRef(item_val);
12872
12873 case_block.instructions.shrinkRetainingCapacity(0);
12874 case_block.error_return_trace_index = child_block.error_return_trace_index;
12875
12876 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12877 emit_bb = true;
12878
12879 const prong_hint = try spa.analyzeProngRuntime(
12880 &case_block,
12881 .special,
12882 else_prong.body,
12883 else_prong.capture,
12884 child_block.src(.{ .switch_capture = .{
12885 .switch_node_offset = switch_node_offset,
12886 .case_idx = .special_else,
12887 } }),
12888 &.{item_ref},
12889 item_ref,
12890 else_prong.has_tag_capture,
12891 );
12892 try branch_hints.append(gpa, prong_hint);
12893
12894 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12895 1 + // `item`, no ranges
12896 case_block.instructions.items.len);
12897 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12898 .items_len = 1,
12899 .ranges_len = 0,
12900 .body_len = @intCast(case_block.instructions.items.len),
12901 }));
12902 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
12903 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12904 }
12905 },
12906 .int => {
12907 var it = try RangeSetUnhandledIterator.init(sema, operand_ty, range_set);
12908 while (try it.next()) |cur| {
12909 cases_len += 1;
12910
12911 const item_ref = Air.internedToRef(cur);
12912
12913 case_block.instructions.shrinkRetainingCapacity(0);
12914 case_block.error_return_trace_index = child_block.error_return_trace_index;
12915
12916 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12917 emit_bb = true;
12918
12919 const prong_hint = try spa.analyzeProngRuntime(
12920 &case_block,
12921 .special,
12922 else_prong.body,
12923 else_prong.capture,
12924 child_block.src(.{ .switch_capture = .{
12925 .switch_node_offset = switch_node_offset,
12926 .case_idx = .special_else,
12927 } }),
12928 &.{item_ref},
12929 item_ref,
12930 else_prong.has_tag_capture,
12931 );
12932 try branch_hints.append(gpa, prong_hint);
12933
12934 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12935 1 + // `item`, no ranges
12936 case_block.instructions.items.len);
12937 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12938 .items_len = 1,
12939 .ranges_len = 0,
12940 .body_len = @intCast(case_block.instructions.items.len),
12941 }));
12942 cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
12943 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12944 }
12945 },
12946 .bool => {
12947 if (true_count == 0) {
12948 cases_len += 1;
12949
12950 case_block.instructions.shrinkRetainingCapacity(0);
12951 case_block.error_return_trace_index = child_block.error_return_trace_index;
12952
12953 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12954 emit_bb = true;
12955
12956 const prong_hint = try spa.analyzeProngRuntime(
12957 &case_block,
12958 .special,
12959 else_prong.body,
12960 else_prong.capture,
12961 child_block.src(.{ .switch_capture = .{
12962 .switch_node_offset = switch_node_offset,
12963 .case_idx = .special_else,
12964 } }),
12965 &.{.bool_true},
12966 .bool_true,
12967 else_prong.has_tag_capture,
12968 );
12969 try branch_hints.append(gpa, prong_hint);
12970
12971 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
12972 1 + // `item`, no ranges
12973 case_block.instructions.items.len);
12974 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12975 .items_len = 1,
12976 .ranges_len = 0,
12977 .body_len = @intCast(case_block.instructions.items.len),
12978 }));
12979 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_true));
12980 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
12981 }
12982 if (false_count == 0) {
12983 cases_len += 1;
12984
12985 case_block.instructions.shrinkRetainingCapacity(0);
12986 case_block.error_return_trace_index = child_block.error_return_trace_index;
12987
12988 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
12989 emit_bb = true;
12990
12991 const prong_hint = try spa.analyzeProngRuntime(
12992 &case_block,
12993 .special,
12994 else_prong.body,
12995 else_prong.capture,
12996 child_block.src(.{ .switch_capture = .{
12997 .switch_node_offset = switch_node_offset,
12998 .case_idx = .special_else,
12999 } }),
13000 &.{.bool_false},
13001 .bool_false,
13002 else_prong.has_tag_capture,
13003 );
13004 try branch_hints.append(gpa, prong_hint);
13005
13006 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
13007 1 + // `item`, no ranges
13008 case_block.instructions.items.len);
13009 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
13010 .items_len = 1,
13011 .ranges_len = 0,
13012 .body_len = @intCast(case_block.instructions.items.len),
13013 }));
13014 cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_false));
13015 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
13016 }
13017 },
13018 else => return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
13019 operand_ty.fmt(pt),
13020 }),
13021 };
13022
13023 case_block.instructions.shrinkRetainingCapacity(0);
13024 case_block.error_return_trace_index = child_block.error_return_trace_index;
13025
13026 if (zcu.backendSupportsFeature(.is_named_enum_value) and
13027 else_prong.body.len != 0 and block.wantSafety() and
13028 operand_ty.zigTypeTag(zcu) == .@"enum" and
13029 (!operand_ty.isNonexhaustiveEnum(zcu) or union_originally))
13030 {
13031 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
13032 const ok = try case_block.addUnOp(.is_named_enum_value, operand);
13033 try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch);
13034 }
13035
13036 const else_src_idx: LazySrcLoc.Offset.SwitchCaseIndex = if (else_prong_is_underscore)
13037 .special_under
13038 else
13039 .special_else;
13040
13041 const analyze_body = if (union_originally and !else_prong.is_inline)
13042 for (seen_enum_fields, 0..) |seen_field, index| {
13043 if (seen_field != null) continue;
13044 const union_obj = zcu.typeToUnion(maybe_union_ty).?;
13045 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[index]);
13046 if (field_ty.zigTypeTag(zcu) != .noreturn) break true;
13047 } else false
13048 else
13049 true;
13050 const else_hint: std.builtin.BranchHint = if (else_prong.body.len != 0 and err_set and
13051 try sema.maybeErrorUnwrap(&case_block, else_prong.body, operand, operand_src, allow_err_code_unwrap))
13052 h: {
13053 // nothing to do here. weight against error branch
13054 break :h .unlikely;
13055 } else if (else_prong.body.len != 0 and analyze_body and !else_prong.is_inline) h: {
13056 break :h try spa.analyzeProngRuntime(
13057 &case_block,
13058 .special,
13059 else_prong.body,
13060 else_prong.capture,
13061 child_block.src(.{ .switch_capture = .{
13062 .switch_node_offset = switch_node_offset,
13063 .case_idx = else_src_idx,
13064 } }),
13065 undefined, // case_vals may be undefined for special prongs
13066 .none,
13067 false,
13068 );
13069 } else h: {
13070 // We still need a terminator in this block, but we have proven
13071 // that it is unreachable.
13072 if (case_block.wantSafety()) {
13073 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
13074 try sema.safetyPanic(&case_block, src, .corrupt_switch);
13075 } else {
13076 _ = try case_block.addNoOp(.unreach);
13077 }
13078 // Safety check / unreachable branches are cold.
13079 break :h .cold;
13080 };
13081
13082 try branch_hints.append(gpa, else_hint);
13083 break :else_body case_block.instructions.items;
13084 } else else_body: {
13085 try branch_hints.append(gpa, .none);
13086 break :else_body &.{};
13087 };
13088
13089 assert(branch_hints.items.len == cases_len + 1);
13090
13091 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len +
13092 cases_extra.items.len + else_body.len +
13093 (std.math.divCeil(usize, branch_hints.items.len, 10) catch unreachable)); // branch hints
13094
13095 const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{
13096 .cases_len = @intCast(cases_len),
13097 .else_body_len = @intCast(else_body.len),
13098 });
13099
13100 {
13101 // Add branch hints.
13102 var cur_bag: u32 = 0;
13103 for (branch_hints.items, 0..) |hint, idx| {
13104 const idx_in_bag = idx % 10;
13105 cur_bag |= @as(u32, @intFromEnum(hint)) << @intCast(idx_in_bag * 3);
13106 if (idx_in_bag == 9) {
13107 sema.air_extra.appendAssumeCapacity(cur_bag);
13108 cur_bag = 0;
13109 }
13110 }
13111 if (branch_hints.items.len % 10 != 0) {
13112 sema.air_extra.appendAssumeCapacity(cur_bag);
13113 }
13114 }
13115 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items));
13116 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_body));
13117
13118 const has_any_continues = spa.operand == .loop and child_block.label.?.merges.extra_insts.items.len > 0;
13119
13120 return try child_block.addInst(.{
13121 .tag = if (has_any_continues) .loop_switch_br else .switch_br,
13122 .data = .{ .pl_op = .{
13123 .operand = operand,
13124 .payload = payload_index,
13125 } },
13126 });
13127}
13128
13129fn resolveSwitchComptimeLoop(
13130 sema: *Sema,
13131 init_spa: SwitchProngAnalysis,
13132 child_block: *Block,
13133 maybe_ptr_operand_ty: Type,
13134 cond_ty: Type,
13135 init_cond_val: Value,
13136 switch_node_offset: std.zig.Ast.Node.Offset,
13137 special_members_only: ?SpecialProng,
13138 special_generic: SpecialProng,
13139 special_generic_is_under: bool,
13140 case_vals: std.ArrayList(Air.Inst.Ref),
13141 scalar_cases_len: u32,
13142 multi_cases_len: u32,
13143 err_set: bool,
13144 empty_enum: bool,
13145 operand_is_ref: bool,
13146) CompileError!Air.Inst.Ref {
13147 var spa = init_spa;
13148 var cond_val = init_cond_val;
13149
13150 while (true) {
13151 if (resolveSwitchComptime(
13152 sema,
13153 spa,
13154 child_block,
13155 spa.operand.simple.cond,
13156 cond_val,
13157 cond_ty,
13158 switch_node_offset,
13159 special_members_only,
13160 special_generic,
13161 special_generic_is_under,
13162 case_vals,
13163 scalar_cases_len,
13164 multi_cases_len,
13165 err_set,
13166 empty_enum,
13167 )) |result| {
13168 return result;
13169 } else |err| switch (err) {
13170 error.ComptimeBreak => {
13171 const break_inst = sema.code.instructions.get(@intFromEnum(sema.comptime_break_inst));
13172 if (break_inst.tag != .switch_continue) return error.ComptimeBreak;
13173 const extra = sema.code.extraData(Zir.Inst.Break, break_inst.data.@"break".payload_index).data;
13174 if (extra.block_inst != spa.switch_block_inst) return error.ComptimeBreak;
13175 // This is a `switch_continue` targeting this block. Change the operand and start over.
13176 const src = child_block.nodeOffset(extra.operand_src_node.unwrap().?);
13177 const new_operand_uncoerced = try sema.resolveInst(break_inst.data.@"break".operand);
13178 const new_operand = try sema.coerce(child_block, maybe_ptr_operand_ty, new_operand_uncoerced, src);
13179
13180 try sema.emitBackwardBranch(child_block, src);
13181
13182 const val, const ref = if (operand_is_ref)
13183 .{ try sema.analyzeLoad(child_block, src, new_operand, src), new_operand }
13184 else
13185 .{ new_operand, undefined };
13186
13187 const cond_ref = try sema.switchCond(child_block, src, val);
13188
13189 cond_val = try sema.resolveConstDefinedValue(child_block, src, cond_ref, null);
13190 spa.operand = .{ .simple = .{
13191 .by_val = val,
13192 .by_ref = ref,
13193 .cond = cond_ref,
13194 } };
13195 },
13196 else => |e| return e,
13197 }
13198 }
13199}
13200
13201fn resolveSwitchComptime(
13202 sema: *Sema,
13203 spa: SwitchProngAnalysis,
12308 block: *Block,
1320412309 child_block: *Block,
13205 cond_operand: Air.Inst.Ref,
13206 operand_val: Value,
13207 operand_ty: Type,
13208 switch_node_offset: std.zig.Ast.Node.Offset,
13209 special_members_only: ?SpecialProng,
13210 special_generic: SpecialProng,
13211 special_generic_is_under: bool,
13212 case_vals: std.ArrayList(Air.Inst.Ref),
13213 scalar_cases_len: u32,
13214 multi_cases_len: u32,
13215 err_set: bool,
13216 empty_enum: bool,
12310 operand: SwitchOperand,
12311 raw_operand_ty: Type,
12312 maybe_lazy_cond_val: Value,
12313 catch_all_case: CatchAllSwitchCase,
12314 else_is_named_only: bool,
12315 merges: *Block.Merges,
12316 switch_inst: Zir.Inst.Index,
12317 zir_switch: *const Zir.UnwrappedSwitchBlock,
12318 validated_switch: *const ValidatedSwitchBlock,
1321712319) CompileError!Air.Inst.Ref {
13218 const zcu = sema.pt.zcu;
13219 const merges = &child_block.label.?.merges;
13220 const resolved_operand_val = try sema.resolveLazyValue(operand_val);
12320 const pt = sema.pt;
12321 const zcu = pt.zcu;
1322112322
13222 var extra_index: usize = special_generic.end;
13223 {
13224 var scalar_i: usize = 0;
13225 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
13226 extra_index += 1;
13227 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
13228 extra_index += 1;
13229 const body = sema.code.bodySlice(extra_index, info.body_len);
13230 extra_index += info.body_len;
13231
13232 const item = case_vals.items[scalar_i];
13233 const item_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
13234 if (operand_val.eql(item_val, operand_ty, sema.pt.zcu)) {
13235 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand);
13236 return spa.resolveProngComptime(
12323 const switch_node_offset = zir_switch.switch_src_node_offset;
12324
12325 const operand_ty = sema.typeOf(operand.simple.by_val);
12326 const item_ty = switch (operand_ty.zigTypeTag(zcu)) {
12327 .@"union" => operand_ty.unionTagType(zcu).?,
12328 else => operand_ty,
12329 };
12330 const union_originally = operand_ty.zigTypeTag(zcu) == .@"union";
12331 const err_set = item_ty.zigTypeTag(zcu) == .error_set;
12332
12333 const cond_ref = operand.simple.cond;
12334 // We have to resolve lazy values to ensure that comparisons with switch
12335 // prong items don't produce false negatives.
12336 const cond_val = try sema.resolveLazyValue(maybe_lazy_cond_val);
12337
12338 const case_vals = validated_switch.case_vals;
12339 var case_val_idx: usize = 0;
12340 var extra_index = zir_switch.end;
12341 var case_it = zir_switch.iterateCases();
12342 var under_prong: ?struct {
12343 index: Zir.UnwrappedSwitchBlock.Case.Index,
12344 body: []const Zir.Inst.Index,
12345 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
12346 has_tag_capture: bool,
12347 } = null;
12348 while (case_it.next()) |case| {
12349 const prong_info = case.prong_info;
12350 const prong_body = sema.code.bodySlice(extra_index, prong_info.body_len);
12351 extra_index += prong_body.len;
12352 for (case.item_infos) |item_info| {
12353 if (item_info.bodyLen()) |body_len| extra_index += body_len;
12354 }
12355 for (case.range_infos) |range_info| {
12356 if (range_info[0].bodyLen()) |body_len| extra_index += body_len;
12357 if (range_info[1].bodyLen()) |body_len| extra_index += body_len;
12358 }
12359
12360 const item_refs = case_vals[case_val_idx..][0..case.item_infos.len];
12361 case_val_idx += item_refs.len;
12362 const range_refs: []const [2]Air.Inst.Ref = @ptrCast(case_vals[case_val_idx..][0 .. 2 * case.range_infos.len]);
12363 case_val_idx += 2 * range_refs.len;
12364 for (item_refs) |item_ref| {
12365 if (item_ref == .none) {
12366 under_prong = .{
12367 .index = case.index,
12368 .body = prong_body,
12369 .capture = case.prong_info.capture,
12370 .has_tag_capture = case.prong_info.has_tag_capture,
12371 };
12372 continue;
12373 }
12374 const item_val = sema.resolveConstDefinedValue(child_block, .unneeded, item_ref, undefined) catch unreachable;
12375 if (cond_val.eql(item_val, item_ty, zcu)) {
12376 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, prong_body, cond_ref);
12377 if (union_originally and operand_ty.unionFieldType(item_val, zcu).?.isNoReturn(zcu)) {
12378 // This prong should be unreachable!
12379 return .unreachable_value;
12380 }
12381 return sema.resolveSwitchProng(
12382 block,
1323712383 child_block,
13238 .normal,
13239 body,
13240 info.capture,
13241 child_block.src(.{ .switch_capture = .{
12384 operand,
12385 raw_operand_ty,
12386 prong_body,
12387 block.src(.{ .switch_capture = .{
1324212388 .switch_node_offset = switch_node_offset,
13243 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
12389 .case_idx = case.index,
1324412390 } }),
13245 &.{item},
13246 if (info.is_inline) cond_operand else .none,
13247 info.has_tag_capture,
12391 prong_info.capture,
12392 prong_info.has_tag_capture,
12393 if (prong_info.is_inline) cond_ref else .none,
12394 .{ .item_refs = item_refs },
12395 validated_switch.else_err_ty,
1324812396 merges,
12397 switch_inst,
12398 zir_switch,
1324912399 );
1325012400 }
1325112401 }
13252 }
13253 {
13254 var multi_i: usize = 0;
13255 var case_val_idx: usize = scalar_cases_len;
13256 while (multi_i < multi_cases_len) : (multi_i += 1) {
13257 const items_len = sema.code.extra[extra_index];
13258 extra_index += 1;
13259 const ranges_len = sema.code.extra[extra_index];
13260 extra_index += 1;
13261 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
13262 extra_index += 1 + items_len;
13263 const body = sema.code.bodySlice(extra_index + 2 * ranges_len, info.body_len);
13264
13265 const items = case_vals.items[case_val_idx..][0..items_len];
13266 case_val_idx += items_len;
13267
13268 for (items) |item| {
13269 // Validation above ensured these will succeed.
13270 const item_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
13271 if (operand_val.eql(item_val, operand_ty, sema.pt.zcu)) {
13272 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand);
13273 return spa.resolveProngComptime(
13274 child_block,
13275 .normal,
13276 body,
13277 info.capture,
13278 child_block.src(.{ .switch_capture = .{
13279 .switch_node_offset = switch_node_offset,
13280 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
13281 } }),
13282 items,
13283 if (info.is_inline) cond_operand else .none,
13284 info.has_tag_capture,
13285 merges,
13286 );
13287 }
12402 for (range_refs) |range_ref| {
12403 const first_val = sema.resolveConstDefinedValue(child_block, .unneeded, range_ref[0], undefined) catch unreachable;
12404 const last_val = sema.resolveConstDefinedValue(child_block, .unneeded, range_ref[1], undefined) catch unreachable;
12405 if ((try sema.compareAll(cond_val, .gte, first_val, item_ty)) and
12406 (try sema.compareAll(cond_val, .lte, last_val, item_ty)))
12407 {
12408 return sema.resolveSwitchProng(
12409 block,
12410 child_block,
12411 operand,
12412 raw_operand_ty,
12413 prong_body,
12414 block.src(.{ .switch_capture = .{
12415 .switch_node_offset = switch_node_offset,
12416 .case_idx = case.index,
12417 } }),
12418 prong_info.capture,
12419 prong_info.has_tag_capture,
12420 if (prong_info.is_inline) cond_ref else .none,
12421 .has_ranges,
12422 validated_switch.else_err_ty,
12423 merges,
12424 switch_inst,
12425 zir_switch,
12426 );
1328812427 }
12428 }
12429 }
1328912430
13290 var range_i: usize = 0;
13291 while (range_i < ranges_len) : (range_i += 1) {
13292 const range_items = case_vals.items[case_val_idx..][0..2];
13293 extra_index += 2;
13294 case_val_idx += 2;
12431 const else_case = validated_switch.else_case;
1329512432
13296 // Validation above ensured these will succeed.
13297 const first_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, range_items[0], undefined) catch unreachable;
13298 const last_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, range_items[1], undefined) catch unreachable;
13299 if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and
13300 (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty)))
13301 {
13302 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand);
13303 return spa.resolveProngComptime(
13304 child_block,
13305 .normal,
13306 body,
13307 info.capture,
13308 child_block.src(.{ .switch_capture = .{
13309 .switch_node_offset = switch_node_offset,
13310 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
13311 } }),
13312 undefined, // case_vals may be undefined for ranges
13313 if (info.is_inline) cond_operand else .none,
13314 info.has_tag_capture,
13315 merges,
13316 );
13317 }
13318 }
12433 // named-only prong
1331912434
13320 extra_index += info.body_len;
13321 }
13322 }
13323 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, special_generic.body, cond_operand);
13324 if (empty_enum) {
13325 return .void_value;
12435 if (else_is_named_only and item_ty.enumTagFieldIndex(cond_val, zcu) != null) {
12436 assert(item_ty.isNonexhaustiveEnum(zcu));
12437 return sema.resolveSwitchProng(
12438 block,
12439 child_block,
12440 operand,
12441 raw_operand_ty,
12442 else_case.body,
12443 block.src(.{ .switch_capture = .{
12444 .switch_node_offset = switch_node_offset,
12445 .case_idx = else_case.index,
12446 } }),
12447 else_case.capture,
12448 else_case.has_tag_capture,
12449 if (else_case.is_inline) cond_ref else .none,
12450 .special,
12451 validated_switch.else_err_ty,
12452 merges,
12453 switch_inst,
12454 zir_switch,
12455 );
1332612456 }
13327 if (special_members_only) |special| {
13328 assert(operand_ty.isNonexhaustiveEnum(zcu));
13329 if (operand_ty.enumTagFieldIndex(operand_val, zcu)) |_| {
13330 return spa.resolveProngComptime(
13331 child_block,
13332 .special,
13333 special.body,
13334 special.capture,
13335 child_block.src(.{ .switch_capture = .{
13336 .switch_node_offset = switch_node_offset,
13337 .case_idx = .special_else,
13338 } }),
13339 undefined, // case_vals may be undefined for special prongs
13340 if (special.is_inline) cond_operand else .none,
13341 special.has_tag_capture,
13342 merges,
13343 );
12457
12458 // catch-all prong
12459
12460 const index, const body, const capture, const has_tag_capture, const is_inline = switch (catch_all_case) {
12461 .@"else" => .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture, else_case.is_inline },
12462 .under => .{ under_prong.?.index, under_prong.?.body, under_prong.?.capture, under_prong.?.has_tag_capture, false },
12463 .none => unreachable,
12464 };
12465 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_ref);
12466 if (union_originally) {
12467 for (validated_switch.seen_enum_fields, 0..) |maybe_seen, field_i| {
12468 if (maybe_seen != null) continue;
12469 if (!operand_ty.unionFieldTypeByIndex(field_i, zcu).isNoReturn(zcu)) break;
12470 } else {
12471 // This prong should be unreachable!
12472 return .unreachable_value;
1334412473 }
1334512474 }
13346
13347 return spa.resolveProngComptime(
12475 return sema.resolveSwitchProng(
12476 block,
1334812477 child_block,
13349 .special,
13350 special_generic.body,
13351 special_generic.capture,
13352 child_block.src(.{ .switch_capture = .{
12478 operand,
12479 raw_operand_ty,
12480 body,
12481 block.src(.{ .switch_capture = .{
1335312482 .switch_node_offset = switch_node_offset,
13354 .case_idx = if (special_generic_is_under)
13355 .special_under
13356 else
13357 .special_else,
12483 .case_idx = index,
1335812484 } }),
13359 undefined, // case_vals may be undefined for special prongs
13360 if (special_generic.is_inline) cond_operand else .none,
13361 special_generic.has_tag_capture,
12485 capture,
12486 has_tag_capture,
12487 if (is_inline) cond_ref else .none,
12488 .special,
12489 validated_switch.else_err_ty,
1336212490 merges,
12491 switch_inst,
12492 zir_switch,
1336312493 );
1336412494}
1336512495
13366const RangeSetUnhandledIterator = struct {
13367 pt: Zcu.PerThread,
13368 cur: ?InternPool.Index,
13369 max: InternPool.Index,
13370 range_i: usize,
13371 ranges: []const RangeSet.Range,
13372 limbs: []math.big.Limb,
12496const SwitchOperand = union(enum) {
12497 /// This switch will be dispatched only once, with the given operand.
12498 simple: struct {
12499 /// The raw switch operand value. Always defined.
12500 by_val: Air.Inst.Ref,
12501 /// The switch operand *pointer*. Defined only if there is a prong
12502 /// with a by-ref capture.
12503 by_ref: Air.Inst.Ref,
12504 /// The switch condition value. For unions, `operand` is the union
12505 /// and `cond` is its enum tag value.
12506 cond: Air.Inst.Ref,
12507 },
12508 /// This switch may be dispatched multiple times with `continue` syntax.
12509 /// As such, the operand is stored in an alloc if needed.
12510 loop: struct {
12511 /// The `alloc` containing the `switch` operand for the active dispatch.
12512 /// Each prong must load from this `alloc` to get captures.
12513 /// If there are no captures, this may be undefined.
12514 operand_alloc: Air.Inst.Ref,
12515 /// Whether `operand_alloc` contains a by-val operand or a by-ref
12516 /// operand.
12517 operand_is_ref: bool,
12518 /// The switch condition value for the *initial* dispatch. For
12519 /// unions, this is the enum tag value.
12520 init_cond: Air.Inst.Ref,
12521 },
12522};
1337312523
13374 const preallocated_limbs = math.big.int.calcTwosCompLimbCount(128);
12524const CatchAllSwitchCase = enum { none, @"else", under };
1337512525
13376 fn init(sema: *Sema, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator {
13377 const pt = sema.pt;
13378 const int_type = pt.zcu.intern_pool.indexToKey(ty.toIntern()).int_type;
13379 const needed_limbs = math.big.int.calcTwosCompLimbCount(int_type.bits);
13380 return .{
13381 .pt = pt,
13382 .cur = (try ty.minInt(pt, ty)).toIntern(),
13383 .max = (try ty.maxInt(pt, ty)).toIntern(),
13384 .range_i = 0,
13385 .ranges = range_set.ranges.items,
13386 .limbs = if (needed_limbs > preallocated_limbs)
13387 try sema.arena.alloc(math.big.Limb, needed_limbs)
13388 else
13389 &.{},
13390 };
13391 }
12526const SwitchProngKind = union(enum) {
12527 item_refs: []const Air.Inst.Ref,
12528 has_ranges,
12529 special,
12530};
1339212531
13393 fn addOne(it: *const RangeSetUnhandledIterator, val: InternPool.Index) !?InternPool.Index {
13394 if (val == it.max) return null;
13395 const int = it.pt.zcu.intern_pool.indexToKey(val).int;
12532/// Resolve a switch prong which is determined at comptime to have no peers.
12533/// Sets up captures as needed. Uses `analyzeBodyRuntimeBreak`.
12534fn resolveSwitchProng(
12535 sema: *Sema,
12536 block: *Block,
12537 child_block: *Block,
12538 operand: SwitchOperand,
12539 raw_operand_ty: Type,
12540 prong_body: []const Zir.Inst.Index,
12541 /// Must use the `switch_capture` field in `offset`.
12542 capture_src: LazySrcLoc,
12543 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
12544 has_tag_capture: bool,
12545 inline_case_capture: Air.Inst.Ref,
12546 kind: SwitchProngKind,
12547 else_err_ty: ?Type,
12548 merges: *Block.Merges,
12549 switch_inst: Zir.Inst.Index,
12550 zir_switch: *const Zir.UnwrappedSwitchBlock,
12551) CompileError!Air.Inst.Ref {
12552 const src_node_offset = zir_switch.switch_src_node_offset;
12553 const src = block.nodeOffset(src_node_offset);
12554 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
1339612555
13397 switch (int.storage) {
13398 inline .u64, .i64 => |val_int| {
13399 const next_int = @addWithOverflow(val_int, 1);
13400 if (next_int[1] == 0)
13401 return (try it.pt.intValue(.fromInterned(int.ty), next_int[0])).toIntern();
13402 },
13403 .big_int => {},
13404 .lazy_align, .lazy_size => unreachable,
13405 }
12556 // We can propagate `.cold` hints from this branch since it's comptime-known
12557 // to be taken from the parent branch.
12558 const parent_hint = sema.branch_hint;
12559 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
1340612560
13407 var val_space: InternPool.Key.Int.Storage.BigIntSpace = undefined;
13408 const val_bigint = int.storage.toBigInt(&val_space);
12561 const payload_inst: Zir.Inst.Index = if (capture != .none) inst: {
12562 const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst;
12563 const payload_ref = try sema.analyzeSwitchPayloadCapture(
12564 child_block,
12565 operand,
12566 operand.simple.by_val,
12567 operand.simple.by_ref,
12568 sema.typeOf(operand.simple.by_val),
12569 operand_src,
12570 capture_src,
12571 capture == .by_ref,
12572 kind == .special,
12573 switch (kind) {
12574 .item_refs => |item_refs| item_refs,
12575 .has_ranges, .special => undefined,
12576 },
12577 inline_case_capture,
12578 else_err_ty,
12579 );
12580 assert(!sema.typeOf(payload_ref).isNoReturn(sema.pt.zcu));
12581 sema.inst_map.putAssumeCapacity(payload_inst, payload_ref);
12582 break :inst payload_inst;
12583 } else undefined;
12584 defer if (capture != .none) assert(sema.inst_map.remove(payload_inst));
1340912585
13410 var result_limbs: [preallocated_limbs]math.big.Limb = undefined;
13411 var result_bigint = math.big.int.Mutable.init(
13412 if (it.limbs.len > 0) it.limbs else &result_limbs,
13413 0,
12586 const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: {
12587 const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst;
12588 const tag_ref = try sema.analyzeSwitchTagCapture(
12589 child_block,
12590 operand.simple.by_val,
12591 sema.typeOf(operand.simple.by_val),
12592 capture_src,
12593 inline_case_capture,
12594 kind,
1341412595 );
12596 sema.inst_map.putAssumeCapacity(tag_inst, tag_ref);
12597 break :inst tag_inst;
12598 } else undefined;
12599 defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst));
1341512600
13416 result_bigint.addScalar(val_bigint, 1);
13417 return (try it.pt.intValue_big(.fromInterned(int.ty), result_bigint.toConst())).toIntern();
12601 if (zir_switch.has_continue) sema.inst_map.putAssumeCapacity(switch_inst, .fromType(raw_operand_ty));
12602 defer if (zir_switch.has_continue) assert(sema.inst_map.remove(switch_inst));
12603
12604 return sema.resolveBlockBody(block, src, child_block, prong_body, switch_inst, merges);
12605}
12606
12607fn wantSwitchProngBodyAnalysis(
12608 sema: *Sema,
12609 block: *Block,
12610 item_ref: Air.Inst.Ref,
12611 operand_ty: Type,
12612 union_originally: bool,
12613 err_set: bool,
12614 prong_is_comptime_unreach: bool,
12615) bool {
12616 const zcu = sema.pt.zcu;
12617 if (union_originally) {
12618 const unresolved_item_val = sema.resolveConstDefinedValue(block, .unneeded, item_ref, undefined) catch unreachable;
12619 const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable;
12620 const field_ty = operand_ty.unionFieldType(item_val, zcu).?;
12621 if (field_ty.isNoReturn(zcu)) return false;
12622 }
12623 if (err_set and prong_is_comptime_unreach) {
12624 const unresolved_item_val = sema.resolveConstDefinedValue(block, .unneeded, item_ref, undefined) catch unreachable;
12625 const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable;
12626 const err_name = item_val.getErrorName(zcu).unwrap().?;
12627 if (!Type.errorSetHasFieldIp(&zcu.intern_pool, operand_ty.toIntern(), err_name)) return false;
1341812628 }
12629 return true;
12630}
1341912631
13420 fn next(it: *RangeSetUnhandledIterator) !?InternPool.Index {
13421 var cur = it.cur orelse return null;
13422 while (it.range_i < it.ranges.len and cur == it.ranges[it.range_i].first) {
13423 defer it.range_i += 1;
13424 cur = (try it.addOne(it.ranges[it.range_i].last)) orelse {
13425 it.cur = null;
13426 return null;
13427 };
12632/// Assumes that `operand_ty` has more than one possible value.
12633/// Sets up captures as needed. Uses `analyzeBodyRuntimeBreak`.
12634fn analyzeSwitchProng(
12635 sema: *Sema,
12636 case_block: *Block,
12637 operand: SwitchOperand,
12638 operand_ty: Type,
12639 raw_operand_ty: Type,
12640 prong_body: []const Zir.Inst.Index,
12641 /// Must use the `switch_capture` field in `offset`.
12642 capture_src: LazySrcLoc,
12643 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
12644 has_tag_capture: bool,
12645 inline_case_capture: Air.Inst.Ref,
12646 kind: SwitchProngKind,
12647 else_err_ty: ?Type,
12648 switch_inst: Zir.Inst.Index,
12649 zir_switch: *const Zir.UnwrappedSwitchBlock,
12650) CompileError!std.builtin.BranchHint {
12651 const pt = sema.pt;
12652 const zcu = pt.zcu;
12653
12654 const operand_src = case_block.src(.{ .node_offset_switch_operand = zir_switch.switch_src_node_offset });
12655
12656 if (operand_ty.zigTypeTag(zcu) == .error_set) {
12657 const cond_ref = switch (operand) {
12658 .simple => |s| s.cond,
12659 .loop => |l| l.init_cond,
12660 };
12661 if (try sema.maybeErrorUnwrap(case_block, prong_body, cond_ref, operand_src, true)) {
12662 // nothing to do here. weight against error branch
12663 return .unlikely;
1342812664 }
13429 it.cur = try it.addOne(cur);
13430 return cur;
1343112665 }
13432};
1343312666
13434const ResolvedSwitchItem = struct {
13435 ref: Air.Inst.Ref,
13436 val: InternPool.Index,
13437};
13438fn resolveSwitchItemVal(
13439 sema: *Sema,
13440 block: *Block,
13441 item_ref: Zir.Inst.Ref,
13442 /// Coerce `item_ref` to this type.
13443 coerce_ty: Type,
13444 item_src: LazySrcLoc,
13445) CompileError!ResolvedSwitchItem {
13446 const uncoerced_item = try sema.resolveInst(item_ref);
12667 const operand_val, const operand_ptr = load_operand: {
12668 if (capture == .none and !has_tag_capture) {
12669 // No need to load the operand for this prong!
12670 break :load_operand .{ undefined, undefined };
12671 }
12672 if (inline_case_capture != .none and
12673 !(capture != .none and operand_ty.zigTypeTag(zcu) == .@"union"))
12674 {
12675 // We only need to load the operand if there's a union payload capture
12676 // since it's always runtime-known; only the tag is comptime-known here.
12677 break :load_operand .{ undefined, undefined };
12678 }
12679 assert(zir_switch.any_maybe_runtime_capture); // should have caught everything else by now
12680 switch (operand) {
12681 .simple => |s| break :load_operand .{ s.by_val, s.by_ref },
12682 .loop => |l| {
12683 const loaded = try sema.analyzeLoad(case_block, operand_src, l.operand_alloc, operand_src);
12684 if (l.operand_is_ref) {
12685 const by_val = try sema.analyzeLoad(case_block, operand_src, loaded, operand_src);
12686 break :load_operand .{ by_val, loaded };
12687 } else {
12688 break :load_operand .{ loaded, undefined };
12689 }
12690 },
12691 }
12692 };
1344712693
13448 // Constructing a LazySrcLoc is costly because we only have the switch AST node.
13449 // Only if we know for sure we need to report a compile error do we resolve the
13450 // full source locations.
12694 const payload_inst: Zir.Inst.Index = if (capture != .none) inst: {
12695 const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst;
12696 const payload_ref = try sema.analyzeSwitchPayloadCapture(
12697 case_block,
12698 operand,
12699 operand_val,
12700 operand_ptr,
12701 operand_ty,
12702 operand_src,
12703 capture_src,
12704 capture == .by_ref,
12705 kind == .special,
12706 switch (kind) {
12707 .item_refs => |item_refs| item_refs,
12708 .has_ranges, .special => undefined,
12709 },
12710 inline_case_capture,
12711 else_err_ty,
12712 );
12713 assert(!sema.typeOf(payload_ref).isNoReturn(sema.pt.zcu));
12714 sema.inst_map.putAssumeCapacity(payload_inst, payload_ref);
12715 break :inst payload_inst;
12716 } else undefined;
12717 defer if (capture != .none) assert(sema.inst_map.remove(payload_inst));
1345112718
13452 const item = try sema.coerce(block, coerce_ty, uncoerced_item, item_src);
12719 const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: {
12720 const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst;
12721 const tag_ref = try sema.analyzeSwitchTagCapture(
12722 case_block,
12723 operand_val,
12724 operand_ty,
12725 capture_src,
12726 inline_case_capture,
12727 kind,
12728 );
12729 sema.inst_map.putAssumeCapacity(tag_inst, tag_ref);
12730 break :inst tag_inst;
12731 } else undefined;
12732 defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst));
1345312733
13454 const maybe_lazy = try sema.resolveConstDefinedValue(block, item_src, item, .{ .simple = .switch_item });
12734 if (zir_switch.has_continue) sema.inst_map.putAssumeCapacity(switch_inst, .fromType(raw_operand_ty));
12735 defer if (zir_switch.has_continue) assert(sema.inst_map.remove(switch_inst));
1345512736
13456 const val = try sema.resolveLazyValue(maybe_lazy);
13457 const new_item = if (val.toIntern() != maybe_lazy.toIntern()) blk: {
13458 break :blk Air.internedToRef(val.toIntern());
13459 } else item;
12737 return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
12738}
12739
12740fn analyzeSwitchTagCapture(
12741 sema: *Sema,
12742 case_block: *Block,
12743 /// May be `undefined` if `inline_case_capture` is not `.none`.
12744 operand_val: Air.Inst.Ref,
12745 operand_ty: Type,
12746 capture_src: LazySrcLoc,
12747 inline_case_capture: Air.Inst.Ref,
12748 kind: SwitchProngKind,
12749) CompileError!Air.Inst.Ref {
12750 const pt = sema.pt;
12751 const zcu = pt.zcu;
12752
12753 const tag_capture_src: LazySrcLoc = .{
12754 .base_node_inst = capture_src.base_node_inst,
12755 .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture },
12756 };
1346012757
13461 return .{ .ref = new_item, .val = val.toIntern() };
12758 if (operand_ty.zigTypeTag(zcu) != .@"union") {
12759 return sema.fail(case_block, tag_capture_src, "cannot capture tag of non-union type '{f}'", .{
12760 operand_ty.fmt(pt),
12761 });
12762 }
12763 if (inline_case_capture != .none) {
12764 return inline_case_capture; // this already is the tag, it's what we're switching on!
12765 }
12766 switch (kind) {
12767 .has_ranges, .special => {},
12768 .item_refs => |refs| if (refs.len == 1) return refs[0],
12769 }
12770 const tag_ty = operand_ty.unionTagType(zcu).?;
12771 return sema.unionToTag(case_block, tag_ty, operand_val, tag_capture_src);
1346212772}
1346312773
13464fn validateErrSetSwitch(
12774fn analyzeSwitchPayloadCapture(
1346512775 sema: *Sema,
13466 block: *Block,
13467 seen_errors: *SwitchErrorSet,
13468 case_vals: *std.ArrayList(Air.Inst.Ref),
12776 case_block: *Block,
12777 operand: SwitchOperand,
12778 /// May be `undefined` if this is an inline capture and operand is not a union.
12779 operand_val: Air.Inst.Ref,
12780 /// May be `undefined` if `capture_by_ref` is `false` or if `operand_val` is also `undefined`.
12781 operand_ptr: Air.Inst.Ref,
1346912782 operand_ty: Type,
13470 inst_data: @FieldType(Zir.Inst.Data, "pl_node"),
13471 scalar_cases_len: u32,
13472 multi_cases_len: u32,
13473 else_case: struct { body: []const Zir.Inst.Index, end: usize, src: LazySrcLoc },
13474 has_else: bool,
13475) CompileError!?Type {
13476 const gpa = sema.gpa;
12783 operand_src: LazySrcLoc,
12784 capture_src: LazySrcLoc,
12785 capture_by_ref: bool,
12786 is_special_prong: bool,
12787 /// May be `undefined` if `is_special_prong` is `true`.
12788 case_vals: []const Air.Inst.Ref,
12789 /// If this is not `.none`, this is an inline capture.
12790 inline_case_capture: Air.Inst.Ref,
12791 else_err_ty: ?Type,
12792) CompileError!Air.Inst.Ref {
1347712793 const pt = sema.pt;
1347812794 const zcu = pt.zcu;
1347912795 const ip = &zcu.intern_pool;
1348012796
13481 const src_node_offset = inst_data.src_node;
13482 const src = block.nodeOffset(src_node_offset);
13483
13484 var extra_index: usize = else_case.end;
13485 {
13486 var scalar_i: u32 = 0;
13487 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
13488 const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
13489 extra_index += 1;
13490 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
13491 extra_index += 1 + info.body_len;
12797 const switch_node_offset = operand_src.offset.node_offset_switch_operand;
1349212798
13493 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
13494 block,
13495 seen_errors,
13496 item_ref,
13497 operand_ty,
13498 block.src(.{ .switch_case_item = .{
13499 .switch_node_offset = src_node_offset,
13500 .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
13501 .item_idx = .{ .kind = .single, .index = 0 },
13502 } }),
13503 ));
12799 if (inline_case_capture != .none) {
12800 const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, inline_case_capture, undefined) catch unreachable;
12801 if (operand_ty.zigTypeTag(zcu) == .@"union") {
12802 const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?);
12803 const union_obj = zcu.typeToUnion(operand_ty).?;
12804 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
12805 if (capture_by_ref) {
12806 const operand_ptr_info = sema.typeOf(operand_ptr).ptrInfo(zcu);
12807 const ptr_field_ty = try pt.ptrTypeSema(.{
12808 .child = field_ty.toIntern(),
12809 .flags = .{
12810 .is_const = operand_ptr_info.flags.is_const,
12811 .is_volatile = operand_ptr_info.flags.is_volatile,
12812 .address_space = operand_ptr_info.flags.address_space,
12813 },
12814 });
12815 return case_block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty);
12816 } else {
12817 if (try sema.resolveDefinedValue(case_block, operand_src, operand_val)) |union_val| {
12818 const tag_and_val = ip.indexToKey(union_val.toIntern()).un;
12819 return .fromIntern(tag_and_val.val);
12820 }
12821 return case_block.addStructFieldVal(operand_val, field_index, field_ty);
12822 }
12823 } else if (capture_by_ref) {
12824 return sema.uavRef(item_val.toIntern());
12825 } else {
12826 return inline_case_capture;
1350412827 }
1350512828 }
13506 {
13507 var multi_i: u32 = 0;
13508 while (multi_i < multi_cases_len) : (multi_i += 1) {
13509 const items_len = sema.code.extra[extra_index];
13510 extra_index += 1;
13511 const ranges_len = sema.code.extra[extra_index];
13512 extra_index += 1;
13513 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
13514 extra_index += 1;
13515 const items = sema.code.refSlice(extra_index, items_len);
13516 extra_index += items_len + info.body_len;
1351712829
13518 try case_vals.ensureUnusedCapacity(gpa, items.len);
13519 for (items, 0..) |item_ref, item_i| {
13520 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
13521 block,
13522 seen_errors,
13523 item_ref,
13524 operand_ty,
13525 block.src(.{ .switch_case_item = .{
13526 .switch_node_offset = src_node_offset,
13527 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
13528 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
13529 } }),
13530 ));
12830 const operand_ptr_ty = if (capture_by_ref) sema.typeOf(operand_ptr) else undefined;
12831
12832 if (is_special_prong) {
12833 if (capture_by_ref) return operand_ptr;
12834 return switch (operand_ty.zigTypeTag(zcu)) {
12835 .error_set => e: {
12836 if (else_err_ty) |err_ty| {
12837 break :e sema.bitCast(case_block, err_ty, operand_val, operand_src, null);
12838 } else {
12839 try sema.analyzeUnreachable(case_block, operand_src, false);
12840 break :e .unreachable_value;
12841 }
12842 },
12843 else => operand_val,
12844 };
12845 }
12846
12847 switch (operand_ty.zigTypeTag(zcu)) {
12848 .@"union" => {
12849 const union_obj = zcu.typeToUnion(operand_ty).?;
12850 const first_item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable;
12851
12852 const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?;
12853 const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]);
12854
12855 const field_indices = try sema.arena.alloc(u32, case_vals.len);
12856 for (case_vals, field_indices) |item, *field_idx| {
12857 const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, item, undefined) catch unreachable;
12858 field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?;
12859 }
12860
12861 // Fast path: if all the operands are the same type already, we don't need to hit
12862 // PTR! This will also allow us to emit simpler code.
12863 const same_types = for (field_indices[1..]) |field_idx| {
12864 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
12865 if (!field_ty.eql(first_field_ty, zcu)) break false;
12866 } else true;
12867
12868 const capture_ty: Type = capture_ty: {
12869 if (same_types) break :capture_ty first_field_ty;
12870 // We need values to run PTR on, so make a bunch of undef constants.
12871 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
12872 for (dummy_captures, field_indices) |*dummy, field_idx| {
12873 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
12874 dummy.* = try pt.undefRef(field_ty);
12875 }
12876
12877 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
12878 for (case_srcs, 0..) |*case_src, item_i| {
12879 case_src.* = .{
12880 .base_node_inst = capture_src.base_node_inst,
12881 .offset = .{ .switch_case_item = .{
12882 .switch_node_offset = switch_node_offset,
12883 .case_idx = capture_src.offset.switch_capture.case_idx,
12884 .item_idx = .{ .kind = .single, .value = @intCast(item_i) },
12885 } },
12886 };
12887 }
12888
12889 break :capture_ty sema.resolvePeerTypes(
12890 case_block,
12891 capture_src,
12892 dummy_captures,
12893 .{ .override = case_srcs },
12894 ) catch |err| switch (err) {
12895 error.AnalysisFail => {
12896 const msg = sema.err orelse return error.AnalysisFail;
12897 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{});
12898 return error.AnalysisFail;
12899 },
12900 else => |e| return e,
12901 };
12902 };
12903
12904 // By-reference captures have some further restrictions which make them easier to emit
12905 if (capture_by_ref) {
12906 const operand_ptr_info = operand_ptr_ty.ptrInfo(zcu);
12907 const capture_ptr_ty = resolve: {
12908 // By-ref captures of hetereogeneous types are only allowed if all field
12909 // pointer types are peer resolvable to each other.
12910 // We need values to run PTR on, so make a bunch of undef constants.
12911 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
12912 for (field_indices, dummy_captures) |field_idx, *dummy| {
12913 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
12914 const field_ptr_ty = try pt.ptrTypeSema(.{
12915 .child = field_ty.toIntern(),
12916 .flags = .{
12917 .is_const = operand_ptr_info.flags.is_const,
12918 .is_volatile = operand_ptr_info.flags.is_volatile,
12919 .address_space = operand_ptr_info.flags.address_space,
12920 .alignment = union_obj.fieldAlign(ip, field_idx),
12921 },
12922 });
12923 dummy.* = try pt.undefRef(field_ptr_ty);
12924 }
12925 const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
12926 for (case_srcs, 0..) |*case_src, item_i| {
12927 case_src.* = .{
12928 .base_node_inst = capture_src.base_node_inst,
12929 .offset = .{ .switch_case_item = .{
12930 .switch_node_offset = switch_node_offset,
12931 .case_idx = capture_src.offset.switch_capture.case_idx,
12932 .item_idx = .{ .kind = .single, .value = @intCast(item_i) },
12933 } },
12934 };
12935 }
12936
12937 break :resolve sema.resolvePeerTypes(
12938 case_block,
12939 capture_src,
12940 dummy_captures,
12941 .{ .override = case_srcs },
12942 ) catch |err| switch (err) {
12943 error.AnalysisFail => {
12944 const msg = sema.err orelse return error.AnalysisFail;
12945 try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{});
12946 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{});
12947 return error.AnalysisFail;
12948 },
12949 else => |e| return e,
12950 };
12951 };
12952
12953 if (try sema.resolveDefinedValue(case_block, operand_src, operand_ptr)) |op_ptr_val| {
12954 if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty);
12955 const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt);
12956 return .fromValue(try pt.getCoerced(field_ptr_val, capture_ptr_ty));
12957 }
12958
12959 try sema.requireRuntimeBlock(case_block, operand_src, null);
12960 return case_block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty);
12961 }
12962
12963 if (try sema.resolveDefinedValue(case_block, operand_src, operand_val)) |operand_val_val| {
12964 if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty);
12965 const union_val = ip.indexToKey(operand_val_val.toIntern()).un;
12966 if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty);
12967 const uncoerced: Air.Inst.Ref = .fromIntern(union_val.val);
12968 return sema.coerce(case_block, capture_ty, uncoerced, operand_src);
12969 }
12970
12971 try sema.requireRuntimeBlock(case_block, operand_src, null);
12972
12973 if (same_types) {
12974 return case_block.addStructFieldVal(operand_val, first_field_index, capture_ty);
12975 }
12976
12977 // We may have to emit a switch block which coerces the operand to the capture type.
12978 // If we can, try to avoid that using in-memory coercions.
12979 const first_non_imc = in_mem: {
12980 for (field_indices, 0..) |field_idx, i| {
12981 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
12982 if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) {
12983 break :in_mem i;
12984 }
12985 }
12986 // All fields are in-memory coercible to the resolved type!
12987 // Just take the first field and bitcast the result.
12988 const uncoerced = try case_block.addStructFieldVal(operand_val, first_field_index, first_field_ty);
12989 return case_block.addBitCast(capture_ty, uncoerced);
12990 };
12991
12992 // By-val capture with heterogeneous types which are not all in-memory coercible to
12993 // the resolved capture type. We finally have to fall back to the ugly method.
12994
12995 // However, let's first track which operands are in-memory coercible. There may well
12996 // be several, and we can squash all of these cases into the same switch prong using
12997 // a simple bitcast. We'll make this the 'else' prong.
12998
12999 var in_mem_coercible: std.DynamicBitSet = try .initFull(sema.arena, field_indices.len);
13000 in_mem_coercible.unset(first_non_imc);
13001 {
13002 const next = first_non_imc + 1;
13003 for (field_indices[next..], next..) |field_idx, i| {
13004 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
13005 if (.ok != try sema.coerceInMemoryAllowed(case_block, capture_ty, field_ty, false, zcu.getTarget(), .unneeded, .unneeded, null)) {
13006 in_mem_coercible.unset(i);
13007 }
13008 }
1353113009 }
1353213010
13533 try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
13534 }
13535 }
13011 const capture_block_inst = try case_block.addInstAsIndex(.{
13012 .tag = .block,
13013 .data = .{
13014 .ty_pl = .{
13015 .ty = .fromType(capture_ty),
13016 .payload = undefined, // updated below
13017 },
13018 },
13019 });
1353613020
13537 switch (try sema.resolveInferredErrorSetTy(block, src, operand_ty.toIntern())) {
13538 .anyerror_type => {
13539 if (!has_else) {
13540 return sema.fail(
13541 block,
13542 src,
13543 "else prong required when switching on type 'anyerror'",
13544 .{},
13545 );
13021 const prong_count = field_indices.len - in_mem_coercible.count();
13022
13023 const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints
13024 var cases_extra = try std.array_list.Managed(u32).initCapacity(sema.gpa, estimated_extra);
13025 defer cases_extra.deinit();
13026
13027 {
13028 // All branch hints are `.none`, so just add zero elems.
13029 comptime assert(@intFromEnum(std.builtin.BranchHint.none) == 0);
13030 const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable;
13031 try cases_extra.appendNTimes(0, need_elems);
1354613032 }
13547 return .anyerror;
13548 },
13549 else => |err_set_ty_index| else_validation: {
13550 const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names;
13551 var maybe_msg: ?*Zcu.ErrorMsg = null;
13552 errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa);
1355313033
13554 for (error_names.get(ip)) |error_name| {
13555 if (!seen_errors.contains(error_name) and !has_else) {
13556 const msg = maybe_msg orelse blk: {
13557 maybe_msg = try sema.errMsg(
13558 src,
13559 "switch must handle all possibilities",
13560 .{},
13561 );
13562 break :blk maybe_msg.?;
13034 {
13035 // Non-bitcast cases
13036 var it = in_mem_coercible.iterator(.{ .kind = .unset });
13037 while (it.next()) |idx| {
13038 var coerce_block = case_block.makeSubBlock();
13039 defer coerce_block.instructions.deinit(sema.gpa);
13040
13041 const case_src: LazySrcLoc = .{
13042 .base_node_inst = capture_src.base_node_inst,
13043 .offset = .{ .switch_case_item = .{
13044 .switch_node_offset = switch_node_offset,
13045 .case_idx = capture_src.offset.switch_capture.case_idx,
13046 .item_idx = .{ .kind = .single, .value = @intCast(idx) },
13047 } },
1356313048 };
1356413049
13565 try sema.errNote(
13566 src,
13567 msg,
13568 "unhandled error value: 'error.{f}'",
13569 .{error_name.fmt(ip)},
13570 );
13050 const field_idx = field_indices[idx];
13051 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
13052 const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty);
13053 const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src);
13054 _ = try coerce_block.addBr(capture_block_inst, coerced);
13055
13056 try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
13057 1 + // `item`, no ranges
13058 coerce_block.instructions.items.len);
13059 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
13060 .items_len = 1,
13061 .ranges_len = 0,
13062 .body_len = @intCast(coerce_block.instructions.items.len),
13063 }));
13064 cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item
13065 cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body
1357113066 }
1357213067 }
13068 const else_body_len = len: {
13069 // 'else' prong uses a bitcast
13070 var coerce_block = case_block.makeSubBlock();
13071 defer coerce_block.instructions.deinit(sema.gpa);
1357313072
13574 if (maybe_msg) |msg| {
13575 maybe_msg = null;
13576 try sema.addDeclaredHereNote(msg, operand_ty);
13577 return sema.failWithOwnedErrorMsg(block, msg);
13578 }
13073 const first_imc_item_idx = in_mem_coercible.findFirstSet().?;
13074 const first_imc_field_idx = field_indices[first_imc_item_idx];
13075 const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]);
13076 const uncoerced = try coerce_block.addStructFieldVal(operand_val, first_imc_field_idx, first_imc_field_ty);
13077 const coerced = try coerce_block.addBitCast(capture_ty, uncoerced);
13078 _ = try coerce_block.addBr(capture_block_inst, coerced);
13079
13080 try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items));
13081 break :len coerce_block.instructions.items.len;
13082 };
1357913083
13580 if (has_else and seen_errors.count() == error_names.len) {
13581 // In order to enable common patterns for generic code allow simple else bodies
13582 // else => unreachable,
13583 // else => return,
13584 // else => |e| return e,
13585 // even if all the possible errors were already handled.
13586 const tags = sema.code.instructions.items(.tag);
13587 const datas = sema.code.instructions.items(.data);
13588 for (else_case.body) |else_inst| switch (tags[@intFromEnum(else_inst)]) {
13589 .dbg_stmt,
13590 .dbg_var_val,
13591 .ret_type,
13592 .as_node,
13593 .ret_node,
13594 .@"unreachable",
13595 .@"defer",
13596 .defer_err_code,
13597 .err_union_code,
13598 .ret_err_value_code,
13599 .save_err_ret_index,
13600 .restore_err_ret_index_unconditional,
13601 .restore_err_ret_index_fn_entry,
13602 .is_non_err,
13603 .ret_is_non_err,
13604 .condbr,
13605 => {},
13606 .extended => switch (datas[@intFromEnum(else_inst)].extended.opcode) {
13607 .restore_err_ret_index => {},
13608 else => break,
13084 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len +
13085 cases_extra.items.len +
13086 @typeInfo(Air.Block).@"struct".fields.len +
13087 1);
13088
13089 const switch_br_inst: u32 = @intCast(sema.air_instructions.len);
13090 try sema.air_instructions.append(sema.gpa, .{
13091 .tag = .switch_br,
13092 .data = .{
13093 .pl_op = .{
13094 .operand = undefined, // set by switch below
13095 .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{
13096 .cases_len = @intCast(prong_count),
13097 .else_body_len = @intCast(else_body_len),
13098 }),
1360913099 },
13610 else => break,
13611 } else break :else_validation;
13100 },
13101 });
13102 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
13103
13104 // Set up block body
13105 switch (operand) {
13106 .simple => |s| {
13107 const air_datas = sema.air_instructions.items(.data);
13108 air_datas[switch_br_inst].pl_op.operand = s.cond;
13109 air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload =
13110 sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 });
13111 sema.air_extra.appendAssumeCapacity(switch_br_inst);
13112 },
13113 .loop => {
13114 // The block must first extract the tag from the loaded union.
13115 const tag_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
13116 try sema.air_instructions.append(sema.gpa, .{
13117 .tag = .get_union_tag,
13118 .data = .{ .ty_op = .{
13119 .ty = .fromIntern(union_obj.enum_tag_ty),
13120 .operand = operand_val,
13121 } },
13122 });
13123 const air_datas = sema.air_instructions.items(.data);
13124 air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef();
13125 air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload =
13126 sema.addExtraAssumeCapacity(Air.Block{ .body_len = 2 });
13127 sema.air_extra.appendAssumeCapacity(@intFromEnum(tag_inst));
13128 sema.air_extra.appendAssumeCapacity(switch_br_inst);
13129 },
13130 }
1361213131
13132 return capture_block_inst.toRef();
13133 },
13134 .error_set => {
13135 if (capture_by_ref) {
1361313136 return sema.fail(
13614 block,
13615 else_case.src,
13616 "unreachable else prong; all cases already handled",
13137 case_block,
13138 capture_src,
13139 "error set cannot be captured by reference",
1361713140 .{},
1361813141 );
1361913142 }
1362013143
13621 var names: InferredErrorSet.NameMap = .{};
13622 try names.ensureUnusedCapacity(sema.arena, error_names.len);
13623 for (error_names.get(ip)) |error_name| {
13624 if (seen_errors.contains(error_name)) continue;
13144 if (case_vals.len == 1) {
13145 const item_val = sema.resolveConstDefinedValue(case_block, .unneeded, case_vals[0], undefined) catch unreachable;
13146 const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?);
13147 return sema.bitCast(case_block, item_ty, operand_val, operand_src, null);
13148 }
1362513149
13626 names.putAssumeCapacityNoClobber(error_name, {});
13150 var names: InferredErrorSet.NameMap = .{};
13151 try names.ensureUnusedCapacity(sema.arena, case_vals.len);
13152 for (case_vals) |err| {
13153 const err_val = sema.resolveConstDefinedValue(case_block, .unneeded, err, undefined) catch unreachable;
13154 names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {});
13155 }
13156 const error_ty = try pt.errorSetFromUnsortedNames(names.keys());
13157 return sema.bitCast(case_block, error_ty, operand_val, operand_src, null);
13158 },
13159 else => {
13160 // In this case the capture value is just the passed-through value of the
13161 // switch condition. It is comptime-known if there is only one item.
13162 if (capture_by_ref) {
13163 return operand_ptr;
13164 } else if (case_vals.len == 1) {
13165 return case_vals[0];
13166 } else {
13167 return operand_val;
1362713168 }
13628 // No need to keep the hash map metadata correct; here we
13629 // extract the (sorted) keys only.
13630 return try pt.errorSetFromUnsortedNames(names.keys());
1363113169 },
1363213170 }
13633 return null;
1363413171}
1363513172
13636fn validateSwitchRange(
13637 sema: *Sema,
13638 block: *Block,
13639 range_set: *RangeSet,
13640 first_ref: Zir.Inst.Ref,
13641 last_ref: Zir.Inst.Ref,
13642 operand_ty: Type,
13643 item_src: LazySrcLoc,
13644) CompileError![2]Air.Inst.Ref {
13645 const first_src: LazySrcLoc = .{
13646 .base_node_inst = item_src.base_node_inst,
13647 .offset = .{ .switch_case_item_range_first = item_src.offset.switch_case_item },
13648 };
13649 const last_src: LazySrcLoc = .{
13650 .base_node_inst = item_src.base_node_inst,
13651 .offset = .{ .switch_case_item_range_last = item_src.offset.switch_case_item },
13652 };
13653 const first = try sema.resolveSwitchItemVal(block, first_ref, operand_ty, first_src);
13654 const last = try sema.resolveSwitchItemVal(block, last_ref, operand_ty, last_src);
13655 if (try Value.fromInterned(first.val).compareAll(.gt, Value.fromInterned(last.val), operand_ty, sema.pt)) {
13656 return sema.fail(block, item_src, "range start value is greater than the end value", .{});
13657 }
13658 const maybe_prev_src = try range_set.add(first.val, last.val, item_src);
13659 try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
13660 return .{ first.ref, last.ref };
13661}
13173const ResolvedSwitchItem = struct {
13174 ref: Air.Inst.Ref,
13175 val: Value,
13176};
13177const ResolvedSwitchItemAndExtraIndex = struct { ResolvedSwitchItem, usize };
1366213178
13663fn validateSwitchItemInt(
13179fn resolveSwitchItem(
1366413180 sema: *Sema,
1366513181 block: *Block,
13666 range_set: *RangeSet,
13667 item_ref: Zir.Inst.Ref,
13668 operand_ty: Type,
1366913182 item_src: LazySrcLoc,
13670) CompileError!Air.Inst.Ref {
13671 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
13672 const maybe_prev_src = try range_set.add(item.val, item.val, item_src);
13673 try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
13674 return item.ref;
13675}
13183 item_ty: Type,
13184 item_info: Zir.Inst.SwitchBlock.ItemInfo,
13185 extra_index: usize,
13186 switch_inst: Zir.Inst.Index,
13187 prong_is_comptime_unreach: bool,
13188) CompileError!ResolvedSwitchItemAndExtraIndex {
13189 const pt = sema.pt;
13190 const zcu = pt.zcu;
13191 const ip = &zcu.intern_pool;
13192 const comp = zcu.comp;
13193 const gpa = comp.gpa;
13194 const io = comp.io;
1367613195
13677fn validateSwitchItemEnum(
13678 sema: *Sema,
13679 block: *Block,
13680 seen_fields: []?LazySrcLoc,
13681 range_set: *RangeSet,
13682 item_ref: Zir.Inst.Ref,
13683 operand_ty: Type,
13684 item_src: LazySrcLoc,
13685) CompileError!Air.Inst.Ref {
13686 const ip = &sema.pt.zcu.intern_pool;
13687 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
13688 const int = ip.indexToKey(item.val).enum_tag.int;
13689 const field_index = ip.loadEnumType(ip.typeOf(item.val)).tagValueIndex(ip, int) orelse {
13690 const maybe_prev_src = try range_set.add(int, int, item_src);
13691 try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
13692 return item.ref;
13196 var end = extra_index;
13197 const uncoerced: Air.Inst.Ref, const uncoerced_ty: Type = uncoerced: switch (item_info.unwrap()) {
13198 .under => unreachable, // caller must check this before calling us
13199 .enum_literal => |str_index| {
13200 const zir_str = sema.code.nullTerminatedString(str_index);
13201 const name = try ip.getOrPutString(gpa, io, pt.tid, zir_str, .no_embedded_nulls);
13202 const uncoerced = try sema.analyzeDeclLiteral(block, item_src, name, item_ty, false);
13203 break :uncoerced .{ uncoerced, .enum_literal };
13204 },
13205 .error_value => |str_index| {
13206 const zir_str = sema.code.nullTerminatedString(str_index);
13207 const name = try ip.getOrPutString(gpa, io, pt.tid, zir_str, .no_embedded_nulls);
13208 // Make sure there's an error integer value associated with `name`.
13209 _ = try pt.getErrorValue(name);
13210 const err_set_ty = try pt.singleErrorSetType(name);
13211 const uncoerced = Air.internedToRef(try pt.intern(.{ .err = .{
13212 .ty = err_set_ty.toIntern(),
13213 .name = name,
13214 } }));
13215 break :uncoerced .{ uncoerced, err_set_ty };
13216 },
13217 .body_len => |body_len| {
13218 const body = sema.code.bodySlice(extra_index, body_len);
13219 end += body.len;
13220
13221 const uncoerced = ref: {
13222 // The result location of item bodies is `.{ .coerce_ty = switch_inst }`.
13223 sema.inst_map.putAssumeCapacity(switch_inst, .fromType(item_ty));
13224 defer assert(sema.inst_map.remove(switch_inst));
13225 const old_comptime_reason = block.comptime_reason;
13226 defer block.comptime_reason = old_comptime_reason;
13227 block.comptime_reason = .{ .reason = .{
13228 .src = item_src,
13229 .r = .{ .simple = .switch_item },
13230 } };
13231 break :ref try sema.resolveInlineBody(block, body, switch_inst);
13232 };
13233 break :uncoerced .{ uncoerced, sema.typeOf(uncoerced) };
13234 },
1369313235 };
13694 const maybe_prev_src = seen_fields[field_index];
13695 seen_fields[field_index] = item_src;
13696 try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
13697 return item.ref;
13698}
13699
13700fn validateSwitchItemError(
13701 sema: *Sema,
13702 block: *Block,
13703 seen_errors: *SwitchErrorSet,
13704 item_ref: Zir.Inst.Ref,
13705 operand_ty: Type,
13706 item_src: LazySrcLoc,
13707) CompileError!Air.Inst.Ref {
13708 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
13709 const error_name = sema.pt.zcu.intern_pool.indexToKey(item.val).err.name;
13710 const maybe_prev_src = if (try seen_errors.fetchPut(error_name, item_src)) |prev|
13711 prev.value
13712 else
13713 null;
13714 try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
13715 return item.ref;
13716}
13236 const item_ref: Air.Inst.Ref = item_ref: {
13237 if (item_ty.zigTypeTag(zcu) == .error_set and
13238 uncoerced_ty.zigTypeTag(zcu) == .error_set)
13239 {
13240 // We allow prongs with errors which are not part of the error set
13241 // being switched on if their prong body is `=> comptime unreachable,`.
13242 switch (try sema.coerceInMemoryAllowedErrorSets(block, item_ty, uncoerced_ty, item_src, item_src)) {
13243 .ok => if (try sema.resolveValue(uncoerced)) |uncoerced_val| {
13244 break :item_ref try sema.coerceInMemory(uncoerced_val, item_ty);
13245 },
13246 .missing_error => if (prong_is_comptime_unreach) {
13247 break :item_ref uncoerced;
13248 },
13249 .from_anyerror => {},
13250 else => unreachable,
13251 }
13252 }
13253 break :item_ref try sema.coerce(block, item_ty, uncoerced, item_src);
13254 };
13255 const maybe_lazy = try sema.resolveConstDefinedValue(block, item_src, item_ref, .{ .simple = .switch_item });
1371713256
13718fn validateSwitchDupe(
13719 sema: *Sema,
13720 block: *Block,
13721 maybe_prev_src: ?LazySrcLoc,
13722 item_src: LazySrcLoc,
13723) CompileError!void {
13724 const prev_item_src = maybe_prev_src orelse return;
13725 return sema.failWithOwnedErrorMsg(block, msg: {
13726 const msg = try sema.errMsg(
13727 item_src,
13728 "duplicate switch value",
13729 .{},
13730 );
13731 errdefer msg.destroy(sema.gpa);
13732 try sema.errNote(
13733 prev_item_src,
13734 msg,
13735 "previous value here",
13736 .{},
13737 );
13738 break :msg msg;
13739 });
13740}
13257 // We have to resolve lazy values here to avoid false negatives when detecting
13258 // duplicate items and comparing items to a comptime-known switch operand.
1374113259
13742fn validateSwitchItemBool(
13743 sema: *Sema,
13744 block: *Block,
13745 true_count: *u8,
13746 false_count: *u8,
13747 item_ref: Zir.Inst.Ref,
13748 item_src: LazySrcLoc,
13749) CompileError!Air.Inst.Ref {
13750 const item = try sema.resolveSwitchItemVal(block, item_ref, .bool, item_src);
13751 if (Value.fromInterned(item.val).toBool()) {
13752 true_count.* += 1;
13753 } else {
13754 false_count.* += 1;
13755 }
13756 if (true_count.* > 1 or false_count.* > 1) {
13757 return sema.fail(block, item_src, "duplicate switch value", .{});
13758 }
13759 return item.ref;
13260 const val = try sema.resolveLazyValue(maybe_lazy);
13261 const ref: Air.Inst.Ref = if (val.toIntern() == maybe_lazy.toIntern())
13262 item_ref
13263 else
13264 .fromValue(val);
13265 return .{ .{ .ref = ref, .val = val }, end };
1376013266}
1376113267
13762const ValueSrcMap = std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc);
13763
13764fn validateSwitchItemSparse(
13268fn validateSwitchItemOrRange(
1376513269 sema: *Sema,
1376613270 block: *Block,
13767 seen_values: *ValueSrcMap,
13768 item_ref: Zir.Inst.Ref,
13769 operand_ty: Type,
1377013271 item_src: LazySrcLoc,
13771) CompileError!Air.Inst.Ref {
13772 const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
13773 const kv = try seen_values.fetchPut(sema.gpa, item.val, item_src) orelse return item.ref;
13774 try sema.validateSwitchDupe(block, kv.value, item_src);
13775 unreachable;
13776}
13777
13778fn validateSwitchNoRange(
13779 sema: *Sema,
13780 block: *Block,
13781 ranges_len: u32,
13782 operand_ty: Type,
13783 src_node_offset: std.zig.Ast.Node.Offset,
13272 /// If `opt_last_val` is not `null`, this refers to the first val of a range.
13273 item_val: Value,
13274 opt_last_val: ?Value,
13275 item_ty: Type,
13276 seen_enum_fields: []?LazySrcLoc,
13277 seen_errors: *std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc),
13278 seen_sparse_values: *std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc),
13279 range_set: *RangeSet,
13280 true_src: *?LazySrcLoc,
13281 false_src: *?LazySrcLoc,
13282 void_src: *?LazySrcLoc,
1378413283) CompileError!void {
13785 if (ranges_len == 0)
13786 return;
13787
13788 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
13789 const range_src = block.src(.{ .node_offset_switch_range = src_node_offset });
13790
13791 const msg = msg: {
13792 const msg = try sema.errMsg(
13793 operand_src,
13794 "ranges not allowed when switching on type '{f}'",
13795 .{operand_ty.fmt(sema.pt)},
13796 );
13797 errdefer msg.destroy(sema.gpa);
13798 try sema.errNote(
13799 range_src,
13800 msg,
13801 "range here",
13802 .{},
13803 );
13804 break :msg msg;
13284 const pt = sema.pt;
13285 const zcu = pt.zcu;
13286 const ip = &zcu.intern_pool;
13287 const maybe_prev_src: ?LazySrcLoc = maybe_prev_src: switch (item_ty.zigTypeTag(zcu)) {
13288 .@"union" => unreachable,
13289 .@"enum" => {
13290 const int = ip.indexToKey(item_val.toIntern()).enum_tag.int;
13291 if (ip.loadEnumType(item_ty.toIntern()).tagValueIndex(ip, int)) |field_index| {
13292 const maybe_prev_src = seen_enum_fields[field_index];
13293 seen_enum_fields[field_index] = item_src;
13294 break :maybe_prev_src maybe_prev_src;
13295 } else {
13296 break :maybe_prev_src try range_set.add(sema.arena, .{
13297 .first = .fromInterned(int),
13298 .last = .fromInterned(int),
13299 .src = item_src,
13300 }, .fromInterned(ip.typeOf(int)), zcu);
13301 }
13302 },
13303 .error_set => {
13304 const error_name = ip.indexToKey(item_val.toIntern()).err.name;
13305 break :maybe_prev_src if (seen_errors.fetchPutAssumeCapacity(error_name, item_src)) |prev|
13306 prev.value
13307 else
13308 null;
13309 },
13310 .int, .comptime_int => {
13311 if (opt_last_val) |last_val| {
13312 const first_val = item_val;
13313 if (try first_val.compareAll(.gt, last_val, item_ty, pt)) {
13314 return sema.fail(block, item_src, "range start value is greater than the end value", .{});
13315 }
13316 break :maybe_prev_src range_set.addAssumeCapacity(.{
13317 .first = first_val,
13318 .last = last_val,
13319 .src = item_src,
13320 }, item_ty, zcu);
13321 } else {
13322 break :maybe_prev_src range_set.addAssumeCapacity(.{
13323 .first = item_val,
13324 .last = item_val,
13325 .src = item_src,
13326 }, item_ty, zcu);
13327 }
13328 },
13329 .enum_literal, .@"fn", .pointer, .type => {
13330 break :maybe_prev_src if (seen_sparse_values.fetchPutAssumeCapacity(item_val.toIntern(), item_src)) |prev|
13331 prev.value
13332 else
13333 null;
13334 },
13335 .bool => {
13336 if (item_val.toBool()) {
13337 if (true_src.*) |prev_src| break :maybe_prev_src prev_src;
13338 true_src.* = item_src;
13339 } else {
13340 if (false_src.*) |prev_src| break :maybe_prev_src prev_src;
13341 false_src.* = item_src;
13342 }
13343 break :maybe_prev_src null;
13344 },
13345 .void => {
13346 if (void_src.*) |prev_src| break :maybe_prev_src prev_src;
13347 void_src.* = item_src;
13348 break :maybe_prev_src null;
13349 },
13350 else => unreachable, // should have already checked for invalid types
1380513351 };
13806 return sema.failWithOwnedErrorMsg(block, msg);
13352 if (maybe_prev_src) |prev_src| {
13353 return sema.failWithOwnedErrorMsg(block, msg: {
13354 const msg = try sema.errMsg(
13355 item_src,
13356 "duplicate switch value",
13357 .{},
13358 );
13359 errdefer msg.destroy(sema.gpa);
13360 try sema.errNote(
13361 prev_src,
13362 msg,
13363 "previous value here",
13364 .{},
13365 );
13366 break :msg msg;
13367 });
13368 }
1380713369}
1380813370
1380913371fn maybeErrorUnwrap(
......@@ -18687,14 +18249,13 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!
1868718249 break :msg msg;
1868818250 });
1868918251 }
18690 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
18691 if (is_non_err != .none) {
18252 if (try sema.resolveIsNonErrVal(parent_block, operand_src, err_union)) |is_non_err_val| {
1869218253 // We can propagate `.cold` hints from this branch since it's comptime-known
1869318254 // to be taken from the parent branch.
1869418255 const parent_hint = sema.branch_hint;
1869518256 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
1869618257
18697 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
18258 if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(parent_block, operand_src, null);
1869818259 if (is_non_err_val.toBool()) {
1869918260 return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false);
1870018261 }
......@@ -18751,14 +18312,13 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr
1875118312 break :msg msg;
1875218313 });
1875318314 }
18754 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
18755 if (is_non_err != .none) {
18315 if (try sema.resolveIsNonErrVal(parent_block, operand_src, err_union)) |is_non_err_val| {
1875618316 // We can propagate `.cold` hints from this branch since it's comptime-known
1875718317 // to be taken from the parent branch.
1875818318 const parent_hint = sema.branch_hint;
1875918319 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
1876018320
18761 const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
18321 if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(parent_block, operand_src, null);
1876218322 if (is_non_err_val.toBool()) {
1876318323 return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);
1876418324 }
......@@ -31798,58 +31358,73 @@ fn analyzeIsNull(
3179831358 return block.addUnOp(air_tag, operand);
3179931359}
3180031360
31801fn analyzePtrIsNonErrComptimeOnly(
31361fn resolvePtrIsNonErrVal(
3180231362 sema: *Sema,
3180331363 block: *Block,
3180431364 src: LazySrcLoc,
3180531365 operand: Air.Inst.Ref,
31806) CompileError!Air.Inst.Ref {
31366) CompileError!?Value {
3180731367 const pt = sema.pt;
3180831368 const zcu = pt.zcu;
3180931369 const ptr_ty = sema.typeOf(operand);
3181031370 assert(ptr_ty.zigTypeTag(zcu) == .pointer);
3181131371 const child_ty = ptr_ty.childType(zcu);
3181231372
31813 const child_tag = child_ty.zigTypeTag(zcu);
31814 if (child_tag != .error_set and child_tag != .error_union) return .bool_true;
31815 if (child_tag == .error_set) return .bool_false;
31816 assert(child_tag == .error_union);
31373 if (try sema.resolveIsNonErrFromType(block, src, child_ty)) |res| {
31374 return res;
31375 }
31376 assert(child_ty.zigTypeTag(zcu) == .error_union);
3181731377
31818 _ = block;
31819 _ = src;
31378 if (try sema.resolveValue(operand)) |eu_ptr_val| {
31379 if (eu_ptr_val.isUndef(zcu)) return .undef_bool;
31380 if (try sema.pointerDeref(block, src, eu_ptr_val, ptr_ty)) |err_union| {
31381 if (err_union.isUndef(zcu)) return .undef_bool;
31382 return .makeBool(err_union.getErrorName(zcu) == .none);
31383 }
31384 }
3182031385
31821 return .none;
31386 return null;
3182231387}
3182331388
31824fn analyzeIsNonErrComptimeOnly(
31389fn resolveIsNonErrVal(
3182531390 sema: *Sema,
3182631391 block: *Block,
3182731392 src: LazySrcLoc,
3182831393 operand: Air.Inst.Ref,
31829) CompileError!Air.Inst.Ref {
31394) CompileError!?Value {
31395 const zcu = sema.pt.zcu;
31396 if (try sema.resolveIsNonErrFromType(block, src, sema.typeOf(operand))) |res| {
31397 return res;
31398 }
31399 assert(sema.typeOf(operand).zigTypeTag(zcu) == .error_union);
31400
31401 if (try sema.resolveValue(operand)) |err_union| {
31402 if (err_union.isUndef(zcu)) return .undef_bool;
31403 return .makeBool(err_union.getErrorName(zcu) == .none);
31404 }
31405
31406 return null;
31407}
31408
31409fn resolveIsNonErrFromType(
31410 sema: *Sema,
31411 block: *Block,
31412 src: LazySrcLoc,
31413 operand_ty: Type,
31414) CompileError!?Value {
3183031415 const pt = sema.pt;
3183131416 const zcu = pt.zcu;
3183231417 const ip = &zcu.intern_pool;
31833 const operand_ty = sema.typeOf(operand);
3183431418 const ot = operand_ty.zigTypeTag(zcu);
31835 if (ot != .error_set and ot != .error_union) return .bool_true;
31836 if (ot == .error_set) return .bool_false;
31419 if (ot != .error_set and ot != .error_union) return .true;
31420 if (ot == .error_set) return .false;
3183731421 assert(ot == .error_union);
3183831422
3183931423 const payload_ty = operand_ty.errorUnionPayload(zcu);
3184031424 if (payload_ty.zigTypeTag(zcu) == .noreturn) {
31841 return .bool_false;
31842 }
31843
31844 if (operand == .undef) {
31845 return .undef_bool;
31846 } else if (@intFromEnum(operand) < InternPool.static_len) {
31847 // None of the ref tags can be errors.
31848 return .bool_true;
31425 return .false;
3184931426 }
3185031427
31851 const maybe_operand_val = try sema.resolveValue(operand);
31852
3185331428 // exception if the error union error set is known to be empty,
3185431429 // we allow the comparison but always make it comptime-known.
3185531430 const set_ty = ip.errorUnionSet(operand_ty.toIntern());
......@@ -31865,26 +31440,23 @@ fn analyzeIsNonErrComptimeOnly(
3186531440 else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk,
3186631441 }
3186731442
31868 if (maybe_operand_val != null) break :blk;
31869
31870 // Try to avoid resolving inferred error set if possible.
31871 if (ies.errors.count() != 0) return .none;
31443 if (ies.errors.count() != 0) return null;
3187231444 switch (ies.resolved) {
31873 .anyerror_type => return .none,
31445 .anyerror_type => return null,
3187431446 .none => {},
3187531447 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {
31876 0 => return .bool_true,
31877 else => return .none,
31448 0 => return .true,
31449 else => return null,
3187831450 },
3187931451 }
3188031452 // We do not have a comptime answer because this inferred error
3188131453 // set is not resolved, and an instruction later in this function
3188231454 // body may or may not cause an error to be added to this set.
31883 return .none;
31455 return null;
3188431456 },
3188531457 else => switch (ip.indexToKey(set_ty)) {
3188631458 .error_set_type => |error_set_type| {
31887 if (error_set_type.names.len == 0) return .bool_true;
31459 if (error_set_type.names.len == 0) return .true;
3188831460 },
3188931461 .inferred_error_set_type => |func_index| blk: {
3189031462 // If the error set is empty, we must return a comptime true or false.
......@@ -31896,39 +31468,35 @@ fn analyzeIsNonErrComptimeOnly(
3189631468 .none => {},
3189731469 else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk,
3189831470 }
31899 if (maybe_operand_val != null) break :blk;
3190031471 if (sema.fn_ret_ty_ies) |ies| {
3190131472 if (ies.func == func_index) {
3190231473 // Try to avoid resolving inferred error set if possible.
31903 if (ies.errors.count() != 0) return .none;
31474 if (ies.errors.count() != 0) return null;
3190431475 switch (ies.resolved) {
31905 .anyerror_type => return .none,
31476 .anyerror_type => return null,
3190631477 .none => {},
3190731478 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {
31908 0 => return .bool_true,
31909 else => return .none,
31479 0 => return .true,
31480 else => return null,
3191031481 },
3191131482 }
3191231483 // We do not have a comptime answer because this inferred error
3191331484 // set is not resolved, and an instruction later in this function
3191431485 // body may or may not cause an error to be added to this set.
31915 return .none;
31486 return null;
3191631487 }
3191731488 }
3191831489 const resolved_ty = try sema.resolveInferredErrorSet(block, src, set_ty);
3191931490 if (resolved_ty == .anyerror_type)
3192031491 break :blk;
3192131492 if (ip.indexToKey(resolved_ty).error_set_type.names.len == 0)
31922 return .bool_true;
31493 return .true;
3192331494 },
3192431495 else => unreachable,
3192531496 },
3192631497 }
3192731498
31928 if (maybe_operand_val) |err_union| {
31929 return if (err_union.isUndef(zcu)) .undef_bool else if (err_union.getErrorName(zcu) == .none) .bool_true else .bool_false;
31930 }
31931 return .none;
31499 return null;
3193231500}
3193331501
3193431502fn analyzeIsNonErr(
......@@ -31937,12 +31505,10 @@ fn analyzeIsNonErr(
3193731505 src: LazySrcLoc,
3193831506 operand: Air.Inst.Ref,
3193931507) CompileError!Air.Inst.Ref {
31940 const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand);
31941 if (result == .none) {
31942 try sema.requireRuntimeBlock(block, src, null);
31943 return block.addUnOp(.is_non_err, operand);
31508 if (try sema.resolveIsNonErrVal(block, src, operand)) |val| {
31509 return .fromValue(val);
3194431510 } else {
31945 return result;
31511 return block.addUnOp(.is_non_err, operand);
3194631512 }
3194731513}
3194831514
......@@ -31952,12 +31518,10 @@ fn analyzePtrIsNonErr(
3195231518 src: LazySrcLoc,
3195331519 operand: Air.Inst.Ref,
3195431520) CompileError!Air.Inst.Ref {
31955 const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand);
31956 if (result == .none) {
31957 try sema.requireRuntimeBlock(block, src, null);
31958 return block.addUnOp(.is_non_err_ptr, operand);
31521 if (try sema.resolvePtrIsNonErrVal(block, src, operand)) |val| {
31522 return .fromValue(val);
3195931523 } else {
31960 return result;
31524 return block.addUnOp(.is_non_err_ptr, operand);
3196131525 }
3196231526}
3196331527
src/Type.zig+15-15
......@@ -1933,12 +1933,12 @@ pub fn isPtrLikeOptional(ty: Type, zcu: *const Zcu) bool {
19331933 };
19341934}
19351935
1936/// For *[N]T, returns [N]T.
1937/// For *T, returns T.
1938/// For [*]T, returns T.
1939/// For @Vector(N, T), returns T.
1940/// For [N]T, returns T.
1941/// For ?T, returns T.
1936/// For `*[N]T`, returns `[N]T`.
1937/// For `*T`, returns `T`.
1938/// For `[*]T`, returns `T`.
1939/// For `@Vector(N, T)`, returns `T`.
1940/// For `[N]T`, returns `T`.
1941/// For `?T`, returns `T`.
19421942pub fn childType(ty: Type, zcu: *const Zcu) Type {
19431943 return childTypeIp(ty, &zcu.intern_pool);
19441944}
......@@ -1947,15 +1947,15 @@ pub fn childTypeIp(ty: Type, ip: *const InternPool) Type {
19471947 return Type.fromInterned(ip.childType(ty.toIntern()));
19481948}
19491949
1950/// For *[N]T, returns T.
1951/// For ?*T, returns T.
1952/// For ?*[N]T, returns T.
1953/// For ?[*]T, returns T.
1954/// For *T, returns T.
1955/// For [*]T, returns T.
1956/// For [N]T, returns T.
1957/// For []T, returns T.
1958/// For anyframe->T, returns T.
1950/// For `*[N]T`, returns `T`.
1951/// For `?*T`, returns `T`.
1952/// For `?*[N]T`, returns `T`.
1953/// For `?[*]T`, returns `T`.
1954/// For `*T`, returns `T`.
1955/// For `[*]T`, returns `T`.
1956/// For `[N]T`, returns `T`.
1957/// For `[]T`, returns `T`.
1958/// For `anyframe->T`, returns `T`.
19591959pub fn elemType2(ty: Type, zcu: *const Zcu) Type {
19601960 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
19611961 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
src/Zcu.zig+21-68
......@@ -878,7 +878,7 @@ pub const Namespace = struct {
878878 ns: Namespace,
879879 zcu: *Zcu,
880880 name: InternPool.NullTerminatedString,
881 writer: anytype,
881 writer: *Writer,
882882 ) @TypeOf(writer).Error!void {
883883 const sep: u8 = if (ns.parent.unwrap()) |parent| sep: {
884884 try zcu.namespacePtr(parent).renderFullyQualifiedDebugName(
......@@ -1125,7 +1125,7 @@ pub const File = struct {
11251125 return file.sub_file_path.len - ext.len;
11261126 }
11271127
1128 pub fn renderFullyQualifiedName(file: File, writer: anytype) !void {
1128 pub fn renderFullyQualifiedName(file: File, writer: *Writer) !void {
11291129 // Convert all the slashes into dots and truncate the extension.
11301130 const ext = std.fs.path.extension(file.sub_file_path);
11311131 const noext = file.sub_file_path[0 .. file.sub_file_path.len - ext.len];
......@@ -1135,7 +1135,7 @@ pub const File = struct {
11351135 };
11361136 }
11371137
1138 pub fn renderFullyQualifiedDebugName(file: File, writer: anytype) !void {
1138 pub fn renderFullyQualifiedDebugName(file: File, writer: *Writer) !void {
11391139 for (file.sub_file_path) |byte| switch (byte) {
11401140 '/', '\\' => try writer.writeByte('/'),
11411141 else => try writer.writeByte(byte),
......@@ -1742,27 +1742,6 @@ pub const SrcLoc = struct {
17421742 } else unreachable;
17431743 },
17441744
1745 .node_offset_switch_under_prong => |node_off| {
1746 const tree = try src_loc.file_scope.getTree(zcu);
1747 const switch_node = node_off.toAbsolute(src_loc.base_node);
1748 _, const extra_index = tree.nodeData(switch_node).node_and_extra;
1749 const case_nodes = tree.extraDataSlice(tree.extraData(extra_index, Ast.Node.SubRange), Ast.Node.Index);
1750 for (case_nodes) |case_node| {
1751 const case = tree.fullSwitchCase(case_node).?;
1752 for (case.ast.values) |val| {
1753 if (tree.nodeTag(val) == .identifier and
1754 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_"))
1755 {
1756 return tree.tokensToSpan(
1757 tree.firstToken(case_node),
1758 tree.lastToken(case_node),
1759 tree.nodeMainToken(val),
1760 );
1761 }
1762 }
1763 } else unreachable;
1764 },
1765
17661745 .node_offset_switch_range => |node_off| {
17671746 const tree = try src_loc.file_scope.getTree(zcu);
17681747 const switch_node = node_off.toAbsolute(src_loc.base_node);
......@@ -2176,34 +2155,22 @@ pub const SrcLoc = struct {
21762155
21772156 var multi_i: u32 = 0;
21782157 var scalar_i: u32 = 0;
2179 var underscore_node: Ast.Node.OptionalIndex = .none;
2180 const case = case: for (case_nodes) |case_node| {
2158 const case: Ast.full.SwitchCase = case: for (case_nodes) |case_node| {
21812159 const case = tree.fullSwitchCase(case_node).?;
21822160 if (case.ast.values.len == 0) {
2183 if (want_case_idx == LazySrcLoc.Offset.SwitchCaseIndex.special_else) {
2161 if (want_case_idx == Zir.UnwrappedSwitchBlock.Case.Index.@"else") {
21842162 break :case case;
21852163 }
21862164 continue :case;
21872165 }
2188 if (underscore_node == .none) for (case.ast.values) |val_node| {
2189 if (tree.nodeTag(val_node) == .identifier and
2190 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val_node)), "_"))
2191 {
2192 underscore_node = val_node.toOptional();
2193 if (want_case_idx == LazySrcLoc.Offset.SwitchCaseIndex.special_under) {
2194 break :case case;
2195 }
2196 continue :case;
2197 }
2198 };
21992166
22002167 const is_multi = case.ast.values.len != 1 or
22012168 tree.nodeTag(case.ast.values[0]) == .switch_range;
22022169
22032170 switch (want_case_idx.kind) {
2204 .scalar => if (!is_multi and want_case_idx.index == scalar_i)
2171 .scalar => if (!is_multi and want_case_idx.value == scalar_i)
22052172 break :case case,
2206 .multi => if (is_multi and want_case_idx.index == multi_i)
2173 .multi => if (is_multi and want_case_idx.value == multi_i)
22072174 break :case case,
22082175 }
22092176
......@@ -2214,12 +2181,12 @@ pub const SrcLoc = struct {
22142181 }
22152182 } else unreachable;
22162183
2217 const want_item = switch (src_loc.lazy) {
2184 const want_item_idx = switch (src_loc.lazy) {
22182185 .switch_case_item,
22192186 .switch_case_item_range_first,
22202187 .switch_case_item_range_last,
22212188 => |x| item_idx: {
2222 assert(want_case_idx != LazySrcLoc.Offset.SwitchCaseIndex.special_else);
2189 assert(want_case_idx != Zir.UnwrappedSwitchBlock.Case.Index.@"else");
22232190 break :item_idx x.item_idx;
22242191 },
22252192 .switch_capture, .switch_tag_capture => {
......@@ -2242,16 +2209,14 @@ pub const SrcLoc = struct {
22422209 else => unreachable,
22432210 };
22442211
2245 switch (want_item.kind) {
2212 switch (want_item_idx.kind) {
22462213 .single => {
22472214 var item_i: u32 = 0;
22482215 for (case.ast.values) |item_node| {
2249 if (item_node.toOptional() == underscore_node or
2250 tree.nodeTag(item_node) == .switch_range)
2251 {
2216 if (tree.nodeTag(item_node) == .switch_range) {
22522217 continue;
22532218 }
2254 if (item_i != want_item.index) {
2219 if (item_i != want_item_idx.value) {
22552220 item_i += 1;
22562221 continue;
22572222 }
......@@ -2264,7 +2229,7 @@ pub const SrcLoc = struct {
22642229 if (tree.nodeTag(item_node) != .switch_range) {
22652230 continue;
22662231 }
2267 if (range_i != want_item.index) {
2232 if (range_i != want_item_idx.value) {
22682233 range_i += 1;
22692234 continue;
22702235 }
......@@ -2446,10 +2411,6 @@ pub const LazySrcLoc = struct {
24462411 /// by taking this AST node index offset from the containing base node,
24472412 /// which points to a switch expression AST node. Next, navigate to the else prong.
24482413 node_offset_switch_else_prong: Ast.Node.Offset,
2449 /// The source location points to the `_` prong of a switch expression, found
2450 /// by taking this AST node index offset from the containing base node,
2451 /// which points to a switch expression AST node. Next, navigate to the `_` prong.
2452 node_offset_switch_under_prong: Ast.Node.Offset,
24532414 /// The source location points to all the ranges of a switch expression, found
24542415 /// by taking this AST node index offset from the containing base node,
24552416 /// which points to a switch expression AST node. Next, navigate to any of the
......@@ -2642,29 +2603,21 @@ pub const LazySrcLoc = struct {
26422603 /// The offset of the switch AST node.
26432604 switch_node_offset: Ast.Node.Offset,
26442605 /// The index of the case to point to within this switch.
2645 case_idx: SwitchCaseIndex,
2606 case_idx: Zir.UnwrappedSwitchBlock.Case.Index,
26462607 /// The index of the item to point to within this case.
2647 item_idx: SwitchItemIndex,
2608 item_idx: SwitchItem.Index,
2609
2610 pub const Index = packed struct(u32) {
2611 kind: enum(u1) { single, range },
2612 value: u31,
2613 };
26482614 };
26492615
26502616 pub const SwitchCapture = struct {
26512617 /// The offset of the switch AST node.
26522618 switch_node_offset: Ast.Node.Offset,
26532619 /// The index of the case whose capture to point to.
2654 case_idx: SwitchCaseIndex,
2655 };
2656
2657 pub const SwitchCaseIndex = packed struct(u32) {
2658 kind: enum(u1) { scalar, multi },
2659 index: u31,
2660
2661 pub const special_else: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32)));
2662 pub const special_under: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32) - 1));
2663 };
2664
2665 pub const SwitchItemIndex = packed struct(u32) {
2666 kind: enum(u1) { single, range },
2667 index: u31,
2620 case_idx: Zir.UnwrappedSwitchBlock.Case.Index,
26682621 };
26692622
26702623 pub const ArrayCat = struct {
src/codegen/llvm.zig+6-2
......@@ -6432,7 +6432,7 @@ pub const FuncGen = struct {
64326432
64336433 // Don't worry about the size of the type -- it's irrelevant, because the prong values could be fairly dense.
64346434 // If they are, then we will construct a jump table.
6435 const min, const max = self.switchCaseItemRange(switch_br);
6435 const min, const max = self.switchCaseItemRange(switch_br) orelse break :jmp_table null;
64366436 const min_int = min.getUnsignedInt(zcu) orelse break :jmp_table null;
64376437 const max_int = max.getUnsignedInt(zcu) orelse break :jmp_table null;
64386438 const table_len = max_int - min_int + 1;
......@@ -6595,7 +6595,7 @@ pub const FuncGen = struct {
65956595 }
65966596 }
65976597
6598 fn switchCaseItemRange(self: *FuncGen, switch_br: Air.UnwrappedSwitch) [2]Value {
6598 fn switchCaseItemRange(self: *FuncGen, switch_br: Air.UnwrappedSwitch) ?[2]Value {
65996599 const zcu = self.ng.pt.zcu;
66006600 var it = switch_br.iterateCases();
66016601 var min: ?Value = null;
......@@ -6619,6 +6619,10 @@ pub const FuncGen = struct {
66196619 if (high) max = vals[1];
66206620 }
66216621 }
6622 if (min == null) {
6623 assert(max == null);
6624 return null;
6625 }
66226626 return .{ min.?, max.? };
66236627 }
66246628
src/codegen/wasm/CodeGen.zig+4-1
......@@ -4114,7 +4114,10 @@ fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kind
41144114 const zcu = cg.pt.zcu;
41154115 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
41164116 const operand = try cg.resolveInst(un_op);
4117 const err_union_ty = cg.typeOf(un_op);
4117 const err_union_ty = switch (op_kind) {
4118 .value => cg.typeOf(un_op),
4119 .ptr => cg.typeOf(un_op).childType(zcu),
4120 };
41184121 const pl_ty = err_union_ty.errorUnionPayload(zcu);
41194122
41204123 const result: WValue = result: {
src/print_zir.zig+104-287
......@@ -447,10 +447,9 @@ const Writer = struct {
447447
448448 .switch_block,
449449 .switch_block_ref,
450 .switch_block_err_union,
450451 => try self.writeSwitchBlock(stream, inst),
451452
452 .switch_block_err_union => try self.writeSwitchBlockErrUnion(stream, inst),
453
454453 .field_ptr_load,
455454 .field_ptr,
456455 .decl_literal,
......@@ -1987,322 +1986,140 @@ const Writer = struct {
19871986 try self.writeSrcNode(stream, inst_data.src_node);
19881987 }
19891988
1990 fn writeSwitchBlockErrUnion(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void {
1991 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
1992 const extra = self.code.extraData(Zir.Inst.SwitchBlockErrUnion, inst_data.payload_index);
1993
1994 var extra_index: usize = extra.end;
1995
1996 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
1997 const multi_cases_len = self.code.extra[extra_index];
1998 extra_index += 1;
1999 break :blk multi_cases_len;
2000 } else 0;
2001
2002 const err_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_uses_err_capture) blk: {
2003 const tag_capture_inst = self.code.extra[extra_index];
2004 extra_index += 1;
2005 break :blk @enumFromInt(tag_capture_inst);
2006 } else undefined;
2007
2008 try self.writeInstRef(stream, extra.data.operand);
1989 fn writeSwitchBlock(
1990 self: *Writer,
1991 stream: *std.Io.Writer,
1992 inst: Zir.Inst.Index,
1993 ) !void {
1994 const zir_switch = self.code.getSwitchBlock(inst);
1995 var extra_index = zir_switch.end;
20091996
2010 if (extra.data.bits.any_uses_err_capture) {
2011 try stream.writeAll(", err_capture=");
2012 try self.writeInstIndex(stream, err_capture_inst);
2013 }
1997 try self.writeInstRef(stream, zir_switch.main_operand);
20141998
20151999 self.indent += 2;
20162000
2017 {
2018 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
2019 extra_index += 1;
2020
2021 assert(!info.is_inline);
2022 const body = self.code.bodySlice(extra_index, info.body_len);
2023 extra_index += body.len;
2001 if (zir_switch.non_err_case) |non_err_case| {
2002 if (non_err_case.operand_is_ref) try stream.writeAll(" ref");
20242003
20252004 try stream.writeAll(",\n");
20262005 try stream.splatByteAll(' ', self.indent);
2027 try stream.writeAll("non_err => ");
2028 try self.writeBracedBody(stream, body);
2029 }
20302006
2031 if (extra.data.bits.has_else) {
2032 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
2033 extra_index += 1;
2034 const capture_text = switch (info.capture) {
2035 .none => "",
2036 .by_val => "by_val ",
2037 .by_ref => "by_ref ",
2038 };
2039 const inline_text = if (info.is_inline) "inline " else "";
2040 const body = self.code.bodySlice(extra_index, info.body_len);
2041 extra_index += body.len;
2007 try self.writeSwitchCaptures(stream, non_err_case.capture, false, inst, &zir_switch);
20422008
2009 try stream.writeAll("non_err => ");
2010 try self.writeBracedBody(stream, non_err_case.body);
2011 try stream.writeAll(" ");
2012 try self.writeSrcNode(stream, zir_switch.catch_or_if_src_node_offset.unwrap().?);
2013 }
2014 if (zir_switch.else_case) |else_case| {
20432015 try stream.writeAll(",\n");
20442016 try stream.splatByteAll(' ', self.indent);
2045 try stream.print("{s}{s}else => ", .{ capture_text, inline_text });
2046 try self.writeBracedBody(stream, body);
2047 }
2048
2049 {
2050 const scalar_cases_len = extra.data.bits.scalar_cases_len;
2051 var scalar_i: usize = 0;
2052 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2053 const item_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
2054 extra_index += 1;
2055 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
2056 extra_index += 1;
2057 const body = self.code.bodySlice(extra_index, info.body_len);
2058 extra_index += info.body_len;
2059
2060 try stream.writeAll(",\n");
2061 try stream.splatByteAll(' ', self.indent);
2062 switch (info.capture) {
2063 .none => {},
2064 .by_val => try stream.writeAll("by_val "),
2065 .by_ref => try stream.writeAll("by_ref "),
2066 }
2067 if (info.is_inline) try stream.writeAll("inline ");
2068 try self.writeInstRef(stream, item_ref);
2069 try stream.writeAll(" => ");
2070 try self.writeBracedBody(stream, body);
2071 }
2072 }
2073 {
2074 var multi_i: usize = 0;
2075 while (multi_i < multi_cases_len) : (multi_i += 1) {
2076 const items_len = self.code.extra[extra_index];
2077 extra_index += 1;
2078 const ranges_len = self.code.extra[extra_index];
2079 extra_index += 1;
2080 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
2081 extra_index += 1;
2082 const items = self.code.refSlice(extra_index, items_len);
2083 extra_index += items_len;
2084
2085 try stream.writeAll(",\n");
2086 try stream.splatByteAll(' ', self.indent);
2087 switch (info.capture) {
2088 .none => {},
2089 .by_val => try stream.writeAll("by_val "),
2090 .by_ref => try stream.writeAll("by_ref "),
2091 }
2092 if (info.is_inline) try stream.writeAll("inline ");
2093
2094 for (items, 0..) |item_ref, item_i| {
2095 if (item_i != 0) try stream.writeAll(", ");
2096 try self.writeInstRef(stream, item_ref);
2097 }
2098
2099 var range_i: usize = 0;
2100 while (range_i < ranges_len) : (range_i += 1) {
2101 const item_first = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
2102 extra_index += 1;
2103 const item_last = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
2104 extra_index += 1;
2105
2106 if (range_i != 0 or items.len != 0) {
2107 try stream.writeAll(", ");
2108 }
2109 try self.writeInstRef(stream, item_first);
2110 try stream.writeAll("...");
2111 try self.writeInstRef(stream, item_last);
2112 }
2113
2114 const body = self.code.bodySlice(extra_index, info.body_len);
2115 extra_index += info.body_len;
2116 try stream.writeAll(" => ");
2117 try self.writeBracedBody(stream, body);
2118 }
2119 }
2120
2121 self.indent -= 2;
2122
2123 try stream.writeAll(") ");
2124 try self.writeSrcNode(stream, inst_data.src_node);
2125 }
2126
2127 fn writeSwitchBlock(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void {
2128 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
2129 const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
2130
2131 var extra_index: usize = extra.end;
2132
2133 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
2134 const multi_cases_len = self.code.extra[extra_index];
2135 extra_index += 1;
2136 break :blk multi_cases_len;
2137 } else 0;
2138
2139 const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: {
2140 const tag_capture_inst = self.code.extra[extra_index];
2141 extra_index += 1;
2142 break :blk @enumFromInt(tag_capture_inst);
2143 } else undefined;
21442017
2145 try self.writeInstRef(stream, extra.data.operand);
2146
2147 if (extra.data.bits.any_has_tag_capture) {
2148 try stream.writeAll(", tag_capture=");
2149 try self.writeInstIndex(stream, tag_capture_inst);
2150 }
2151
2152 self.indent += 2;
2153
2154 const special_prongs = extra.data.bits.special_prongs;
2155
2156 if (special_prongs.hasElse()) {
2157 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(self.code.extra[extra_index]);
2158 const capture_text = switch (info.capture) {
2159 .none => "",
2160 .by_val => "by_val ",
2161 .by_ref => "by_ref ",
2162 };
2163 const inline_text = if (info.is_inline) "inline " else "";
2164 extra_index += 1;
2165 const body = self.code.bodySlice(extra_index, info.body_len);
2166 extra_index += body.len;
2018 try self.writeSwitchCaptures(stream, else_case.capture, else_case.has_tag_capture, inst, &zir_switch);
2019 if (else_case.is_inline) try stream.writeAll("inline ");
21672020
2168 try stream.writeAll(",\n");
2169 try stream.splatByteAll(' ', self.indent);
2170 try stream.print("{s}{s}else => ", .{ capture_text, inline_text });
2171 try self.writeBracedBody(stream, body);
2021 try stream.writeAll("else => ");
2022 try self.writeBracedBody(stream, else_case.body);
21722023 }
21732024
2174 if (special_prongs.hasUnder()) {
2175 var single_item_ref: Zir.Inst.Ref = .none;
2176 var items_len: u32 = 0;
2177 var ranges_len: u32 = 0;
2178 if (special_prongs.hasOneAdditionalItem()) {
2179 single_item_ref = @enumFromInt(self.code.extra[extra_index]);
2180 extra_index += 1;
2181 } else if (special_prongs.hasManyAdditionalItems()) {
2182 items_len = self.code.extra[extra_index];
2183 extra_index += 1;
2184 ranges_len = self.code.extra[extra_index];
2185 extra_index += 1;
2186 }
2187 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(self.code.extra[extra_index]);
2188 extra_index += 1;
2189 const items = self.code.refSlice(extra_index, items_len);
2190 extra_index += items_len;
2191
2025 var case_it = zir_switch.iterateCases();
2026 while (case_it.next()) |case| {
21922027 try stream.writeAll(",\n");
21932028 try stream.splatByteAll(' ', self.indent);
2194 switch (info.capture) {
2195 .none => {},
2196 .by_val => try stream.writeAll("by_val "),
2197 .by_ref => try stream.writeAll("by_ref "),
2198 }
2199 if (info.is_inline) try stream.writeAll("inline ");
2200
2201 try stream.writeAll("_");
2202 if (single_item_ref != .none) {
2203 try stream.writeAll(", ");
2204 try self.writeInstRef(stream, single_item_ref);
2205 }
2206 for (items) |item_ref| {
2207 try stream.writeAll(", ");
2208 try self.writeInstRef(stream, item_ref);
2209 }
2210
2211 var range_i: usize = 0;
2212 while (range_i < ranges_len) : (range_i += 1) {
2213 const item_first: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
2214 extra_index += 1;
2215 const item_last: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
2216 extra_index += 1;
22172029
2218 try stream.writeAll(", ");
2219 try self.writeInstRef(stream, item_first);
2220 try stream.writeAll("...");
2221 try self.writeInstRef(stream, item_last);
2222 }
2030 const prong_info = case.prong_info;
2031 try self.writeSwitchCaptures(stream, prong_info.capture, prong_info.has_tag_capture, inst, &zir_switch);
2032 if (prong_info.is_inline) try stream.writeAll("inline ");
22232033
2224 const body = self.code.bodySlice(extra_index, info.body_len);
2225 extra_index += info.body_len;
2226 try stream.writeAll(" => ");
2227 try self.writeBracedBody(stream, body);
2228 }
2034 const prong_body = self.code.bodySlice(extra_index, prong_info.body_len);
2035 extra_index += prong_body.len;
22292036
2230 {
2231 const scalar_cases_len = extra.data.bits.scalar_cases_len;
2232 var scalar_i: usize = 0;
2233 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2234 const item_ref: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
2235 extra_index += 1;
2236 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(self.code.extra[extra_index]);
2237 extra_index += 1;
2238 const body = self.code.bodySlice(extra_index, info.body_len);
2239 extra_index += info.body_len;
2037 for (case.item_infos, 0..) |item_info, i| {
2038 if (i > 0) try stream.writeAll(", ");
22402039
2241 try stream.writeAll(",\n");
2242 try stream.splatByteAll(' ', self.indent);
2243 switch (info.capture) {
2244 .none => {},
2245 .by_val => try stream.writeAll("by_val "),
2246 .by_ref => try stream.writeAll("by_ref "),
2040 switch (item_info.unwrap()) {
2041 .enum_literal => |str_index| {
2042 const str = self.code.nullTerminatedString(str_index);
2043 try stream.print("\".{f}\"", .{std.zig.fmtString(str)});
2044 },
2045 .error_value => |str_index| {
2046 const str = self.code.nullTerminatedString(str_index);
2047 try stream.print("\"error.{f}\"", .{std.zig.fmtString(str)});
2048 },
2049 .under => try stream.writeByte('_'),
2050 .body_len => |body_len| {
2051 const item_body = self.code.bodySlice(extra_index, body_len);
2052 extra_index += item_body.len;
2053 try self.writeBracedDecl(stream, item_body);
2054 },
22472055 }
2248 if (info.is_inline) try stream.writeAll("inline ");
2249 try self.writeInstRef(stream, item_ref);
2250 try stream.writeAll(" => ");
2251 try self.writeBracedBody(stream, body);
22522056 }
2253 }
2254 {
2255 var multi_i: usize = 0;
2256 while (multi_i < multi_cases_len) : (multi_i += 1) {
2257 const items_len = self.code.extra[extra_index];
2258 extra_index += 1;
2259 const ranges_len = self.code.extra[extra_index];
2260 extra_index += 1;
2261 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(self.code.extra[extra_index]);
2262 extra_index += 1;
2263 const items = self.code.refSlice(extra_index, items_len);
2264 extra_index += items_len;
2265
2266 try stream.writeAll(",\n");
2267 try stream.splatByteAll(' ', self.indent);
2268 switch (info.capture) {
2269 .none => {},
2270 .by_val => try stream.writeAll("by_val "),
2271 .by_ref => try stream.writeAll("by_ref "),
2272 }
2273 if (info.is_inline) try stream.writeAll("inline ");
2274
2275 for (items, 0..) |item_ref, item_i| {
2276 if (item_i != 0) try stream.writeAll(", ");
2277 try self.writeInstRef(stream, item_ref);
2057 for (case.range_infos, 0..) |range_info, i| {
2058 if (i > 0 and case.item_infos.len == 0) try stream.writeAll(", ");
2059 switch (range_info[0].unwrap()) {
2060 .enum_literal => |str_index| {
2061 const str = self.code.nullTerminatedString(str_index);
2062 try stream.print("\".{f}\"", .{std.zig.fmtString(str)});
2063 },
2064 .error_value => |str_index| {
2065 const str = self.code.nullTerminatedString(str_index);
2066 try stream.print("\"error.{f}\"", .{std.zig.fmtString(str)});
2067 },
2068 .under => unreachable, // '_..._' is not allowed
2069 .body_len => |body_len| {
2070 const item_body = self.code.bodySlice(extra_index, body_len);
2071 extra_index += item_body.len;
2072 try self.writeBracedDecl(stream, item_body);
2073 },
22782074 }
2279
2280 var range_i: usize = 0;
2281 while (range_i < ranges_len) : (range_i += 1) {
2282 const item_first: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
2283 extra_index += 1;
2284 const item_last: Zir.Inst.Ref = @enumFromInt(self.code.extra[extra_index]);
2285 extra_index += 1;
2286
2287 if (range_i != 0 or items.len != 0) {
2288 try stream.writeAll(", ");
2289 }
2290 try self.writeInstRef(stream, item_first);
2291 try stream.writeAll("...");
2292 try self.writeInstRef(stream, item_last);
2075 try stream.writeAll("...");
2076 switch (range_info[1].unwrap()) {
2077 .enum_literal => |str_index| {
2078 const str = self.code.nullTerminatedString(str_index);
2079 try stream.print("\".{f}\"", .{std.zig.fmtString(str)});
2080 },
2081 .error_value => |str_index| {
2082 const str = self.code.nullTerminatedString(str_index);
2083 try stream.print("\"error.{f}\"", .{std.zig.fmtString(str)});
2084 },
2085 .under => unreachable, // '_..._' is not allowed
2086 .body_len => |body_len| {
2087 const item_body = self.code.bodySlice(extra_index, body_len);
2088 extra_index += item_body.len;
2089 try self.writeBracedDecl(stream, item_body);
2090 },
22932091 }
2294
2295 const body = self.code.bodySlice(extra_index, info.body_len);
2296 extra_index += info.body_len;
2297 try stream.writeAll(" => ");
2298 try self.writeBracedBody(stream, body);
22992092 }
2093 try stream.writeAll(" => ");
2094 try self.writeBracedBody(stream, prong_body);
23002095 }
23012096
23022097 self.indent -= 2;
23032098
23042099 try stream.writeAll(") ");
2305 try self.writeSrcNode(stream, inst_data.src_node);
2100 try self.writeSrcNode(stream, zir_switch.switch_src_node_offset);
2101 }
2102
2103 fn writeSwitchCaptures(
2104 self: *Writer,
2105 stream: *std.Io.Writer,
2106 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
2107 has_tag_capture: bool,
2108 switch_inst: Zir.Inst.Index,
2109 zir_switch: *const Zir.UnwrappedSwitchBlock,
2110 ) !void {
2111 if (capture != .none) {
2112 try stream.print("{t}=", .{capture});
2113 const capture_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst;
2114 try self.writeInstIndex(stream, capture_inst);
2115 try stream.writeAll(" ");
2116 }
2117 if (has_tag_capture) {
2118 try stream.writeAll("tag=");
2119 const capture_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst;
2120 try self.writeInstIndex(stream, capture_inst);
2121 try stream.writeAll(" ");
2122 }
23062123 }
23072124
23082125 fn writePlNodeField(self: *Writer, stream: *std.Io.Writer, inst: Zir.Inst.Index) !void {
test/behavior/for.zig+17
......@@ -524,3 +524,20 @@ test "for loop 0 length range" {
524524 comptime unreachable;
525525 }
526526}
527
528test "labeled break from else" {
529 const S = struct {
530 fn doTheTest(x: u32) !void {
531 var y: u32 = 0;
532 const ok = label: while (y < x) : (y += 1) {
533 if (y == 10) break :label false;
534 } else {
535 break :label true;
536 };
537 try expect(ok);
538 }
539 };
540
541 try S.doTheTest(5);
542 try comptime S.doTheTest(5);
543}
test/behavior/packed-struct.zig+14-28
......@@ -513,35 +513,21 @@ test "@intFromPtr on a packed struct field unaligned and nested" {
513513 };
514514 };
515515
516 switch (comptime @alignOf(S2)) {
517 4 => {
518 comptime assert(@TypeOf(&S2.s.base) == *align(4) u8);
519 comptime assert(@TypeOf(&S2.s.p0.a) == *align(1:0:2) u4);
520 comptime assert(@TypeOf(&S2.s.p0.b) == *align(1:4:2) u4);
521 comptime assert(@TypeOf(&S2.s.p0.c) == *u8);
522 comptime assert(@TypeOf(&S2.s.bit0) == *align(4:24:8) u1);
523 comptime assert(@TypeOf(&S2.s.p1.a) == *align(4:25:8) u8);
524 comptime assert(@TypeOf(&S2.s.p2.a) == *align(4:33:8) u7);
525 comptime assert(@TypeOf(&S2.s.p2.b) == *u8);
526 comptime assert(@TypeOf(&S2.s.p3.a) == *align(2:0:2) u4);
527 comptime assert(@TypeOf(&S2.s.p3.b) == *align(2:4:2) u4);
528 comptime assert(@TypeOf(&S2.s.p3.c) == *u8);
529 },
530 8 => {
531 comptime assert(@TypeOf(&S2.s.base) == *align(8) u8);
532 comptime assert(@TypeOf(&S2.s.p0.a) == *align(1:0:2) u4);
533 comptime assert(@TypeOf(&S2.s.p0.b) == *align(1:4:2) u4);
534 comptime assert(@TypeOf(&S2.s.p0.c) == *u8);
535 comptime assert(@TypeOf(&S2.s.bit0) == *align(8:24:8) u1);
536 comptime assert(@TypeOf(&S2.s.p1.a) == *align(8:25:8) u8);
537 comptime assert(@TypeOf(&S2.s.p2.a) == *align(8:33:8) u7);
538 comptime assert(@TypeOf(&S2.s.p2.b) == *u8);
539 comptime assert(@TypeOf(&S2.s.p3.a) == *align(2:0:2) u4);
540 comptime assert(@TypeOf(&S2.s.p3.b) == *align(2:4:2) u4);
541 comptime assert(@TypeOf(&S2.s.p3.c) == *u8);
542 },
543 else => {},
516 {
517 const a = @alignOf(S2);
518 comptime assert(@TypeOf(&S2.s.base) == *align(a:0:8) u8);
519 comptime assert(@TypeOf(&S2.s.p0.a) == *align(a:8:8) u4);
520 comptime assert(@TypeOf(&S2.s.p0.b) == *align(a:12:8) u4);
521 comptime assert(@TypeOf(&S2.s.p0.c) == *align(a:16:8) u8);
522 comptime assert(@TypeOf(&S2.s.bit0) == *align(a:24:8) u1);
523 comptime assert(@TypeOf(&S2.s.p1.a) == *align(a:25:8) u8);
524 comptime assert(@TypeOf(&S2.s.p2.a) == *align(a:33:8) u7);
525 comptime assert(@TypeOf(&S2.s.p2.b) == *align(a:40:8) u8);
526 comptime assert(@TypeOf(&S2.s.p3.a) == *align(a:48:8) u4);
527 comptime assert(@TypeOf(&S2.s.p3.b) == *align(a:52:8) u4);
528 comptime assert(@TypeOf(&S2.s.p3.c) == *align(a:56:8) u8);
544529 }
530
545531 try expect(@intFromPtr(&S2.s.base) - @intFromPtr(&S2.s) == 0);
546532 try expect(@intFromPtr(&S2.s.p0.a) - @intFromPtr(&S2.s) == 0);
547533 try expect(@intFromPtr(&S2.s.p0.b) - @intFromPtr(&S2.s) == 0);
test/behavior/switch.zig+187
......@@ -1120,3 +1120,190 @@ test "switch on non-exhaustive enum" {
11201120 try E.doTheTest(.a);
11211121 try comptime E.doTheTest(.a);
11221122}
1123
1124test "decl literals as switch cases" {
1125 const E = enum(u8) {
1126 bar = 3,
1127 _,
1128
1129 const foo: @This() = @enumFromInt(0xa);
1130
1131 fn doTheTest(e: @This()) !void {
1132 switch (e) {
1133 .bar => return error.TestFailed,
1134 .foo => {},
1135 else => return error.TestFailed,
1136 }
1137 }
1138 };
1139
1140 try E.doTheTest(.foo);
1141 try comptime E.doTheTest(.foo);
1142}
1143
1144// TODO audit after #15909 and/or #19855 are decided/implemented
1145test "switch with uninstantiable union fields" {
1146 const U = union(enum) {
1147 ok: void,
1148 a: noreturn,
1149 b: noreturn,
1150 c: error{},
1151
1152 fn doTheTest(u: @This()) void {
1153 switch (u) {
1154 .ok => {},
1155 .a => comptime unreachable,
1156 .b => comptime unreachable,
1157 .c => comptime unreachable,
1158 }
1159 switch (u) {
1160 .ok => {},
1161 .a, .b, .c => comptime unreachable,
1162 }
1163 switch (u) {
1164 .ok => {},
1165 else => comptime unreachable,
1166 }
1167 switch (u) {
1168 .a => comptime unreachable,
1169 .ok, .b, .c => {},
1170 }
1171 }
1172 };
1173
1174 U.doTheTest(.ok);
1175 comptime U.doTheTest(.ok);
1176}
1177
1178test "switch with tag capture" {
1179 const U = union(enum) {
1180 a,
1181 b: i32,
1182 c: u8,
1183 d: i32,
1184 e: noreturn,
1185
1186 fn doTheTest() !void {
1187 try doTheSwitch(.a);
1188 try doTheSwitch(.{ .b = 123 });
1189 try doTheSwitch(.{ .c = 0xFF });
1190 }
1191 fn doTheSwitch(u: @This()) !void {
1192 switch (u) {
1193 .a => |nothing, tag| {
1194 comptime assert(nothing == {});
1195 comptime assert(tag == .a);
1196 try expect(@intFromEnum(tag) == @intFromEnum(@This().a));
1197 },
1198 .b, .d => |_, tag| {
1199 try expect(tag == .b or tag == .d);
1200 },
1201 .e => |payload, tag| {
1202 _ = &payload;
1203 _ = &tag;
1204 comptime unreachable;
1205 },
1206 else => |un, tag| {
1207 try expect(tag == .c);
1208 try expect(un == .c);
1209 try expect(un.c == 0xFF);
1210 },
1211 }
1212 switch (u) {
1213 inline .a, .b, .c => |payload, tag| {
1214 if (@TypeOf(payload) == void) comptime assert(tag == .a);
1215 if (@TypeOf(payload) == i32) comptime assert(tag == .b);
1216 if (@TypeOf(payload) == u8) comptime assert(tag == .c);
1217 },
1218 inline else => |payload, tag| {
1219 if (@TypeOf(payload) == i32) comptime assert(tag == .d);
1220 comptime assert(tag != .e);
1221 },
1222 }
1223 }
1224 };
1225
1226 try U.doTheTest();
1227 try comptime U.doTheTest();
1228}
1229
1230test "switch with complex item expressions" {
1231 const S = struct {
1232 fn doTheTest() !void {
1233 try doTheSwitch(2000, 20);
1234 try doTheSwitch(2000, 10);
1235 try doTheSwitch(2000, 5);
1236
1237 try doTheOtherSwitch(@enumFromInt(123));
1238 try doTheOtherSwitch(@enumFromInt(456));
1239 }
1240 fn doTheSwitch(x: u32, comptime factor: u32) !void {
1241 const ok = switch (x) {
1242 num(factor) => true,
1243 typedNum(u32, factor) => true,
1244 blk: {
1245 var val = 400;
1246 val *= factor;
1247 break :blk val;
1248 } => true,
1249 else => false,
1250 };
1251 try expect(ok);
1252 }
1253 fn num(factor: u32) u32 {
1254 return 100 * factor;
1255 }
1256 fn typedNum(comptime T: type, factor: T) T {
1257 return 200 * factor;
1258 }
1259
1260 const E = enum(u32) { _ };
1261 fn doTheOtherSwitch(e: E) !void {
1262 const ok = switch (e) {
1263 @enumFromInt(123) => true,
1264 @enumFromInt(456) => true,
1265 else => false,
1266 };
1267 try expect(ok);
1268 }
1269 };
1270
1271 try S.doTheTest();
1272 try comptime S.doTheTest();
1273}
1274
1275test "switch evaluation order" {
1276 const eu: anyerror!u32 = 0;
1277 _ = eu catch |err| switch (err) {
1278 if (true) @compileError("unreachable") => unreachable,
1279 else => unreachable,
1280 };
1281}
1282
1283test "switch resolves lazy values correctly" {
1284 const S = extern struct {
1285 a: u16,
1286 b: i16,
1287 };
1288 switch (@sizeOf(S)) {
1289 4 => {},
1290 else => comptime unreachable,
1291 }
1292}
1293
1294test "single-item prong in switch on enum has comptime-known capture" {
1295 const E = enum {
1296 a,
1297 b,
1298 c,
1299 fn doTheTest(e: @This()) !void {
1300 switch (e) {
1301 .a => |tag| comptime assert(tag == .a),
1302 .b => return error.TestFailed,
1303 .c => return error.TestFailed,
1304 }
1305 }
1306 };
1307 try E.doTheTest(.a);
1308 try comptime E.doTheTest(.a);
1309}
test/behavior/switch_loop.zig+239
......@@ -1,5 +1,6 @@
11const builtin = @import("builtin");
22const std = @import("std");
3const assert = std.debug.assert;
34const expect = std.testing.expect;
45
56test "simple switch loop" {
......@@ -270,3 +271,241 @@ test "switch loop on non-exhaustive enum" {
270271 try S.doTheTest();
271272 try comptime S.doTheTest();
272273}
274
275test "switch loop with discarded tag capture" {
276 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
277 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
278 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
279
280 const S = struct {
281 const U = union(enum) {
282 a: u32,
283 b: u32,
284 c: u32,
285 };
286
287 fn doTheTest() void {
288 const a: U = .{ .a = 10 };
289 blk: switch (a) {
290 inline .b => |_, tag| {
291 _ = tag;
292 continue :blk .{ .c = 20 };
293 },
294 else => {},
295 }
296 }
297 };
298 S.doTheTest();
299 comptime S.doTheTest();
300}
301
302test "switch loop with single catch-all prong" {
303 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
304
305 const S = struct {
306 const E = enum { a, b, c };
307 const U = union(E) { a: u32, b: u16, c: u8 };
308
309 fn doTheTest() !void {
310 var x: usize = 0;
311 label: switch (E.a) {
312 else => {
313 x += 1;
314 if (x == 10) break :label;
315 if (x >= 5) continue :label .b;
316 continue :label .c;
317 },
318 }
319 try expect(x == 10);
320
321 label: switch (E.a) {
322 .a, .b, .c => {
323 x += 1;
324 if (x == 20) break :label;
325 if (x >= 15) continue :label .b;
326 continue :label .c;
327 },
328 }
329 try expect(x == 20);
330
331 label: switch (E.a) {
332 else => if (false) continue :label true,
333 }
334
335 const ok = label: switch (U{ .a = 123 }) {
336 else => |u| {
337 const y: u32 = switch (u) {
338 inline else => |y| y,
339 };
340 if (y == 456) break :label true;
341 continue :label .{ .b = 456 };
342 },
343 };
344 comptime assert(ok);
345 }
346 };
347 try S.doTheTest();
348 try comptime S.doTheTest();
349}
350
351test "switch loop on type with opv" {
352 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
353
354 const S = struct {
355 const E = enum { opv };
356 const U = union(E) { opv: u0 };
357
358 fn doTheTest() !void {
359 var x: usize = 0;
360 label: switch (E.opv) {
361 .opv => {
362 x += 1;
363 if (x == 10) break :label;
364 if (x >= 5) continue :label .opv;
365 continue :label .opv;
366 },
367 }
368 try expect(x == 10);
369
370 label: switch (E.opv) {
371 else => {
372 x += 1;
373 if (x == 20) break :label;
374 if (x >= 15) continue :label .opv;
375 continue :label .opv;
376 },
377 }
378 try expect(x == 20);
379
380 label: switch (E.opv) {
381 .opv => if (false) continue :label true,
382 }
383
384 label: switch (U{ .opv = 0 }) {
385 .opv => |val| {
386 x += 1;
387 if (x == 30) break :label;
388 if (x >= 25) continue :label .{ .opv = val };
389 continue :label .{ .opv = 0 };
390 },
391 }
392 try expect(x == 30);
393 }
394 };
395 try S.doTheTest();
396 try comptime S.doTheTest();
397}
398
399test "switch loop with tag capture" {
400 const U = union(enum) {
401 a,
402 b: i32,
403 c: u8,
404 d: i32,
405 e: noreturn,
406
407 fn doTheTest() !void {
408 try doTheSwitch(.a);
409 try doTheSwitch(.{ .b = 123 });
410 try doTheSwitch(.{ .c = 0xFF });
411 }
412 fn doTheSwitch(u: @This()) !void {
413 const ok1 = label: switch (u) {
414 .a => |nothing, tag| {
415 comptime assert(nothing == {});
416 comptime assert(tag == .a);
417 try expect(@intFromEnum(tag) == @intFromEnum(@This().a));
418 continue :label .{ .d = 456 };
419 },
420 .b, .d => |_, tag| {
421 try expect(tag == .b or tag == .d);
422 continue :label .{ .c = 0x0F };
423 },
424 .e => |payload, tag| {
425 _ = &payload;
426 _ = &tag;
427 return error.AnalyzedNoreturnProng;
428 },
429 else => |un, tag| {
430 try expect(tag == .c);
431 try expect(un == .c);
432 if (un.c == 0xFF) continue :label .a;
433 if (un.c == 0x00) break :label false;
434 break :label true;
435 },
436 };
437 try expect(ok1);
438
439 const ok2 = label: switch (u) {
440 inline .a, .b, .c => |payload, tag| {
441 if (@TypeOf(payload) == void) {
442 comptime assert(tag == .a);
443 continue :label .{ .b = 456 };
444 }
445 if (@TypeOf(payload) == i32) {
446 comptime assert(tag == .b);
447 continue :label .{ .d = payload };
448 }
449 if (@TypeOf(payload) == u8) {
450 comptime assert(tag == .c);
451 continue :label .{ .d = payload };
452 }
453 },
454 inline else => |payload, tag| {
455 if (@TypeOf(payload) == i32) comptime assert(tag == .d);
456 comptime assert(tag != .e);
457 if (payload == 0) break :label false;
458 break :label true;
459 },
460 };
461 try expect(ok2);
462 }
463 };
464
465 try U.doTheTest();
466 try comptime U.doTheTest();
467}
468
469test "switch loop for error handling" {
470 const Error = error{ MyError, MyOtherError };
471 const S = struct {
472 fn doTheTest() !void {
473 try doThePayloadSwitch(123);
474 try doTheErrSwitch(error.MyError);
475 try doTheErrSwitch(error.MyOtherError);
476 }
477 fn doThePayloadSwitch(eu: Error!u32) !void {
478 const x = eu catch |err| label: switch (err) {
479 error.MyError => continue :label error.MyOtherError,
480 error.MyOtherError => break :label 0,
481 };
482 try expect(x == 123);
483
484 const y = if (eu) |payload| label: {
485 break :label payload * 2;
486 } else |err| label: switch (err) {
487 error.MyError => continue :label error.MyOtherError,
488 error.MyOtherError => break :label 0,
489 };
490 try expect(y == 246);
491 }
492 fn doTheErrSwitch(eu: Error!u32) !void {
493 const x = eu catch |err| label: switch (err) {
494 error.MyError => continue :label error.MyOtherError,
495 error.MyOtherError => break :label 123,
496 };
497 try expect(x == 123);
498
499 const y = if (eu) |payload| label: {
500 break :label payload * 2;
501 } else |err| label: switch (err) {
502 error.MyError => continue :label error.MyOtherError,
503 error.MyOtherError => break :label 123,
504 };
505 try expect(y == 123);
506 }
507 };
508
509 try S.doTheTest();
510 try comptime S.doTheTest();
511}
test/behavior/switch_on_captured_error.zig+200-12
......@@ -18,6 +18,8 @@ test "switch on error union catch capture" {
1818 try testCapture();
1919 try testInline();
2020 try testEmptyErrSet();
21 try testUnreachableElseProng();
22 try testErrNotInSet();
2123 try testAddressOf();
2224 }
2325
......@@ -240,22 +242,90 @@ test "switch on error union catch capture" {
240242 {
241243 var a: error{}!u64 = 0;
242244 _ = &a;
243 const b: u64 = a catch |err| switch (err) {
244 else => |e| return e,
245 const b = a catch |err| switch (err) {
246 undefined => @compileError("unreachable"),
245247 };
246248 try expectEqual(@as(u64, 0), b);
247249 }
250 }
251
252 fn testUnreachableElseProng() !void {
248253 {
249254 var a: error{}!u64 = 0;
250255 _ = &a;
251 const b: u64 = a catch |err| switch (err) {
252 error.UnknownError => return error.Fail,
256 const b = a catch |err| switch (err) {
257 else => unreachable,
258 };
259 try expectEqual(@as(u64, 0), b);
260 }
261 {
262 var a: error{}!u64 = 0;
263 _ = &a;
264 const b = a catch |err| switch (err) {
265 else => return,
266 };
267 try expectEqual(@as(u64, 0), b);
268 }
269 {
270 var a: error{}!u64 = 0;
271 _ = &a;
272 const b = a catch |err| switch (err) {
273 else => |e| return e,
274 };
275 try expectEqual(@as(u64, 0), b);
276 }
277 {
278 var a: error{MyError}!u64 = error.MyError;
279 _ = &a;
280 const b = a catch |err| switch (err) {
281 error.MyError => 0,
282 else => unreachable,
283 };
284 try expectEqual(@as(u64, 0), b);
285 }
286 {
287 var a: error{MyError}!u64 = error.MyError;
288 _ = &a;
289 const b = a catch |err| switch (err) {
290 error.MyError => 0,
291 else => return,
292 };
293 try expectEqual(@as(u64, 0), b);
294 }
295 {
296 var a: error{MyError}!u64 = error.MyError;
297 _ = &a;
298 const b = a catch |err| switch (err) {
299 error.MyError => 0,
253300 else => |e| return e,
254301 };
255302 try expectEqual(@as(u64, 0), b);
256303 }
257304 }
258305
306 fn testErrNotInSet() !void {
307 {
308 var a: error{MyError}!u64 = 0;
309 _ = &a;
310 const b = a catch |err| switch (err) {
311 error.MyError => 1,
312 error.MyOtherError => comptime unreachable,
313 error.YetAnotherError, error.ThereIsAnother => comptime unreachable,
314 };
315 try expectEqual(@as(u64, 0), b);
316 }
317 {
318 var a: error{MyError}!u64 = error.MyError;
319 _ = &a;
320 const b = a catch |err| switch (err) {
321 error.MyError => 0,
322 error.MyOtherError => comptime unreachable,
323 error.YetAnotherError, error.ThereIsAnother => comptime unreachable,
324 };
325 try expectEqual(@as(u64, 0), b);
326 }
327 }
328
259329 fn testAddressOf() !void {
260330 {
261331 const a: anyerror!usize = 0;
......@@ -318,6 +388,9 @@ test "switch on error union if else capture" {
318388 try testInlinePtr();
319389 try testEmptyErrSet();
320390 try testEmptyErrSetPtr();
391 try testUnreachableElseProng();
392 try testUnreachableElseProngPtr();
393 try testErrNotInSet();
321394 try testAddressOf();
322395 }
323396
......@@ -755,40 +828,155 @@ test "switch on error union if else capture" {
755828 {
756829 var a: error{}!u64 = 0;
757830 _ = &a;
758 const b: u64 = if (a) |x| x else |err| switch (err) {
759 else => |e| return e,
831 const b = if (a) |x| x else |err| switch (err) {
832 undefined => @compileError("unreachable"),
760833 };
761834 try expectEqual(@as(u64, 0), b);
762835 }
836 }
837
838 fn testEmptyErrSetPtr() !void {
763839 {
764840 var a: error{}!u64 = 0;
765841 _ = &a;
766 const b: u64 = if (a) |x| x else |err| switch (err) {
842 const b = if (a) |*x| x.* else |err| switch (err) {
843 undefined => @compileError("unreachable"),
844 };
845 try expectEqual(@as(u64, 0), b);
846 }
847 }
848
849 fn testUnreachableElseProng() !void {
850 {
851 var a: error{}!u64 = 0;
852 _ = &a;
853 const b = if (a) |x| x else |err| switch (err) {
854 else => unreachable,
855 };
856 try expectEqual(@as(u64, 0), b);
857 }
858 {
859 var a: error{}!u64 = 0;
860 _ = &a;
861 const b = if (a) |x| x else |err| switch (err) {
862 error.UnknownError => return error.Fail,
863 else => return,
864 };
865 try expectEqual(@as(u64, 0), b);
866 }
867 {
868 var a: error{}!u64 = 0;
869 _ = &a;
870 const b = if (a) |x| x else |err| switch (err) {
767871 error.UnknownError => return error.Fail,
768872 else => |e| return e,
769873 };
770874 try expectEqual(@as(u64, 0), b);
771875 }
876 {
877 var a: error{MyError}!u64 = error.MyError;
878 _ = &a;
879 const b = if (a) |x| x else |err| switch (err) {
880 error.MyError => 0,
881 else => unreachable,
882 };
883 try expectEqual(@as(u64, 0), b);
884 }
885 {
886 var a: error{MyError}!u64 = error.MyError;
887 _ = &a;
888 const b = if (a) |x| x else |err| switch (err) {
889 error.MyError => 0,
890 else => return,
891 };
892 try expectEqual(@as(u64, 0), b);
893 }
894 {
895 var a: error{MyError}!u64 = error.MyError;
896 _ = &a;
897 const b = if (a) |x| x else |err| switch (err) {
898 error.MyError => 0,
899 else => |e| return e,
900 };
901 try expectEqual(@as(u64, 0), b);
902 }
772903 }
773904
774 fn testEmptyErrSetPtr() !void {
905 fn testUnreachableElseProngPtr() !void {
775906 {
776907 var a: error{}!u64 = 0;
777908 _ = &a;
778 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
779 else => |e| return e,
909 const b = if (a) |*x| x.* else |err| switch (err) {
910 else => unreachable,
780911 };
781912 try expectEqual(@as(u64, 0), b);
782913 }
783914 {
784915 var a: error{}!u64 = 0;
785916 _ = &a;
786 const b: u64 = if (a) |*x| x.* else |err| switch (err) {
787 error.UnknownError => return error.Fail,
917 const b = if (a) |*x| x.* else |err| switch (err) {
918 else => return,
919 };
920 try expectEqual(@as(u64, 0), b);
921 }
922 {
923 var a: error{}!u64 = 0;
924 _ = &a;
925 const b = if (a) |*x| x.* else |err| switch (err) {
788926 else => |e| return e,
789927 };
790928 try expectEqual(@as(u64, 0), b);
791929 }
930 {
931 var a: error{MyError}!u64 = error.MyError;
932 _ = &a;
933 const b = if (a) |*x| x.* else |err| switch (err) {
934 error.MyError => 0,
935 else => unreachable,
936 };
937 try expectEqual(@as(u64, 0), b);
938 }
939 {
940 var a: error{MyError}!u64 = error.MyError;
941 _ = &a;
942 const b = if (a) |*x| x.* else |err| switch (err) {
943 error.MyError => 0,
944 else => return,
945 };
946 try expectEqual(@as(u64, 0), b);
947 }
948 {
949 var a: error{MyError}!u64 = error.MyError;
950 _ = &a;
951 const b = if (a) |*x| x.* else |err| switch (err) {
952 error.MyError => 0,
953 else => |e| return e,
954 };
955 try expectEqual(@as(u64, 0), b);
956 }
957 }
958
959 fn testErrNotInSet() !void {
960 {
961 var a: error{MyError}!u64 = 0;
962 _ = &a;
963 const b = if (a) |x| x else |err| switch (err) {
964 error.MyError => 1,
965 error.MyOtherError => comptime unreachable,
966 error.YetAnotherError, error.ThereIsAnother => comptime unreachable,
967 };
968 try expectEqual(@as(u64, 0), b);
969 }
970 {
971 var a: error{MyError}!u64 = error.MyError;
972 _ = &a;
973 const b = if (a) |x| x else |err| switch (err) {
974 error.MyError => 0,
975 error.MyOtherError => comptime unreachable,
976 error.YetAnotherError, error.ThereIsAnother => comptime unreachable,
977 };
978 try expectEqual(@as(u64, 0), b);
979 }
792980 }
793981
794982 fn testAddressOf() !void {
test/behavior/while.zig+17
......@@ -399,3 +399,20 @@ test "breaking from a loop in an if statement" {
399399 } else 2;
400400 _ = opt;
401401}
402
403test "labeled break from else" {
404 const S = struct {
405 fn doTheTest(x: u32) !void {
406 const arr: []const u32 = &.{ 1, 3, 10 };
407 const ok = label: for (arr) |y| {
408 if (y == x) break :label false;
409 } else {
410 break :label true;
411 };
412 try expect(ok);
413 }
414 };
415
416 try S.doTheTest(5);
417 try comptime S.doTheTest(5);
418}
test/cases/compile_errors/continue_loop_from_else_block.zig created+17
......@@ -0,0 +1,17 @@
1export fn entry1() void {
2 var x: u32 = 0;
3 result: while (x < 5) : (x += 1) {} else {
4 continue :result;
5 }
6}
7
8export fn entry2() void {
9 result: for (0..5) |_| {} else {
10 continue :result;
11 }
12}
13
14// error
15//
16// :4:9: error: continue outside of loop or labeled switch expression
17// :10:9: error: continue outside of loop or labeled switch expression
test/cases/compile_errors/duplicate_boolean_switch_value.zig+2
......@@ -18,4 +18,6 @@ comptime {
1818// error
1919//
2020// :5:9: error: duplicate switch value
21// :3:9: note: previous value here
2122// :13:9: error: duplicate switch value
23// :11:9: note: previous value here
test/cases/compile_errors/invalid_switch_item.zig+4-4
......@@ -36,11 +36,11 @@ export fn f3() void {
3636
3737// error
3838//
39// :8:10: error: no field named 'x' in enum 'tmp.E'
39// :8:10: error: enum 'tmp.E' has no member named 'x'
4040// :1:11: note: enum declared here
41// :16:10: error: no field named 'x' in enum 'tmp.E'
41// :16:10: error: enum 'tmp.E' has no member named 'x'
4242// :1:11: note: enum declared here
43// :24:10: error: no field named 'x' in enum 'tmp.E'
43// :24:10: error: enum 'tmp.E' has no member named 'x'
4444// :1:11: note: enum declared here
45// :32:10: error: no field named 'x' in enum 'tmp.E'
45// :32:10: error: enum 'tmp.E' has no member named 'x'
4646// :1:11: note: enum declared here
test/cases/compile_errors/labeled_block_continue.zig created+10
......@@ -0,0 +1,10 @@
1export fn foo() void {
2 const result: u32 = b: {
3 continue :b 123;
4 };
5 _ = result;
6}
7
8// error
9//
10// :3:9: error: continue outside of loop or labeled switch expression
test/cases/compile_errors/switch_loop_discarded_capture.zig created+16
......@@ -0,0 +1,16 @@
1export fn foo() void {
2 const S = struct {
3 fn doTheTest() void {
4 blk: switch (@as(u8, 'a')) {
5 '1' => |_| continue :blk '1',
6 else => {},
7 }
8 }
9 };
10 S.doTheTest();
11 comptime S.doTheTest();
12}
13
14// error
15//
16// :5:25: error: discard of capture; omit it instead
test/cases/compile_errors/switch_on_error_with_1_field_with_no_prongs.zig+23-7
......@@ -1,18 +1,34 @@
11const Error = error{M};
22
3export fn entry() void {
4 const f: Error!void = void{};
3export fn entry1() void {
4 var f: Error!void = {};
5 _ = &f;
56 if (f) {} else |e| switch (e) {}
67}
78
89export fn entry2() void {
9 const f: Error!void = void{};
10 var f: Error!void = {};
11 _ = &f;
12 f catch |e| switch (e) {};
13}
14
15export fn entry3() void {
16 const f: Error!void = error.M;
17 if (f) {} else |e| switch (e) {}
18}
19
20export fn entry4() void {
21 const f: Error!void = error.M;
1022 f catch |e| switch (e) {};
1123}
1224
1325// error
1426//
15// :5:24: error: switch must handle all possibilities
16// :5:24: note: unhandled error value: 'error.M'
17// :10:17: error: switch must handle all possibilities
18// :10:17: note: unhandled error value: 'error.M'
27// :6:24: error: switch must handle all possibilities
28// :6:24: note: unhandled error value: 'error.M'
29// :12:17: error: switch must handle all possibilities
30// :12:17: note: unhandled error value: 'error.M'
31// :17:24: error: switch must handle all possibilities
32// :17:24: note: unhandled error value: 'error.M'
33// :22:17: error: switch must handle all possibilities
34// :22:17: note: unhandled error value: 'error.M'
test/cases/compile_errors/switch_on_non_err_union.zig+1-2
......@@ -5,6 +5,5 @@ pub fn main() void {
55}
66
77// error
8// target=x86_64-linux
98//
10// :2:23: error: expected error union type, found 'bool'
9// :2:11: error: expected error union type, found 'bool'
test/cases/compile_errors/tag_capture_on_non_inline_prong.zig deleted-12
......@@ -1,12 +0,0 @@
1const E = enum { a, b, c, d };
2pub export fn entry() void {
3 var x: E = .a;
4 switch (x) {
5 .a, .b => |aorb, d| @compileLog(aorb, d),
6 inline .c, .d => |*cord| @compileLog(cord),
7 }
8}
9
10// error
11//
12// :5:26: error: tag capture on non-inline prong