authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-10-16 23:11:35+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-10-30 15:58:13+02:00
log4155d2ae242d18c0bc280aa22f733bf7dcb6e1f0
treef63b33a1ebb55b9c74f90bfd230b55104d9da473
parent3c96d799531dbfaf4127ed2fcaa0e69658f90e23
signaturelock-open Commit is signed but in an unrecognized format.

stage2: switch ranges and multi item prongs


5 files changed, 189 insertions(+), 94 deletions(-)

src/astgen.zig+140-91
...@@ -1561,6 +1561,17 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)...@@ -1561,6 +1561,17 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)
1561 return &for_block.base;1561 return &for_block.base;
1562}1562}
15631563
1564fn getRangeNode(node: *ast.Node) ?*ast.Node.SimpleInfixOp {
1565 var cur = node;
1566 while (true) {
1567 switch (cur.tag) {
1568 .Range => return @fieldParentPtr(ast.Node.SimpleInfixOp, "base", cur),
1569 .GroupedExpression => cur = @fieldParentPtr(ast.Node.GroupedExpression, "base", cur).expr,
1570 else => return null,
1571 }
1572 }
1573}
1574
1564fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.Switch) InnerError!*zir.Inst {1575fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.Switch) InnerError!*zir.Inst {
1565 var block_scope: Scope.GenZIR = .{1576 var block_scope: Scope.GenZIR = .{
1566 .parent = scope,1577 .parent = scope,
...@@ -1581,6 +1592,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1581,6 +1592,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1581 const tree = scope.tree();1592 const tree = scope.tree();
1582 const switch_src = tree.token_locs[switch_node.switch_token].start;1593 const switch_src = tree.token_locs[switch_node.switch_token].start;
1583 const target_ptr = try expr(mod, &block_scope.base, .ref, switch_node.expr);1594 const target_ptr = try expr(mod, &block_scope.base, .ref, switch_node.expr);
1595 const target = try addZIRUnOp(mod, &block_scope.base, target_ptr.src, .deref, target_ptr);
1584 // Add the switch instruction here so that it comes before any range checks.1596 // Add the switch instruction here so that it comes before any range checks.
1585 const switch_inst = (try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{1597 const switch_inst = (try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{
1586 .target_ptr = target_ptr,1598 .target_ptr = target_ptr,
...@@ -1593,24 +1605,51 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1593,24 +1605,51 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1593 var cases = std.ArrayList(zir.Inst.SwitchBr.Case).init(mod.gpa);1605 var cases = std.ArrayList(zir.Inst.SwitchBr.Case).init(mod.gpa);
1594 defer cases.deinit();1606 defer cases.deinit();
15951607
1608 // Add comptime block containing all prong items first,
1609 const item_block = try addZIRInstBlock(mod, scope, switch_src, .block_comptime_flat, .{
1610 .instructions = undefined, // populated below
1611 });
1612 // then add block containing the switch.
1613 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
1614 .instructions = undefined, // populated below
1615 });
1616
1617 // Most result location types can be forwarded directly; however
1618 // if we need to write to a pointer which has an inferred type,
1619 // proper type inference requires peer type resolution on the switch case.
1620 const case_rl: ResultLoc = switch (rl) {
1621 .discard, .none, .ty, .ptr, .ref => rl,
1622 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
1623 };
1624
1625 var case_scope: Scope.GenZIR = .{
1626 .parent = scope,
1627 .decl = block_scope.decl,
1628 .arena = block_scope.arena,
1629 .instructions = .{},
1630 };
1631 defer case_scope.instructions.deinit(mod.gpa);
1632
1596 // first we gather all the switch items and check else/'_' prongs1633 // first we gather all the switch items and check else/'_' prongs
1597 var else_src: ?usize = null;1634 var else_src: ?usize = null;
1598 var underscore_src: ?usize = null;1635 var underscore_src: ?usize = null;
1599 var range_inst: ?*zir.Inst = null;1636 var first_range: ?*zir.Inst = null;
1637 var special_case: ?*ast.Node.SwitchCase = null;
1600 for (switch_node.cases()) |uncasted_case| {1638 for (switch_node.cases()) |uncasted_case| {
1601 const case = uncasted_case.castTag(.SwitchCase).?;1639 const case = uncasted_case.castTag(.SwitchCase).?;
1602 const case_src = tree.token_locs[case.firstToken()].start;1640 const case_src = tree.token_locs[case.firstToken()].start;
1641 // reset without freeing to reduce allocations.
1642 case_scope.instructions.items.len = 0;
1643 assert(case.items_len != 0);
16031644
1604 if (case.payload != null) {1645 // Check for else/_ prong, those are handled last.
1605 return mod.fail(scope, case_src, "TODO switch case payload capture", .{});
1606 }
1607
1608 if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) {1646 if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) {
1609 if (else_src) |src| {1647 if (else_src) |src| {
1610 return mod.fail(scope, case_src, "multiple else prongs in switch expression", .{});1648 return mod.fail(scope, case_src, "multiple else prongs in switch expression", .{});
1611 // TODO notes "previous else prong is here"1649 // TODO notes "previous else prong is here"
1612 }1650 }
1613 else_src = case_src;1651 else_src = case_src;
1652 special_case = case;
1614 continue;1653 continue;
1615 } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and1654 } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and
1616 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))1655 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))
...@@ -1620,6 +1659,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1620,6 +1659,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1620 // TODO notes "previous '_' prong is here"1659 // TODO notes "previous '_' prong is here"
1621 }1660 }
1622 underscore_src = case_src;1661 underscore_src = case_src;
1662 special_case = case;
1623 continue;1663 continue;
1624 }1664 }
16251665
...@@ -1631,103 +1671,107 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1631,103 +1671,107 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1631 }1671 }
1632 }1672 }
16331673
1634 // TODO and not range1674 // If this is a simple one item prong then it is handled by the switchbr.
1635 if (case.items_len == 1) {1675 if (case.items_len == 1 and getRangeNode(case.items()[0]) == null) {
1636 const item = try expr(mod, &item_scope.base, .none, case.items()[0]);1676 const item = try expr(mod, &item_scope.base, .none, case.items()[0]);
1677 try items.append(item);
1678 try switchCaseExpr(mod, &case_scope.base, case_rl, block, case);
1679
1637 try cases.append(.{1680 try cases.append(.{
1638 .item = item,1681 .item = item,
1639 .body = undefined, // populated below1682 .body = .{ .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items) },
1640 });1683 });
1641 continue;1684 continue;
1642 }1685 }
1643 return mod.fail(scope, case_src, "TODO switch ranges", .{});
1644 }
16451686
1646 // Actually populate switch instruction values.1687 // TODO if the case has few items and no ranges it might be better
1647 if (else_src != null) switch_inst.kw_args.special_prong = .@"else";1688 // to just handle them as switch prongs.
1648 if (underscore_src != null) switch_inst.kw_args.special_prong = .underscore;1689
1649 switch_inst.positionals.cases = try block_scope.arena.dupe(zir.Inst.SwitchBr.Case, cases.items);1690 // Check if the target matches any of the items.
1650 switch_inst.positionals.items = try block_scope.arena.dupe(*zir.Inst, items.items);1691 // 1, 2, 3..6 will result in
1651 switch_inst.kw_args.range = range_inst;1692 // target == 1 or target == 2 or (target >= 3 and target <= 6)
1693 var any_ok: ?*zir.Inst = null;
1694 for (case.items()) |item| {
1695 if (getRangeNode(item)) |range| {
1696 const start = try expr(mod, &item_scope.base, .none, range.lhs);
1697 const end = try expr(mod, &item_scope.base, .none, range.rhs);
1698 const range_src = tree.token_locs[range.op_token].start;
1699 const range_inst = try addZIRBinOp(mod, &item_scope.base, range_src, .switch_range, start, end);
1700 try items.append(range_inst);
1701 if (first_range == null) first_range = range_inst;
1702
1703 // 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);
1707
1708 if (any_ok) |some| {
1709 any_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .boolor, some, range_ok);
1710 } else {
1711 any_ok = range_ok;
1712 }
1713 continue;
1714 }
16521715
1653 // Add comptime block containing all prong items first,1716 const item_inst = try expr(mod, &item_scope.base, .none, item);
1654 _ = try addZIRInstBlock(mod, scope, switch_src, .block_comptime_flat, .{1717 try items.append(item_inst);
1655 .instructions = try block_scope.arena.dupe(*zir.Inst, item_scope.instructions.items),1718 const cpm_ok = try addZIRBinOp(mod, &block_scope.base, item_inst.src, .cmp_eq, target, item_inst);
1656 });
1657 // then add block containing the switch.
1658 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
1659 .instructions = undefined, // populated below
1660 });
16611719
1662 // Most result location types can be forwarded directly; however1720 if (any_ok) |some| {
1663 // if we need to write to a pointer which has an inferred type,1721 any_ok = try addZIRBinOp(mod, &block_scope.base, item_inst.src, .boolor, some, cpm_ok);
1664 // proper type inference requires peer type resolution on the switch case.1722 } else {
1665 const case_rl: ResultLoc = switch (rl) {1723 any_ok = cpm_ok;
1666 .discard, .none, .ty, .ptr, .ref => rl,1724 }
1667 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },1725 }
1668 };
16691726
1670 var case_scope: Scope.GenZIR = .{1727 const condbr = try addZIRInstSpecial(mod, &block_scope.base, case_src, zir.Inst.CondBr, .{
1671 .parent = scope,1728 .condition = any_ok.?,
1672 .decl = block_scope.decl,1729 .then_body = undefined, // populated below
1673 .arena = block_scope.arena,1730 .else_body = undefined, // populated below
1674 .instructions = .{},1731 }, .{});
1675 };
1676 defer case_scope.instructions.deinit(mod.gpa);
16771732
1678 // And finally we fill generate the bodies of each case.1733 try switchCaseExpr(mod, &case_scope.base, case_rl, block, case);
1679 var case_index: usize = 0;1734 condbr.positionals.then_body = .{
1680 var special_case: ?*ast.Node.SwitchCase = null;1735 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
1681 for (switch_node.cases()) |uncasted_case| {1736 };
1682 const case = uncasted_case.castTag(.SwitchCase).?;
1683 const case_src = tree.token_locs[case.firstToken()].start;
1684 // reset without freeing to reduce allocations.
1685 defer case_scope.instructions.items.len = 0;
16861737
1687 if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) {1738 // reset to add the empty block
1688 // validated earlier1739 case_scope.instructions.items.len = 0;
1689 special_case = case;1740 const empty_block = try addZIRInstBlock(mod, &case_scope.base, case_src, .block, .{
1690 continue;1741 .instructions = undefined, // populated below
1691 } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and1742 });
1692 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))1743 condbr.positionals.else_body = .{
1693 {1744 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
1694 // validated earlier1745 };
1695 special_case = case;
1696 continue;
1697 }
16981746
1699 if (case.items_len == 1) {1747 // reset to add a break to the empty block
1700 // Generate the body of this case.1748 case_scope.instructions.items.len = 0;
1701 const case_body = try expr(mod, &case_scope.base, case_rl, case.expr);1749 _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.BreakVoid, .{
1702 if (!case_body.tag.isNoReturn()) {1750 .block = empty_block,
1703 _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.Break, .{1751 }, .{});
1704 .block = block,1752 empty_block.positionals.body = .{
1705 .operand = case_body,1753 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
1706 }, .{});1754 };
1707 }
1708 switch_inst.positionals.cases[case_index].body = .{
1709 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
1710 };
1711 case_index += 1;
1712 continue;
1713 }
1714 return mod.fail(scope, case_src, "TODO switch ranges", .{});
1715 }1755 }
17161756
1757 // All items have been generated, add the instructions to the comptime block.
1758 item_block.positionals.body = .{
1759 .instructions = try block_scope.arena.dupe(*zir.Inst, item_scope.instructions.items),
1760 };
1761
1762 // Actually populate switch instruction values.
1763 if (else_src != null) switch_inst.kw_args.special_prong = .@"else";
1764 if (underscore_src != null) switch_inst.kw_args.special_prong = .underscore;
1765 switch_inst.positionals.cases = try block_scope.arena.dupe(zir.Inst.SwitchBr.Case, cases.items);
1766 switch_inst.positionals.items = try block_scope.arena.dupe(*zir.Inst, items.items);
1767 switch_inst.kw_args.range = first_range;
1768
1717 // Generate else block or a break last to finish the block.1769 // Generate else block or a break last to finish the block.
1718 if (special_case) |case| {1770 if (special_case) |case| {
1719 const case_src = tree.token_locs[case.firstToken()].start;1771 try switchCaseExpr(mod, &block_scope.base, case_rl, block, case);
1720 const case_body = try expr(mod, &block_scope.base, case_rl, case.expr);
1721 if (!case_body.tag.isNoReturn()) {
1722 _ = try addZIRInst(mod, &block_scope.base, case_src, zir.Inst.Break, .{
1723 .block = block,
1724 .operand = case_body,
1725 }, .{});
1726 }
1727 } else {1772 } else {
1728 _ = try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.BreakVoid, .{1773 // Not handling all possible cases is a compile error.
1729 .block = block,1774 _ = try addZIRNoOp(mod, &block_scope.base, switch_src, .unreach_nocheck);
1730 }, .{});
1731 }1775 }
17321776
1733 // Set block instructions now that it is finished.1777 // Set block instructions now that it is finished.
...@@ -1737,15 +1781,20 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1737,15 +1781,20 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1737 return &block.base;1781 return &block.base;
1738}1782}
17391783
1740/// Only used for `a...b` in switches.1784fn switchCaseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, block: *zir.Inst.Block, case: *ast.Node.SwitchCase) !void {
1741fn switchRange(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
1742 const tree = scope.tree();1785 const tree = scope.tree();
1743 const src = tree.token_locs[node.op_token].start;1786 const case_src = tree.token_locs[case.firstToken()].start;
17441787 if (case.payload != null) {
1745 const start = try expr(mod, scope, .none, node.lhs);1788 return mod.fail(scope, case_src, "TODO switch case payload capture", .{});
1746 const end = try expr(mod, scope, .none, node.rhs);1789 }
17471790
1748 return try addZIRBinOp(mod, scope, src, .switch_range, start, end);1791 const case_body = try expr(mod, scope, rl, case.expr);
1792 if (!case_body.tag.isNoReturn()) {
1793 _ = try addZIRInst(mod, scope, case_src, zir.Inst.Break, .{
1794 .block = block,
1795 .operand = case_body,
1796 }, .{});
1797 }
1749}1798}
17501799
1751fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {1800fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
src/codegen.zig+9-1
...@@ -758,6 +758,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -758,6 +758,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
758 .br => return self.genBr(inst.castTag(.br).?),758 .br => return self.genBr(inst.castTag(.br).?),
759 .breakpoint => return self.genBreakpoint(inst.src),759 .breakpoint => return self.genBreakpoint(inst.src),
760 .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?),760 .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?),
761 .booland => return self.genBoolOp(inst.castTag(.booland).?),
762 .boolor => return self.genBoolOp(inst.castTag(.boolor).?),
761 .call => return self.genCall(inst.castTag(.call).?),763 .call => return self.genCall(inst.castTag(.call).?),
762 .cmp_lt => return self.genCmp(inst.castTag(.cmp_lt).?, .lt),764 .cmp_lt => return self.genCmp(inst.castTag(.cmp_lt).?, .lt),
763 .cmp_lte => return self.genCmp(inst.castTag(.cmp_lte).?, .lte),765 .cmp_lte => return self.genCmp(inst.castTag(.cmp_lte).?, .lte),
...@@ -782,11 +784,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -782,11 +784,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
782 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),784 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),
783 .store => return self.genStore(inst.castTag(.store).?),785 .store => return self.genStore(inst.castTag(.store).?),
784 .sub => return self.genSub(inst.castTag(.sub).?),786 .sub => return self.genSub(inst.castTag(.sub).?),
787 .switchbr => return self.genSwitch(inst.castTag(.switchbr).?),
785 .unreach => return MCValue{ .unreach = {} },788 .unreach => return MCValue{ .unreach = {} },
786 .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?),789 .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?),
787 .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),790 .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
788 .varptr => return self.genVarPtr(inst.castTag(.varptr).?),791 .varptr => return self.genVarPtr(inst.castTag(.varptr).?),
789 .switchbr => return self.genSwitch(inst.castTag(.switchbr).?),
790 }792 }
791 }793 }
792794
...@@ -2030,6 +2032,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2030,6 +2032,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2030 return self.brVoid(inst.base.src, inst.block);2032 return self.brVoid(inst.base.src, inst.block);
2031 }2033 }
20322034
2035 fn genBoolOp(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
2036 switch (arch) {
2037 else => return self.fail(inst.base.src, "TODO genBoolOp for {}", .{self.target.cpu.arch}),
2038 }
2039 }
2040
2033 fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue {2041 fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue {
2034 // Emit a jump with a relocation. It will be patched up after the block ends.2042 // Emit a jump with a relocation. It will be patched up after the block ends.
2035 try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);2043 try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);
src/ir.zig+4
...@@ -74,6 +74,8 @@ pub const Inst = struct {...@@ -74,6 +74,8 @@ pub const Inst = struct {
74 isnonnull,74 isnonnull,
75 isnull,75 isnull,
76 iserr,76 iserr,
77 booland,
78 boolor,
77 /// Read a value from a pointer.79 /// Read a value from a pointer.
78 load,80 load,
79 loop,81 loop,
...@@ -126,6 +128,8 @@ pub const Inst = struct {...@@ -126,6 +128,8 @@ pub const Inst = struct {
126 .cmp_gt,128 .cmp_gt,
127 .cmp_neq,129 .cmp_neq,
128 .store,130 .store,
131 .booland,
132 .boolor,
129 => BinOp,133 => BinOp,
130134
131 .arg => Arg,135 .arg => Arg,
src/zir.zig+11-1
...@@ -85,8 +85,12 @@ pub const Inst = struct {...@@ -85,8 +85,12 @@ pub const Inst = struct {
85 block_comptime,85 block_comptime,
86 /// Same as `block_flat` but additionally makes the inner instructions execute at comptime.86 /// Same as `block_flat` but additionally makes the inner instructions execute at comptime.
87 block_comptime_flat,87 block_comptime_flat,
88 /// Boolean AND. See also `bitand`.
89 booland,
88 /// Boolean NOT. See also `bitnot`.90 /// Boolean NOT. See also `bitnot`.
89 boolnot,91 boolnot,
92 /// Boolean OR. See also `bitor`.
93 boolor,
90 /// Return a value from a `Block`.94 /// Return a value from a `Block`.
91 @"break",95 @"break",
92 breakpoint,96 breakpoint,
...@@ -333,6 +337,8 @@ pub const Inst = struct {...@@ -333,6 +337,8 @@ pub const Inst = struct {
333 .array_type,337 .array_type,
334 .bitand,338 .bitand,
335 .bitor,339 .bitor,
340 .booland,
341 .boolor,
336 .div,342 .div,
337 .mod_rem,343 .mod_rem,
338 .mul,344 .mul,
...@@ -425,6 +431,8 @@ pub const Inst = struct {...@@ -425,6 +431,8 @@ pub const Inst = struct {
425 .block_comptime,431 .block_comptime,
426 .block_comptime_flat,432 .block_comptime_flat,
427 .boolnot,433 .boolnot,
434 .booland,
435 .boolor,
428 .breakpoint,436 .breakpoint,
429 .call,437 .call,
430 .cmp_lt,438 .cmp_lt,
...@@ -502,6 +510,7 @@ pub const Inst = struct {...@@ -502,6 +510,7 @@ pub const Inst = struct {
502 .slice_start,510 .slice_start,
503 .import,511 .import,
504 .switchbr,512 .switchbr,
513 .switch_range,
505 => false,514 => false,
506515
507 .@"break",516 .@"break",
...@@ -513,7 +522,6 @@ pub const Inst = struct {...@@ -513,7 +522,6 @@ pub const Inst = struct {
513 .unreach_nocheck,522 .unreach_nocheck,
514 .@"unreachable",523 .@"unreachable",
515 .loop,524 .loop,
516 .switch_range,
517 => true,525 => true,
518 };526 };
519 }527 }
...@@ -2320,6 +2328,8 @@ const EmitZIR = struct {...@@ -2320,6 +2328,8 @@ const EmitZIR = struct {
2320 .cmp_gte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gte).?, .cmp_gte),2328 .cmp_gte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gte).?, .cmp_gte),
2321 .cmp_gt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gt).?, .cmp_gt),2329 .cmp_gt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gt).?, .cmp_gt),
2322 .cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq),2330 .cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq),
2331 .booland => try self.emitBinOp(inst.src, new_body, inst.castTag(.booland).?, .booland),
2332 .boolor => try self.emitBinOp(inst.src, new_body, inst.castTag(.boolor).?, .boolor),
23232333
2324 .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, .bitcast),2334 .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, .bitcast),
2325 .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast),2335 .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast),
src/zir_sema.zig+25-1
...@@ -137,6 +137,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -137,6 +137,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
137 .import => return analyzeInstImport(mod, scope, old_inst.castTag(.import).?),137 .import => return analyzeInstImport(mod, scope, old_inst.castTag(.import).?),
138 .switchbr => return analyzeInstSwitchBr(mod, scope, old_inst.castTag(.switchbr).?),138 .switchbr => return analyzeInstSwitchBr(mod, scope, old_inst.castTag(.switchbr).?),
139 .switch_range => return analyzeInstSwitchRange(mod, scope, old_inst.castTag(.switch_range).?),139 .switch_range => return analyzeInstSwitchRange(mod, scope, old_inst.castTag(.switch_range).?),
140 .booland => return analyzeInstBoolOp(mod, scope, old_inst.castTag(.booland).?),
141 .boolor => return analyzeInstBoolOp(mod, scope, old_inst.castTag(.boolor).?),
140 }142 }
141}143}
142144
...@@ -1224,7 +1226,7 @@ fn analyzeInstSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In...@@ -1224,7 +1226,7 @@ fn analyzeInstSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In
1224 if (start.value()) |start_val| {1226 if (start.value()) |start_val| {
1225 if (end.value()) |end_val| {1227 if (end.value()) |end_val| {
1226 if (start_val.compare(.gte, end_val)) {1228 if (start_val.compare(.gte, end_val)) {
1227 return mod.fail(scope, inst.base.src, "range start value is greater than the end value", .{});1229 return mod.fail(scope, inst.base.src, "range start value must be smaller than the end value", .{});
1228 }1230 }
1229 }1231 }
1230 }1232 }
...@@ -1609,6 +1611,28 @@ fn analyzeInstBoolNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerEr...@@ -1609,6 +1611,28 @@ fn analyzeInstBoolNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerEr
1609 return mod.addUnOp(b, inst.base.src, bool_type, .not, operand);1611 return mod.addUnOp(b, inst.base.src, bool_type, .not, operand);
1610}1612}
16111613
1614fn analyzeInstBoolOp(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
1615 const bool_type = Type.initTag(.bool);
1616 const uncasted_lhs = try resolveInst(mod, scope, inst.positionals.lhs);
1617 const lhs = try mod.coerce(scope, bool_type, uncasted_lhs);
1618 const uncasted_rhs = try resolveInst(mod, scope, inst.positionals.rhs);
1619 const rhs = try mod.coerce(scope, bool_type, uncasted_rhs);
1620
1621 const is_bool_or = inst.base.tag == .boolor;
1622
1623 if (lhs.value()) |lhs_val| {
1624 if (rhs.value()) |rhs_val| {
1625 if (is_bool_or) {
1626 return mod.constBool(scope, inst.base.src, lhs_val.toBool() or rhs_val.toBool());
1627 } else {
1628 return mod.constBool(scope, inst.base.src, lhs_val.toBool() and rhs_val.toBool());
1629 }
1630 }
1631 }
1632 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
1633 return mod.addBinOp(b, inst.base.src, bool_type, if (is_bool_or) .boolor else .booland, lhs, rhs);
1634}
1635
1612fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst {1636fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst {
1613 const operand = try resolveInst(mod, scope, inst.positionals.operand);1637 const operand = try resolveInst(mod, scope, inst.positionals.operand);
1614 return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic);1638 return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic);