| author | |
| committer | |
| log | 769d5a9c435c5c145983e4d3af1706924248e367 |
| tree | 1aa427c706261ccf5256e8699be331601821251e |
| parent | 12e4c648ccc68f5190dd5076088b3959ebeee65d |
| signature |
5 files changed, 82 insertions(+), 39 deletions(-)
src/Module.zig+3-1| ... | ... | @@ -2122,16 +2122,18 @@ pub fn addSwitchBr( |
| 2122 | 2122 | src: usize, |
| 2123 | 2123 | target_ptr: *Inst, |
| 2124 | 2124 | cases: []Inst.SwitchBr.Case, |
| 2125 | else_body: ir.Body, | |
| 2125 | 2126 | ) !*Inst { |
| 2126 | 2127 | const inst = try block.arena.create(Inst.SwitchBr); |
| 2127 | 2128 | inst.* = .{ |
| 2128 | 2129 | .base = .{ |
| 2129 | 2130 | .tag = .switchbr, |
| 2130 | .ty = Type.initTag(.void), | |
| 2131 | .ty = Type.initTag(.noreturn), | |
| 2131 | 2132 | .src = src, |
| 2132 | 2133 | }, |
| 2133 | 2134 | .target_ptr = target_ptr, |
| 2134 | 2135 | .cases = cases, |
| 2136 | .else_body = else_body, | |
| 2135 | 2137 | }; |
| 2136 | 2138 | try block.instructions.append(self.gpa, &inst.base); |
| 2137 | 2139 | return &inst.base; |
src/astgen.zig+35-28| ... | ... | @@ -1581,14 +1581,6 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 1581 | 1581 | }; |
| 1582 | 1582 | defer block_scope.instructions.deinit(mod.gpa); |
| 1583 | 1583 | |
| 1584 | var item_scope: Scope.GenZIR = .{ | |
| 1585 | .parent = scope, | |
| 1586 | .decl = scope.decl().?, | |
| 1587 | .arena = scope.arena(), | |
| 1588 | .instructions = .{}, | |
| 1589 | }; | |
| 1590 | defer item_scope.instructions.deinit(mod.gpa); | |
| 1591 | ||
| 1592 | 1584 | const tree = scope.tree(); |
| 1593 | 1585 | const switch_src = tree.token_locs[switch_node.switch_token].start; |
| 1594 | 1586 | const target_ptr = try expr(mod, &block_scope.base, .ref, switch_node.expr); |
| ... | ... | @@ -1598,6 +1590,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 1598 | 1590 | .target_ptr = target_ptr, |
| 1599 | 1591 | .cases = undefined, // populated below |
| 1600 | 1592 | .items = &[_]*zir.Inst{}, // populated below |
| 1593 | .else_body = undefined, // populated below | |
| 1601 | 1594 | }, .{})).castTag(.switchbr).?; |
| 1602 | 1595 | |
| 1603 | 1596 | var items = std.ArrayList(*zir.Inst).init(mod.gpa); |
| ... | ... | @@ -1611,7 +1604,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 1611 | 1604 | }); |
| 1612 | 1605 | // then add block containing the switch. |
| 1613 | 1606 | const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{ |
| 1614 | .instructions = undefined, // populated below | |
| 1607 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), | |
| 1615 | 1608 | }); |
| 1616 | 1609 | |
| 1617 | 1610 | // Most result location types can be forwarded directly; however |
| ... | ... | @@ -1622,6 +1615,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 1622 | 1615 | .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block }, |
| 1623 | 1616 | }; |
| 1624 | 1617 | |
| 1618 | var item_scope: Scope.GenZIR = .{ | |
| 1619 | .parent = scope, | |
| 1620 | .decl = scope.decl().?, | |
| 1621 | .arena = scope.arena(), | |
| 1622 | .instructions = .{}, | |
| 1623 | }; | |
| 1624 | defer item_scope.instructions.deinit(mod.gpa); | |
| 1625 | ||
| 1625 | 1626 | var case_scope: Scope.GenZIR = .{ |
| 1626 | 1627 | .parent = scope, |
| 1627 | 1628 | .decl = block_scope.decl, |
| ... | ... | @@ -1630,6 +1631,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 1630 | 1631 | }; |
| 1631 | 1632 | defer case_scope.instructions.deinit(mod.gpa); |
| 1632 | 1633 | |
| 1634 | var else_scope: Scope.GenZIR = .{ | |
| 1635 | .parent = scope, | |
| 1636 | .decl = block_scope.decl, | |
| 1637 | .arena = block_scope.arena, | |
| 1638 | .instructions = .{}, | |
| 1639 | }; | |
| 1640 | defer else_scope.instructions.deinit(mod.gpa); | |
| 1641 | ||
| 1633 | 1642 | // first we gather all the switch items and check else/'_' prongs |
| 1634 | 1643 | var else_src: ?usize = null; |
| 1635 | 1644 | var underscore_src: ?usize = null; |
| ... | ... | @@ -1701,12 +1710,12 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 1701 | 1710 | if (first_range == null) first_range = range_inst; |
| 1702 | 1711 | |
| 1703 | 1712 | // target >= start and target <= end |
| 1704 | const range_start_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .cmp_gte, target, start); | |
| 1705 | const range_end_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .cmp_lte, target, end); | |
| 1706 | const range_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .booland, range_start_ok, range_end_ok); | |
| 1713 | const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, start); | |
| 1714 | const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, end); | |
| 1715 | const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .booland, range_start_ok, range_end_ok); | |
| 1707 | 1716 | |
| 1708 | 1717 | if (any_ok) |some| { |
| 1709 | any_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .boolor, some, range_ok); | |
| 1718 | any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .boolor, some, range_ok); | |
| 1710 | 1719 | } else { |
| 1711 | 1720 | any_ok = range_ok; |
| 1712 | 1721 | } |
| ... | ... | @@ -1715,16 +1724,16 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 1715 | 1724 | |
| 1716 | 1725 | const item_inst = try expr(mod, &item_scope.base, .none, item); |
| 1717 | 1726 | try items.append(item_inst); |
| 1718 | const cpm_ok = try addZIRBinOp(mod, &block_scope.base, item_inst.src, .cmp_eq, target, item_inst); | |
| 1727 | const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst); | |
| 1719 | 1728 | |
| 1720 | 1729 | if (any_ok) |some| { |
| 1721 | any_ok = try addZIRBinOp(mod, &block_scope.base, item_inst.src, .boolor, some, cpm_ok); | |
| 1730 | any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .boolor, some, cpm_ok); | |
| 1722 | 1731 | } else { |
| 1723 | 1732 | any_ok = cpm_ok; |
| 1724 | 1733 | } |
| 1725 | 1734 | } |
| 1726 | 1735 | |
| 1727 | const condbr = try addZIRInstSpecial(mod, &block_scope.base, case_src, zir.Inst.CondBr, .{ | |
| 1736 | const condbr = try addZIRInstSpecial(mod, &else_scope.base, case_src, zir.Inst.CondBr, .{ | |
| 1728 | 1737 | .condition = any_ok.?, |
| 1729 | 1738 | .then_body = undefined, // populated below |
| 1730 | 1739 | .else_body = undefined, // populated below |
| ... | ... | @@ -1754,6 +1763,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 1754 | 1763 | }; |
| 1755 | 1764 | } |
| 1756 | 1765 | |
| 1766 | // Generate else block or a break last to finish the block. | |
| 1767 | if (special_case) |case| { | |
| 1768 | try switchCaseExpr(mod, &else_scope.base, case_rl, block, case); | |
| 1769 | } else { | |
| 1770 | // Not handling all possible cases is a compile error. | |
| 1771 | _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreach_nocheck); | |
| 1772 | } | |
| 1773 | ||
| 1757 | 1774 | // All items have been generated, add the instructions to the comptime block. |
| 1758 | 1775 | item_block.positionals.body = .{ |
| 1759 | 1776 | .instructions = try block_scope.arena.dupe(*zir.Inst, item_scope.instructions.items), |
| ... | ... | @@ -1765,18 +1782,8 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node |
| 1765 | 1782 | switch_inst.positionals.cases = try block_scope.arena.dupe(zir.Inst.SwitchBr.Case, cases.items); |
| 1766 | 1783 | switch_inst.positionals.items = try block_scope.arena.dupe(*zir.Inst, items.items); |
| 1767 | 1784 | switch_inst.kw_args.range = first_range; |
| 1768 | ||
| 1769 | // Generate else block or a break last to finish the block. | |
| 1770 | if (special_case) |case| { | |
| 1771 | try switchCaseExpr(mod, &block_scope.base, case_rl, block, case); | |
| 1772 | } else { | |
| 1773 | // Not handling all possible cases is a compile error. | |
| 1774 | _ = try addZIRNoOp(mod, &block_scope.base, switch_src, .unreach_nocheck); | |
| 1775 | } | |
| 1776 | ||
| 1777 | // Set block instructions now that it is finished. | |
| 1778 | block.positionals.body = .{ | |
| 1779 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), | |
| 1785 | switch_inst.positionals.else_body = .{ | |
| 1786 | .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items), | |
| 1780 | 1787 | }; |
| 1781 | 1788 | return &block.base; |
| 1782 | 1789 | } |
src/ir.zig+7-1| ... | ... | @@ -472,8 +472,11 @@ pub const Inst = struct { |
| 472 | 472 | target_ptr: *Inst, |
| 473 | 473 | cases: []Case, |
| 474 | 474 | /// Set of instructions whose lifetimes end at the start of one of the cases. |
| 475 | /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... , case_n_count ... else_count]. | |
| 475 | /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ]. | |
| 476 | 476 | deaths: [*]*Inst = undefined, |
| 477 | else_index: u32 = 0, | |
| 478 | else_deaths: u32 = 0, | |
| 479 | else_body: Body, | |
| 477 | 480 | |
| 478 | 481 | pub const Case = struct { |
| 479 | 482 | item: Value, |
| ... | ... | @@ -498,6 +501,9 @@ pub const Inst = struct { |
| 498 | 501 | const case = self.cases[case_index]; |
| 499 | 502 | return (self.deaths + case.index)[0..case.deaths]; |
| 500 | 503 | } |
| 504 | pub fn elseDeaths(self: *const SwitchBr) []*Inst { | |
| 505 | return (self.deaths + self.else_index)[0..self.else_deaths]; | |
| 506 | } | |
| 501 | 507 | }; |
| 502 | 508 | }; |
| 503 | 509 |
src/zir.zig+10-2| ... | ... | @@ -509,7 +509,6 @@ pub const Inst = struct { |
| 509 | 509 | .slice, |
| 510 | 510 | .slice_start, |
| 511 | 511 | .import, |
| 512 | .switchbr, | |
| 513 | 512 | .switch_range, |
| 514 | 513 | => false, |
| 515 | 514 | |
| ... | ... | @@ -522,6 +521,7 @@ pub const Inst = struct { |
| 522 | 521 | .unreach_nocheck, |
| 523 | 522 | .@"unreachable", |
| 524 | 523 | .loop, |
| 524 | .switchbr, | |
| 525 | 525 | => true, |
| 526 | 526 | }; |
| 527 | 527 | } |
| ... | ... | @@ -1012,9 +1012,10 @@ pub const Inst = struct { |
| 1012 | 1012 | |
| 1013 | 1013 | positionals: struct { |
| 1014 | 1014 | target_ptr: *Inst, |
| 1015 | cases: []Case, | |
| 1016 | 1015 | /// List of all individual items and ranges |
| 1017 | 1016 | items: []*Inst, |
| 1017 | cases: []Case, | |
| 1018 | else_body: Module.Body, | |
| 1018 | 1019 | }, |
| 1019 | 1020 | kw_args: struct { |
| 1020 | 1021 | /// Pointer to first range if such exists. |
| ... | ... | @@ -2569,6 +2570,7 @@ const EmitZIR = struct { |
| 2569 | 2570 | .target_ptr = try self.resolveInst(new_body, old_inst.target_ptr), |
| 2570 | 2571 | .cases = cases, |
| 2571 | 2572 | .items = &[_]*Inst{}, // TODO this should actually be populated |
| 2573 | .else_body = undefined, // populated below | |
| 2572 | 2574 | }, |
| 2573 | 2575 | .kw_args = .{}, |
| 2574 | 2576 | }; |
| ... | ... | @@ -2590,6 +2592,12 @@ const EmitZIR = struct { |
| 2590 | 2592 | .body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) }, |
| 2591 | 2593 | }; |
| 2592 | 2594 | } |
| 2595 | ||
| 2596 | body_tmp.items.len = 0; | |
| 2597 | try self.emitBody(old_inst.else_body, inst_table, &body_tmp); | |
| 2598 | new_inst.positionals.else_body = .{ | |
| 2599 | .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items), | |
| 2600 | }; | |
| 2593 | 2601 | break :blk &new_inst.base; |
| 2594 | 2602 | }, |
| 2595 | 2603 | .varptr => @panic("TODO"), |
src/zir_sema.zig+27-7| ... | ... | @@ -1238,7 +1238,20 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In |
| 1238 | 1238 | const target = try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target_ptr.src); |
| 1239 | 1239 | try validateSwitch(mod, scope, target, inst); |
| 1240 | 1240 | |
| 1241 | // TODO comptime execution | |
| 1241 | if (try mod.resolveDefinedValue(scope, target)) |target_val| { | |
| 1242 | for (inst.positionals.cases) |case| { | |
| 1243 | const resolved = try resolveInst(mod, scope, case.item); | |
| 1244 | const casted = try mod.coerce(scope, target.ty, resolved); | |
| 1245 | const item = try mod.resolveConstValue(scope, casted); | |
| 1246 | ||
| 1247 | if (target_val.eql(item)) { | |
| 1248 | try analyzeBody(mod, scope, case.body); | |
| 1249 | return mod.constNoReturn(scope, inst.base.src); | |
| 1250 | } | |
| 1251 | } | |
| 1252 | try analyzeBody(mod, scope, inst.positionals.else_body); | |
| 1253 | return mod.constNoReturn(scope, inst.base.src); | |
| 1254 | } | |
| 1242 | 1255 | |
| 1243 | 1256 | const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src); |
| 1244 | 1257 | const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len); |
| ... | ... | @@ -1253,7 +1266,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In |
| 1253 | 1266 | }; |
| 1254 | 1267 | defer case_block.instructions.deinit(mod.gpa); |
| 1255 | 1268 | |
| 1256 | for (inst.positionals.cases[0..inst.positionals.cases.len]) |case, i| { | |
| 1269 | for (inst.positionals.cases) |case, i| { | |
| 1257 | 1270 | // Reset without freeing. |
| 1258 | 1271 | case_block.instructions.items.len = 0; |
| 1259 | 1272 | |
| ... | ... | @@ -1269,7 +1282,14 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In |
| 1269 | 1282 | }; |
| 1270 | 1283 | } |
| 1271 | 1284 | |
| 1272 | return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases); | |
| 1285 | case_block.instructions.items.len = 0; | |
| 1286 | try analyzeBody(mod, &case_block.base, inst.positionals.else_body); | |
| 1287 | ||
| 1288 | const else_body: ir.Body = .{ | |
| 1289 | .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items), | |
| 1290 | }; | |
| 1291 | ||
| 1292 | return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases, else_body); | |
| 1273 | 1293 | } |
| 1274 | 1294 | |
| 1275 | 1295 | fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void { |
| ... | ... | @@ -1354,14 +1374,14 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw |
| 1354 | 1374 | false_count += 1; |
| 1355 | 1375 | } |
| 1356 | 1376 | |
| 1357 | if (true_count > 1 or false_count > 1) { | |
| 1377 | if (true_count + false_count > 2) { | |
| 1358 | 1378 | return mod.fail(scope, item.src, "duplicate switch value", .{}); |
| 1359 | 1379 | } |
| 1360 | 1380 | } |
| 1361 | if ((true_count == 0 or false_count == 0) and inst.kw_args.special_prong != .@"else") { | |
| 1381 | if ((true_count + false_count < 2) and inst.kw_args.special_prong != .@"else") { | |
| 1362 | 1382 | return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{}); |
| 1363 | 1383 | } |
| 1364 | if ((true_count == 1 and false_count == 1) and inst.kw_args.special_prong == .@"else") { | |
| 1384 | if ((true_count + false_count == 2) and inst.kw_args.special_prong == .@"else") { | |
| 1365 | 1385 | return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); |
| 1366 | 1386 | } |
| 1367 | 1387 | }, |
| ... | ... | @@ -1696,7 +1716,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE |
| 1696 | 1716 | if (try mod.resolveDefinedValue(scope, cond)) |cond_val| { |
| 1697 | 1717 | const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body; |
| 1698 | 1718 | try analyzeBody(mod, scope, body.*); |
| 1699 | return mod.constVoid(scope, inst.base.src); | |
| 1719 | return mod.constNoReturn(scope, inst.base.src); | |
| 1700 | 1720 | } |
| 1701 | 1721 | |
| 1702 | 1722 | const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src); |