authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-28 23:12:26-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-28 23:12:26-07:00
log623d5f442c832ec0ea2a07aba73b8e2eae57191c
treebea996aaf34c60b2be183754415905ee1c77c2f4
parent281a7baaeac6b6b3c8c78124f5e484f7ee101cf0

stage2: guidance on how to implement switch expressions

Here's what I think the ZIR should be. AstGen is not yet implemented to match this, and the main implementation of analyzeSwitch in Sema is not yet implemented to match it either. Here are some example byte size reductions from master branch, with the ZIR memory layout from this commit: ``` switch (foo) { a => 1, b => 2, c => 3, d => 4, } ``` 184 bytes (master) => 40 bytes (this branch) ``` switch (foo) { a, b => 1, c..d, e, f => 2, g => 3, else => 4, } ``` 240 bytes (master) => 80 bytes (this branch)

4 files changed, 226 insertions(+), 98 deletions(-)

src/AstGen.zig+15-4
...@@ -1257,6 +1257,18 @@ fn blockExprStmts(...@@ -1257,6 +1257,18 @@ fn blockExprStmts(
1257 .break_inline,1257 .break_inline,
1258 .condbr,1258 .condbr,
1259 .condbr_inline,1259 .condbr_inline,
1260 .switch_br,
1261 .switch_br_range,
1262 .switch_br_else,
1263 .switch_br_else_range,
1264 .switch_br_underscore,
1265 .switch_br_underscore_range,
1266 .switch_br_ref,
1267 .switch_br_ref_range,
1268 .switch_br_ref_else,
1269 .switch_br_ref_else_range,
1270 .switch_br_ref_underscore,
1271 .switch_br_ref_underscore_range,
1260 .compile_error,1272 .compile_error,
1261 .ret_node,1273 .ret_node,
1262 .ret_tok,1274 .ret_tok,
...@@ -2536,20 +2548,19 @@ fn switchExpr(...@@ -2536,20 +2548,19 @@ fn switchExpr(
2536 rl: ResultLoc,2548 rl: ResultLoc,
2537 switch_node: ast.Node.Index,2549 switch_node: ast.Node.Index,
2538) InnerError!zir.Inst.Ref {2550) InnerError!zir.Inst.Ref {
2539 if (true) @panic("TODO update for zir-memory-layout");
2540 const tree = parent_gz.tree();2551 const tree = parent_gz.tree();
2541 const node_datas = tree.nodes.items(.data);2552 const node_datas = tree.nodes.items(.data);
2542 const main_tokens = tree.nodes.items(.main_token);2553 const main_tokens = tree.nodes.items(.main_token);
2543 const token_tags = tree.tokens.items(.tag);2554 const token_tags = tree.tokens.items(.tag);
2544 const node_tags = tree.nodes.items(.tag);2555 const node_tags = tree.nodes.items(.tag);
25452556
2557 if (true) @panic("TODO rework for zir-memory-layout branch");
2558
2546 const switch_token = main_tokens[switch_node];2559 const switch_token = main_tokens[switch_node];
2547 const target_node = node_datas[switch_node].lhs;2560 const target_node = node_datas[switch_node].lhs;
2548 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);2561 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
2549 const case_nodes = tree.extra_data[extra.start..extra.end];2562 const case_nodes = tree.extra_data[extra.start..extra.end];
25502563
2551 const switch_src = token_starts[switch_token];
2552
2553 var block_scope: GenZir = .{2564 var block_scope: GenZir = .{
2554 .parent = scope,2565 .parent = scope,
2555 .decl = scope.ownerDecl().?,2566 .decl = scope.ownerDecl().?,
...@@ -2627,7 +2638,7 @@ fn switchExpr(...@@ -2627,7 +2638,7 @@ fn switchExpr(
2627 const msg = msg: {2638 const msg = msg: {
2628 const msg = try mod.errMsg(2639 const msg = try mod.errMsg(
2629 scope,2640 scope,
2630 switch_src,2641 parent_gz.nodeSrcLoc(switch_node),
2631 "else and '_' prong in switch expression",2642 "else and '_' prong in switch expression",
2632 .{},2643 .{},
2633 );2644 );
src/Module.zig+9
...@@ -1532,6 +1532,7 @@ pub const SrcLoc = struct {...@@ -1532,6 +1532,7 @@ pub const SrcLoc = struct {
1532 .node_offset_bin_op,1532 .node_offset_bin_op,
1533 .node_offset_bin_lhs,1533 .node_offset_bin_lhs,
1534 .node_offset_bin_rhs,1534 .node_offset_bin_rhs,
1535 .node_offset_switch_operand,
1535 => src_loc.container.decl.container.file_scope,1536 => src_loc.container.decl.container.file_scope,
1536 };1537 };
1537 }1538 }
...@@ -1663,6 +1664,7 @@ pub const SrcLoc = struct {...@@ -1663,6 +1664,7 @@ pub const SrcLoc = struct {
1663 const token_starts = tree.tokens.items(.start);1664 const token_starts = tree.tokens.items(.start);
1664 return token_starts[tok_index];1665 return token_starts[tok_index];
1665 },1666 },
1667 .node_offset_switch_operand => @panic("TODO"),
1666 }1668 }
1667 }1669 }
1668};1670};
...@@ -1795,6 +1797,11 @@ pub const LazySrcLoc = union(enum) {...@@ -1795,6 +1797,11 @@ pub const LazySrcLoc = union(enum) {
1795 /// which points to a binary expression AST node. Next, nagivate to the RHS.1797 /// which points to a binary expression AST node. Next, nagivate to the RHS.
1796 /// The Decl is determined contextually.1798 /// The Decl is determined contextually.
1797 node_offset_bin_rhs: i32,1799 node_offset_bin_rhs: i32,
1800 /// The source location points to the operand of a switch expression, found
1801 /// by taking this AST node index offset from the containing Decl AST node,
1802 /// which points to a switch expression AST node. Next, nagivate to the operand.
1803 /// The Decl is determined contextually.
1804 node_offset_switch_operand: i32,
17981805
1799 /// Upgrade to a `SrcLoc` based on the `Decl` or file in the provided scope.1806 /// Upgrade to a `SrcLoc` based on the `Decl` or file in the provided scope.
1800 pub fn toSrcLoc(lazy: LazySrcLoc, scope: *Scope) SrcLoc {1807 pub fn toSrcLoc(lazy: LazySrcLoc, scope: *Scope) SrcLoc {
...@@ -1828,6 +1835,7 @@ pub const LazySrcLoc = union(enum) {...@@ -1828,6 +1835,7 @@ pub const LazySrcLoc = union(enum) {
1828 .node_offset_bin_op,1835 .node_offset_bin_op,
1829 .node_offset_bin_lhs,1836 .node_offset_bin_lhs,
1830 .node_offset_bin_rhs,1837 .node_offset_bin_rhs,
1838 .node_offset_switch_operand,
1831 => .{1839 => .{
1832 .container = .{ .decl = scope.srcDecl().? },1840 .container = .{ .decl = scope.srcDecl().? },
1833 .lazy = lazy,1841 .lazy = lazy,
...@@ -1867,6 +1875,7 @@ pub const LazySrcLoc = union(enum) {...@@ -1867,6 +1875,7 @@ pub const LazySrcLoc = union(enum) {
1867 .node_offset_bin_op,1875 .node_offset_bin_op,
1868 .node_offset_bin_lhs,1876 .node_offset_bin_lhs,
1869 .node_offset_bin_rhs,1877 .node_offset_bin_rhs,
1878 .node_offset_switch_operand,
1870 => .{1879 => .{
1871 .container = .{ .decl = decl },1880 .container = .{ .decl = decl },
1872 .lazy = lazy,1881 .lazy = lazy,
src/Sema.zig+89-53
...@@ -229,10 +229,6 @@ pub fn analyzeBody(...@@ -229,10 +229,6 @@ pub fn analyzeBody(
229 .typeof => try sema.zirTypeof(block, inst),229 .typeof => try sema.zirTypeof(block, inst),
230 .typeof_peer => try sema.zirTypeofPeer(block, inst),230 .typeof_peer => try sema.zirTypeofPeer(block, inst),
231 .xor => try sema.zirBitwise(block, inst, .xor),231 .xor => try sema.zirBitwise(block, inst, .xor),
232 // TODO
233 //.switchbr => try sema.zirSwitchBr(block, inst, false),
234 //.switchbr_ref => try sema.zirSwitchBr(block, inst, true),
235 //.switch_range => try sema.zirSwitchRange(block, inst),
236232
237 // Instructions that we know to *always* be noreturn based solely on their tag.233 // Instructions that we know to *always* be noreturn based solely on their tag.
238 // These functions match the return type of analyzeBody so that we can234 // These functions match the return type of analyzeBody so that we can
...@@ -246,6 +242,18 @@ pub fn analyzeBody(...@@ -246,6 +242,18 @@ pub fn analyzeBody(
246 .ret_tok => return sema.zirRetTok(block, inst, false),242 .ret_tok => return sema.zirRetTok(block, inst, false),
247 .@"unreachable" => return sema.zirUnreachable(block, inst),243 .@"unreachable" => return sema.zirUnreachable(block, inst),
248 .repeat => return sema.zirRepeat(block, inst),244 .repeat => return sema.zirRepeat(block, inst),
245 .switch_br => return sema.zirSwitchBr(block, inst, false, .full),
246 .switch_br_range => return sema.zirSwitchBrRange(block, inst, false, .full),
247 .switch_br_else => return sema.zirSwitchBr(block, inst, false, .@"else"),
248 .switch_br_else_range => return sema.zirSwitchBrRange(block, inst, false, .@"else"),
249 .switch_br_underscore => return sema.zirSwitchBr(block, inst, false, .under),
250 .switch_br_underscore_range => return sema.zirSwitchBrRange(block, inst, false, .under),
251 .switch_br_ref => return sema.zirSwitchBr(block, inst, true, .full),
252 .switch_br_ref_range => return sema.zirSwitchBrRange(block, inst, true, .full),
253 .switch_br_ref_else => return sema.zirSwitchBr(block, inst, true, .@"else"),
254 .switch_br_ref_else_range => return sema.zirSwitchBrRange(block, inst, true, .@"else"),
255 .switch_br_ref_underscore => return sema.zirSwitchBr(block, inst, true, .under),
256 .switch_br_ref_underscore_range => return sema.zirSwitchBrRange(block, inst, true, .under),
249257
250 // Instructions that we know can *never* be noreturn based solely on258 // Instructions that we know can *never* be noreturn based solely on
251 // their tag. We avoid needlessly checking if they are noreturn and259 // their tag. We avoid needlessly checking if they are noreturn and
...@@ -2197,54 +2205,82 @@ fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inne...@@ -2197,54 +2205,82 @@ fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inne
2197 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);2205 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);
2198}2206}
21992207
2200fn zirSwitchRange(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {2208const ElseProng = enum { full, @"else", under };
2209
2210fn zirSwitchBr(
2211 sema: *Sema,
2212 block: *Scope.Block,
2213 inst: zir.Inst.Index,
2214 is_ref: bool,
2215 else_prong: ElseProng,
2216) InnerError!zir.Inst.Index {
2201 const tracy = trace(@src());2217 const tracy = trace(@src());
2202 defer tracy.end();2218 defer tracy.end();
22032219
2204 const src: LazySrcLoc = .todo;2220 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2205 const bin_inst = sema.code.instructions.items(.data)[inst].bin;2221 const src = inst_data.src();
2206 const start = try sema.resolveInst(bin_inst.lhs);2222 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
2207 const end = try sema.resolveInst(bin_inst.rhs);2223 const extra = sema.code.extraData(zir.Inst.SwitchBr, inst_data.payload_index);
22082224
2209 switch (start.ty.zigTypeTag()) {2225 const operand_ptr = try sema.resolveInst(extra.data.operand);
2210 .Int, .ComptimeInt => {},2226 const operand = if (is_ref)
2211 else => return sema.mod.constVoid(sema.arena, .unneeded),2227 try sema.analyzeLoad(block, src, operand_ptr, operand_src)
2212 }2228 else
2213 switch (end.ty.zigTypeTag()) {2229 operand_ptr;
2214 .Int, .ComptimeInt => {},2230
2215 else => return sema.mod.constVoid(sema.arena, .unneeded),2231 return sema.analyzeSwitch(block, operand, extra.end, else_prong, extra.data.cases_len, 0, 0);
2216 }
2217 // .switch_range must be inside a comptime scope
2218 const start_val = start.value().?;
2219 const end_val = end.value().?;
2220 if (start_val.compare(.gte, end_val)) {
2221 return sema.mod.fail(&block.base, src, "range start value must be smaller than the end value", .{});
2222 }
2223 return sema.mod.constVoid(sema.arena, .unneeded);
2224}2232}
22252233
2226fn zirSwitchBr(2234fn zirSwitchBrRange(
2227 sema: *Sema,2235 sema: *Sema,
2228 parent_block: *Scope.Block,2236 block: *Scope.Block,
2229 inst: zir.Inst.Index,2237 inst: zir.Inst.Index,
2230 ref: bool,2238 is_ref: bool,
2231) InnerError!zir.Inst.Ref {2239 else_prong: ElseProng,
2240) InnerError!zir.Inst.Index {
2232 const tracy = trace(@src());2241 const tracy = trace(@src());
2233 defer tracy.end();2242 defer tracy.end();
22342243
2235 if (true) @panic("TODO rework with zir-memory-layout in mind");2244 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
2245 const src = inst_data.src();
2246 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
2247 const extra = sema.code.extraData(zir.Inst.SwitchBrRange, inst_data.payload_index);
22362248
2237 const target_ptr = try sema.resolveInst(inst.positionals.target);2249 const operand_ptr = try sema.resolveInst(extra.data.operand);
2238 const target = if (ref)2250 const operand = if (is_ref)
2239 try sema.analyzeLoad(parent_block, inst.base.src, target_ptr, inst.positionals.target.src)2251 try sema.analyzeLoad(block, src, operand_ptr, operand_src)
2240 else2252 else
2241 target_ptr;2253 operand_ptr;
2242 try sema.validateSwitch(parent_block, target, inst);2254
2255 return sema.analyzeSwitch(
2256 block,
2257 operand,
2258 extra.end,
2259 else_prong,
2260 extra.data.scalar_cases_len,
2261 extra.data.multi_cases_len,
2262 extra.data.range_cases_len,
2263 );
2264}
22432265
2244 if (try sema.resolveDefinedValue(parent_block, inst.base.src, target)) |target_val| {2266fn analyzeSwitch(
2267 sema: *Sema,
2268 parent_block: *Scope.Block,
2269 operand: *Inst,
2270 extra_end: usize,
2271 else_prong: ElseProng,
2272 scalar_cases_len: usize,
2273 multi_cases_len: usize,
2274 range_cases_len: usize,
2275) InnerError!zir.Inst.Index {
2276 if (true) @panic("TODO rework for zir-memory-layout branch");
2277
2278 try sema.validateSwitch(parent_block, operand, inst);
2279
2280 if (try sema.resolveDefinedValue(parent_block, inst.base.src, operand)) |target_val| {
2245 for (inst.positionals.cases) |case| {2281 for (inst.positionals.cases) |case| {
2246 const resolved = try sema.resolveInst(case.item);2282 const resolved = try sema.resolveInst(case.item);
2247 const casted = try sema.coerce(block, target.ty, resolved, resolved_src);2283 const casted = try sema.coerce(block, operand.ty, resolved, resolved_src);
2248 const item = try sema.resolveConstValue(parent_block, case_src, casted);2284 const item = try sema.resolveConstValue(parent_block, case_src, casted);
22492285
2250 if (target_val.eql(item)) {2286 if (target_val.eql(item)) {
...@@ -2280,7 +2316,7 @@ fn zirSwitchBr(...@@ -2280,7 +2316,7 @@ fn zirSwitchBr(
2280 case_block.instructions.items.len = 0;2316 case_block.instructions.items.len = 0;
22812317
2282 const resolved = try sema.resolveInst(case.item);2318 const resolved = try sema.resolveInst(case.item);
2283 const casted = try sema.coerce(block, target.ty, resolved, resolved_src);2319 const casted = try sema.coerce(block, operand.ty, resolved, resolved_src);
2284 const item = try sema.resolveConstValue(parent_block, case_src, casted);2320 const item = try sema.resolveConstValue(parent_block, case_src, casted);
22852321
2286 _ = try sema.analyzeBody(&case_block, case.body);2322 _ = try sema.analyzeBody(&case_block, case.body);
...@@ -2298,29 +2334,29 @@ fn zirSwitchBr(...@@ -2298,29 +2334,29 @@ fn zirSwitchBr(
2298 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),2334 .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items),
2299 };2335 };
23002336
2301 return mod.addSwitchBr(parent_block, inst.base.src, target, cases, else_body);2337 return mod.addSwitchBr(parent_block, inst.base.src, operand, cases, else_body);
2302}2338}
23032339
2304fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Inst.Index) InnerError!void {2340fn validateSwitch(sema: *Sema, block: *Scope.Block, operand: *Inst, inst: zir.Inst.Index) InnerError!void {
2305 // validate usage of '_' prongs2341 // validate usage of '_' prongs
2306 if (inst.positionals.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) {2342 if (inst.positionals.special_prong == .underscore and operand.ty.zigTypeTag() != .Enum) {
2307 return sema.mod.fail(&block.base, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{});2343 return sema.mod.fail(&block.base, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{});
2308 // TODO notes "'_' prong here" inst.positionals.cases[last].src2344 // TODO notes "'_' prong here" inst.positionals.cases[last].src
2309 }2345 }
23102346
2311 // check that target type supports ranges2347 // check that operand type supports ranges
2312 if (inst.positionals.range) |range_inst| {2348 if (inst.positionals.range) |range_inst| {
2313 switch (target.ty.zigTypeTag()) {2349 switch (operand.ty.zigTypeTag()) {
2314 .Int, .ComptimeInt => {},2350 .Int, .ComptimeInt => {},
2315 else => {2351 else => {
2316 return sema.mod.fail(&block.base, target.src, "ranges not allowed when switching on type {}", .{target.ty});2352 return sema.mod.fail(&block.base, operand.src, "ranges not allowed when switching on type {}", .{operand.ty});
2317 // TODO notes "range used here" range_inst.src2353 // TODO notes "range used here" range_inst.src
2318 },2354 },
2319 }2355 }
2320 }2356 }
23212357
2322 // validate for duplicate items/missing else prong2358 // validate for duplicate items/missing else prong
2323 switch (target.ty.zigTypeTag()) {2359 switch (operand.ty.zigTypeTag()) {
2324 .Enum => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Enum", .{}),2360 .Enum => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Enum", .{}),
2325 .ErrorSet => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .ErrorSet", .{}),2361 .ErrorSet => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .ErrorSet", .{}),
2326 .Union => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Union", .{}),2362 .Union => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Union", .{}),
...@@ -2331,9 +2367,9 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins...@@ -2331,9 +2367,9 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins
2331 for (inst.positionals.items) |item| {2367 for (inst.positionals.items) |item| {
2332 const maybe_src = if (item.castTag(.switch_range)) |range| blk: {2368 const maybe_src = if (item.castTag(.switch_range)) |range| blk: {
2333 const start_resolved = try sema.resolveInst(range.positionals.lhs);2369 const start_resolved = try sema.resolveInst(range.positionals.lhs);
2334 const start_casted = try sema.coerce(block, target.ty, start_resolved);2370 const start_casted = try sema.coerce(block, operand.ty, start_resolved);
2335 const end_resolved = try sema.resolveInst(range.positionals.rhs);2371 const end_resolved = try sema.resolveInst(range.positionals.rhs);
2336 const end_casted = try sema.coerce(block, target.ty, end_resolved);2372 const end_casted = try sema.coerce(block, operand.ty, end_resolved);
23372373
2338 break :blk try range_set.add(2374 break :blk try range_set.add(
2339 try sema.resolveConstValue(block, range_start_src, start_casted),2375 try sema.resolveConstValue(block, range_start_src, start_casted),
...@@ -2342,7 +2378,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins...@@ -2342,7 +2378,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins
2342 );2378 );
2343 } else blk: {2379 } else blk: {
2344 const resolved = try sema.resolveInst(item);2380 const resolved = try sema.resolveInst(item);
2345 const casted = try sema.coerce(block, target.ty, resolved);2381 const casted = try sema.coerce(block, operand.ty, resolved);
2346 const value = try sema.resolveConstValue(block, item_src, casted);2382 const value = try sema.resolveConstValue(block, item_src, casted);
2347 break :blk try range_set.add(value, value, item.src);2383 break :blk try range_set.add(value, value, item.src);
2348 };2384 };
...@@ -2353,12 +2389,12 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins...@@ -2353,12 +2389,12 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins
2353 }2389 }
2354 }2390 }
23552391
2356 if (target.ty.zigTypeTag() == .Int) {2392 if (operand.ty.zigTypeTag() == .Int) {
2357 var arena = std.heap.ArenaAllocator.init(sema.gpa);2393 var arena = std.heap.ArenaAllocator.init(sema.gpa);
2358 defer arena.deinit();2394 defer arena.deinit();
23592395
2360 const start = try target.ty.minInt(&arena, mod.getTarget());2396 const start = try operand.ty.minInt(&arena, mod.getTarget());
2361 const end = try target.ty.maxInt(&arena, mod.getTarget());2397 const end = try operand.ty.maxInt(&arena, mod.getTarget());
2362 if (try range_set.spans(start, end)) {2398 if (try range_set.spans(start, end)) {
2363 if (inst.positionals.special_prong == .@"else") {2399 if (inst.positionals.special_prong == .@"else") {
2364 return sema.mod.fail(&block.base, inst.base.src, "unreachable else prong, all cases already handled", .{});2400 return sema.mod.fail(&block.base, inst.base.src, "unreachable else prong, all cases already handled", .{});
...@@ -2396,7 +2432,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins...@@ -2396,7 +2432,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins
2396 },2432 },
2397 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {2433 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {
2398 if (inst.positionals.special_prong != .@"else") {2434 if (inst.positionals.special_prong != .@"else") {
2399 return sema.mod.fail(&block.base, inst.base.src, "else prong required when switching on type '{}'", .{target.ty});2435 return sema.mod.fail(&block.base, inst.base.src, "else prong required when switching on type '{}'", .{operand.ty});
2400 }2436 }
24012437
2402 var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(sema.gpa);2438 var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(sema.gpa);
...@@ -2404,7 +2440,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins...@@ -2404,7 +2440,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins
24042440
2405 for (inst.positionals.items) |item| {2441 for (inst.positionals.items) |item| {
2406 const resolved = try sema.resolveInst(item);2442 const resolved = try sema.resolveInst(item);
2407 const casted = try sema.coerce(block, target.ty, resolved);2443 const casted = try sema.coerce(block, operand.ty, resolved);
2408 const val = try sema.resolveConstValue(block, item_src, casted);2444 const val = try sema.resolveConstValue(block, item_src, casted);
24092445
2410 if (try seen_values.fetchPut(val, item.src)) |prev| {2446 if (try seen_values.fetchPut(val, item.src)) |prev| {
...@@ -2429,7 +2465,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins...@@ -2429,7 +2465,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins
2429 .ComptimeFloat,2465 .ComptimeFloat,
2430 .Float,2466 .Float,
2431 => {2467 => {
2432 return sema.mod.fail(&block.base, target.src, "invalid switch target type '{}'", .{target.ty});2468 return sema.mod.fail(&block.base, operand.src, "invalid switch operand type '{}'", .{operand.ty});
2433 },2469 },
2434 }2470 }
2435}2471}
src/zir.zig+113-41
...@@ -585,39 +585,35 @@ pub const Inst = struct {...@@ -585,39 +585,35 @@ pub const Inst = struct {
585 /// An enum literal 8 or fewer bytes. No source location.585 /// An enum literal 8 or fewer bytes. No source location.
586 /// Uses the `small_str` field.586 /// Uses the `small_str` field.
587 enum_literal_small,587 enum_literal_small,
588 // /// A switch expression.588 /// A switch expression. Uses the `pl_node` union field.
589 // /// lhs is target, SwitchBr[rhs]589 /// AST node is the switch, payload is `SwitchBr`.
590 // /// All prongs of target handled.590 /// All prongs of target handled.
591 // switch_br,591 switch_br,
592 // /// Same as switch_br, except has a range field.592 /// Same as switch_br, except has a range field.
593 // switch_br_range,593 switch_br_range,
594 // /// Same as switch_br, except has an else prong.594 /// Same as switch_br, except has an else prong.
595 // switch_br_else,595 switch_br_else,
596 // /// Same as switch_br_else, except has a range field.596 /// Same as switch_br_else, except has a range field.
597 // switch_br_else_range,597 switch_br_else_range,
598 // /// Same as switch_br, except has an underscore prong.598 /// Same as switch_br, except has an underscore prong.
599 // switch_br_underscore,599 switch_br_underscore,
600 // /// Same as switch_br, except has a range field.600 /// Same as switch_br, except has a range field.
601 // switch_br_underscore_range,601 switch_br_underscore_range,
602 // /// Same as `switch_br` but the target is a pointer to the value being switched on.602 /// Same as `switch_br` but the target is a pointer to the value being switched on.
603 // switch_br_ref,603 switch_br_ref,
604 // /// Same as `switch_br_range` but the target is a pointer to the value being switched on.604 /// Same as `switch_br_range` but the target is a pointer to the value being switched on.
605 // switch_br_ref_range,605 switch_br_ref_range,
606 // /// Same as `switch_br_else` but the target is a pointer to the value being switched on.606 /// Same as `switch_br_else` but the target is a pointer to the value being switched on.
607 // switch_br_ref_else,607 switch_br_ref_else,
608 // /// Same as `switch_br_else_range` but the target is a pointer to the608 /// Same as `switch_br_else_range` but the target is a pointer to the
609 // /// value being switched on.609 /// value being switched on.
610 // switch_br_ref_else_range,610 switch_br_ref_else_range,
611 // /// Same as `switch_br_underscore` but the target is a pointer to the value611 /// Same as `switch_br_underscore` but the target is a pointer to the value
612 // /// being switched on.612 /// being switched on.
613 // switch_br_ref_underscore,613 switch_br_ref_underscore,
614 // /// Same as `switch_br_underscore_range` but the target is a pointer to614 /// Same as `switch_br_underscore_range` but the target is a pointer to
615 // /// the value being switched on.615 /// the value being switched on.
616 // switch_br_ref_underscore_range,616 switch_br_ref_underscore_range,
617 // /// A range in a switch case, `lhs...rhs`.
618 // /// Only checks that `lhs >= rhs` if they are ints, everything else is
619 // /// validated by the switch_br instruction.
620 // switch_range,
621617
622 /// Returns whether the instruction is one of the control flow "noreturn" types.618 /// Returns whether the instruction is one of the control flow "noreturn" types.
623 /// Function calls do not count.619 /// Function calls do not count.
...@@ -760,6 +756,18 @@ pub const Inst = struct {...@@ -760,6 +756,18 @@ pub const Inst = struct {
760 .@"unreachable",756 .@"unreachable",
761 .repeat,757 .repeat,
762 .repeat_inline,758 .repeat_inline,
759 .switch_br,
760 .switch_br_range,
761 .switch_br_else,
762 .switch_br_else_range,
763 .switch_br_underscore,
764 .switch_br_underscore_range,
765 .switch_br_ref,
766 .switch_br_ref_range,
767 .switch_br_ref_else,
768 .switch_br_ref_else_range,
769 .switch_br_ref_underscore,
770 .switch_br_ref_underscore_range,
763 => true,771 => true,
764 };772 };
765 }773 }
...@@ -1322,22 +1330,53 @@ pub const Inst = struct {...@@ -1322,22 +1330,53 @@ pub const Inst = struct {
1322 rhs: Ref,1330 rhs: Ref,
1323 };1331 };
13241332
1325 /// Stored in extra. Depending on zir tag and len fields, extra fields trail1333 /// This form is supported when there are no ranges, and exactly 1 item per block.
1334 /// Depending on zir tag and len fields, extra fields trail
1326 /// this one in the extra array.1335 /// this one in the extra array.
1327 /// 0. range: Ref // If the tag has "_range" in it.1336 /// 0. else_body { // If the tag has "_else" or "_underscore" in it.
1328 /// 1. else_body: Ref // If the tag has "_else" or "_underscore" in it.1337 /// body_len: u32,
1329 /// 2. items: list of all individual items and ranges.1338 /// body member Index for every body_len
1330 /// 3. cases: {1339 /// }
1340 /// 1. cases: {
1331 /// item: Ref,1341 /// item: Ref,
1332 /// body_len: u32,1342 /// body_len: u32,
1333 /// body member Ref for every body_len1343 /// body member Index for every body_len
1334 /// } for every cases_len1344 /// } for every cases_len
1335 pub const SwitchBr = struct {1345 pub const SwitchBr = struct {
1336 /// TODO investigate, why do we need to store this? is it redundant?1346 operand: Ref,
1337 items_len: u32,
1338 cases_len: u32,1347 cases_len: u32,
1339 };1348 };
13401349
1350 /// This form is required when there exists a block which has more than one item,
1351 /// or a range.
1352 /// Depending on zir tag and len fields, extra fields trail
1353 /// this one in the extra array.
1354 /// 0. else_body { // If the tag has "_else" or "_underscore" in it.
1355 /// body_len: u32,
1356 /// body member Index for every body_len
1357 /// }
1358 /// 1. scalar_cases: { // for every scalar_cases_len
1359 /// item: Ref,
1360 /// body_len: u32,
1361 /// body member Index for every body_len
1362 /// }
1363 /// 2. multi_cases: { // for every multi_cases_len
1364 /// items_len: u32,
1365 /// item: Ref for every items_len
1366 /// block_index: u32, // index in extra to a `Block`
1367 /// }
1368 /// 3. range_cases: { // for every range_cases_len
1369 /// item_start: Ref,
1370 /// item_end: Ref,
1371 /// block_index: u32, // index in extra to a `Block`
1372 /// }
1373 pub const SwitchBrRange = struct {
1374 operand: Ref,
1375 scalar_cases_len: u32,
1376 multi_cases_len: u32,
1377 range_cases_len: u32,
1378 };
1379
1341 pub const Field = struct {1380 pub const Field = struct {
1342 lhs: Ref,1381 lhs: Ref,
1343 /// Offset into `string_bytes`.1382 /// Offset into `string_bytes`.
...@@ -1503,6 +1542,22 @@ const Writer = struct {...@@ -1503,6 +1542,22 @@ const Writer = struct {
1503 .condbr_inline,1542 .condbr_inline,
1504 => try self.writePlNodeCondBr(stream, inst),1543 => try self.writePlNodeCondBr(stream, inst),
15051544
1545 .switch_br,
1546 .switch_br_else,
1547 .switch_br_underscore,
1548 .switch_br_ref,
1549 .switch_br_ref_else,
1550 .switch_br_ref_underscore,
1551 => try self.writePlNodeSwitchBr(stream, inst),
1552
1553 .switch_br_range,
1554 .switch_br_else_range,
1555 .switch_br_underscore_range,
1556 .switch_br_ref_range,
1557 .switch_br_ref_else_range,
1558 .switch_br_ref_underscore_range,
1559 => try self.writePlNodeSwitchBrRange(stream, inst),
1560
1506 .compile_log,1561 .compile_log,
1507 .typeof_peer,1562 .typeof_peer,
1508 => try self.writePlNodeMultiOp(stream, inst),1563 => try self.writePlNodeMultiOp(stream, inst),
...@@ -1708,6 +1763,23 @@ const Writer = struct {...@@ -1708,6 +1763,23 @@ const Writer = struct {
1708 try self.writeSrc(stream, inst_data.src());1763 try self.writeSrc(stream, inst_data.src());
1709 }1764 }
17101765
1766 fn writePlNodeSwitchBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1767 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1768 const extra = self.code.extraData(Inst.SwitchBr, inst_data.payload_index);
1769
1770 try self.writeInstRef(stream, extra.data.operand);
1771 try stream.writeAll(", TODO) ");
1772 try self.writeSrc(stream, inst_data.src());
1773 }
1774
1775 fn writePlNodeSwitchBrRange(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1776 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1777 const extra = self.code.extraData(Inst.SwitchBrRange, inst_data.payload_index);
1778 try self.writeInstRef(stream, extra.data.operand);
1779 try stream.writeAll(", TODO) ");
1780 try self.writeSrc(stream, inst_data.src());
1781 }
1782
1711 fn writePlNodeMultiOp(self: *Writer, stream: anytype, inst: Inst.Index) !void {1783 fn writePlNodeMultiOp(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1712 const inst_data = self.code.instructions.items(.data)[inst].pl_node;1784 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1713 const extra = self.code.extraData(Inst.MultiOp, inst_data.payload_index);1785 const extra = self.code.extraData(Inst.MultiOp, inst_data.payload_index);