authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-10-31 09:39:28+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-10-31 09:39:28+02:00
log7c8d9cfa40ab96aede2a7fe8ec9dce6f10bc910a
treebac19bf24748e7d63e3bbf86ca5b34c4cfa2c754
parentbb6e39e274eb0a68bfb1029ab75d1791abeb2911
parent22ec5e085914d9fd7b17a28a8d3ad01258f3ad03
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #6660 from Vexu/stage2

Stage2 switch and package imports

14 files changed, 1379 insertions(+), 30 deletions(-)

src/Compilation.zig+1
......@@ -660,6 +660,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
660660 .source = .{ .unloaded = {} },
661661 .contents = .{ .not_available = {} },
662662 .status = .never_loaded,
663 .pkg = root_pkg,
663664 .root_container = .{
664665 .file_scope = root_scope,
665666 .decls = .{},
src/Module.zig+82-8
......@@ -469,6 +469,22 @@ pub const Scope = struct {
469469 }
470470 }
471471
472 pub fn getOwnerPkg(base: *Scope) *Package {
473 var cur = base;
474 while (true) {
475 cur = switch (cur.tag) {
476 .container => return @fieldParentPtr(Container, "base", cur).file_scope.pkg,
477 .file => return @fieldParentPtr(File, "base", cur).pkg,
478 .zir_module => unreachable, // TODO are zir modules allowed to import packages?
479 .gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent,
480 .local_val => @fieldParentPtr(LocalVal, "base", cur).parent,
481 .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent,
482 .block => @fieldParentPtr(Block, "base", cur).decl.scope,
483 .decl => @fieldParentPtr(DeclAnalysis, "base", cur).decl.scope,
484 };
485 }
486 }
487
472488 /// Asserts the scope is a namespace Scope and removes the Decl from the namespace.
473489 pub fn removeDecl(base: *Scope, child: *Decl) void {
474490 switch (base.tag) {
......@@ -576,6 +592,8 @@ pub const Scope = struct {
576592 unloaded_parse_failure,
577593 loaded_success,
578594 },
595 /// Package that this file is a part of, managed externally.
596 pkg: *Package,
579597
580598 root_container: Container,
581599
......@@ -614,7 +632,7 @@ pub const Scope = struct {
614632 pub fn getSource(self: *File, module: *Module) ![:0]const u8 {
615633 switch (self.source) {
616634 .unloaded => {
617 const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions(
635 const source = try self.pkg.root_src_directory.handle.readFileAllocOptions(
618636 module.gpa,
619637 self.sub_file_path,
620638 std.math.maxInt(u32),
......@@ -1036,6 +1054,10 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
10361054 .param_types = param_types,
10371055 }, .{});
10381056
1057 if (self.comp.verbose_ir) {
1058 zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {};
1059 }
1060
10391061 // We need the memory for the Type to go into the arena for the Decl
10401062 var decl_arena = std.heap.ArenaAllocator.init(self.gpa);
10411063 errdefer decl_arena.deinit();
......@@ -1109,6 +1131,10 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
11091131 _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid);
11101132 }
11111133
1134 if (self.comp.verbose_ir) {
1135 zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {};
1136 }
1137
11121138 const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR);
11131139 fn_zir.* = .{
11141140 .body = .{
......@@ -1240,6 +1266,9 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12401266
12411267 const src = tree.token_locs[init_node.firstToken()].start;
12421268 const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node);
1269 if (self.comp.verbose_ir) {
1270 zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {};
1271 }
12431272
12441273 var inner_block: Scope.Block = .{
12451274 .parent = null,
......@@ -1281,6 +1310,10 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
12811310 .val = Value.initTag(.type_type),
12821311 });
12831312 const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node);
1313 if (self.comp.verbose_ir) {
1314 zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {};
1315 }
1316
12841317 const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{
12851318 .instructions = type_scope.instructions.items,
12861319 });
......@@ -1354,6 +1387,9 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
13541387 defer gen_scope.instructions.deinit(self.gpa);
13551388
13561389 _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr);
1390 if (self.comp.verbose_ir) {
1391 zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {};
1392 }
13571393
13581394 var block_scope: Scope.Block = .{
13591395 .parent = null,
......@@ -2080,6 +2116,29 @@ pub fn addCall(
20802116 return &inst.base;
20812117}
20822118
2119pub fn addSwitchBr(
2120 self: *Module,
2121 block: *Scope.Block,
2122 src: usize,
2123 target_ptr: *Inst,
2124 cases: []Inst.SwitchBr.Case,
2125 else_body: ir.Body,
2126) !*Inst {
2127 const inst = try block.arena.create(Inst.SwitchBr);
2128 inst.* = .{
2129 .base = .{
2130 .tag = .switchbr,
2131 .ty = Type.initTag(.noreturn),
2132 .src = src,
2133 },
2134 .target_ptr = target_ptr,
2135 .cases = cases,
2136 .else_body = else_body,
2137 };
2138 try block.instructions.append(self.gpa, &inst.base);
2139 return &inst.base;
2140}
2141
20832142pub fn constInst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*Inst {
20842143 const const_inst = try scope.arena().create(Inst.Constant);
20852144 const_inst.* = .{
......@@ -2400,28 +2459,43 @@ pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst,
24002459}
24012460
24022461pub fn analyzeImport(self: *Module, scope: *Scope, src: usize, target_string: []const u8) !*Scope.File {
2403 // TODO if (package_table.get(target_string)) |pkg|
2404 if (self.import_table.get(target_string)) |some| {
2462 const cur_pkg = scope.getOwnerPkg();
2463 const cur_pkg_dir_path = cur_pkg.root_src_directory.path orelse ".";
2464 const found_pkg = cur_pkg.table.get(target_string);
2465
2466 const resolved_path = if (found_pkg) |pkg|
2467 try std.fs.path.resolve(self.gpa, &[_][]const u8{ pkg.root_src_directory.path orelse ".", pkg.root_src_path })
2468 else
2469 try std.fs.path.resolve(self.gpa, &[_][]const u8{ cur_pkg_dir_path, target_string });
2470 errdefer self.gpa.free(resolved_path);
2471
2472 if (self.import_table.get(resolved_path)) |some| {
2473 self.gpa.free(resolved_path);
24052474 return some;
24062475 }
24072476
2408 // TODO check for imports outside of pkg path
2409 if (false) return error.ImportOutsidePkgPath;
2477 if (found_pkg == null) {
2478 const resolved_root_path = try std.fs.path.resolve(self.gpa, &[_][]const u8{cur_pkg_dir_path});
2479 defer self.gpa.free(resolved_root_path);
2480
2481 if (!mem.startsWith(u8, resolved_path, resolved_root_path)) {
2482 return error.ImportOutsidePkgPath;
2483 }
2484 }
24102485
24112486 // TODO Scope.Container arena for ty and sub_file_path
24122487 const struct_payload = try self.gpa.create(Type.Payload.EmptyStruct);
24132488 errdefer self.gpa.destroy(struct_payload);
24142489 const file_scope = try self.gpa.create(Scope.File);
24152490 errdefer self.gpa.destroy(file_scope);
2416 const file_path = try self.gpa.dupe(u8, target_string);
2417 errdefer self.gpa.free(file_path);
24182491
24192492 struct_payload.* = .{ .scope = &file_scope.root_container };
24202493 file_scope.* = .{
2421 .sub_file_path = file_path,
2494 .sub_file_path = resolved_path,
24222495 .source = .{ .unloaded = {} },
24232496 .contents = .{ .not_available = {} },
24242497 .status = .never_loaded,
2498 .pkg = found_pkg orelse cur_pkg,
24252499 .root_container = .{
24262500 .file_scope = file_scope,
24272501 .decls = .{},
src/RangeSet.zig created+76
......@@ -0,0 +1,76 @@
1const std = @import("std");
2const Order = std.math.Order;
3const Value = @import("value.zig").Value;
4const RangeSet = @This();
5
6ranges: std.ArrayList(Range),
7
8pub const Range = struct {
9 start: Value,
10 end: Value,
11 src: usize,
12};
13
14pub fn init(allocator: *std.mem.Allocator) RangeSet {
15 return .{
16 .ranges = std.ArrayList(Range).init(allocator),
17 };
18}
19
20pub fn deinit(self: *RangeSet) void {
21 self.ranges.deinit();
22}
23
24pub fn add(self: *RangeSet, start: Value, end: Value, src: usize) !?usize {
25 for (self.ranges.items) |range| {
26 if ((start.compare(.gte, range.start) and start.compare(.lte, range.end)) or
27 (end.compare(.gte, range.start) and end.compare(.lte, range.end)))
28 {
29 // ranges overlap
30 return range.src;
31 }
32 }
33 try self.ranges.append(.{
34 .start = start,
35 .end = end,
36 .src = src,
37 });
38 return null;
39}
40
41/// Assumes a and b do not overlap
42fn lessThan(_: void, a: Range, b: Range) bool {
43 return a.start.compare(.lt, b.start);
44}
45
46pub fn spans(self: *RangeSet, start: Value, end: Value) !bool {
47 std.sort.sort(Range, self.ranges.items, {}, lessThan);
48
49 if (!self.ranges.items[0].start.eql(start) or
50 !self.ranges.items[self.ranges.items.len - 1].end.eql(end))
51 {
52 return false;
53 }
54
55 var space: Value.BigIntSpace = undefined;
56
57 var counter = try std.math.big.int.Managed.init(self.ranges.allocator);
58 defer counter.deinit();
59
60 // look for gaps
61 for (self.ranges.items[1..]) |cur, i| {
62 // i starts counting from the second item.
63 const prev = self.ranges.items[i];
64
65 // prev.end + 1 == cur.start
66 try counter.copy(prev.end.toBigInt(&space));
67 try counter.addScalar(counter.toConst(), 1);
68
69 const cur_start_int = cur.start.toBigInt(&space);
70 if (!cur_start_int.eq(counter.toConst())) {
71 return false;
72 }
73 }
74
75 return true;
76}
src/astgen.zig+241-2
......@@ -183,6 +183,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
183183 .VarDecl => unreachable, // Handled in `blockExpr`.
184184 .SwitchCase => unreachable, // Handled in `switchExpr`.
185185 .SwitchElse => unreachable, // Handled in `switchExpr`.
186 .Range => unreachable, // Handled in `switchExpr`.
186187 .Else => unreachable, // Handled explicitly the control flow expression functions.
187188 .Payload => unreachable, // Handled explicitly.
188189 .PointerPayload => unreachable, // Handled explicitly.
......@@ -279,9 +280,9 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
279280 .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),
280281 .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),
281282 .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),
283 .Switch => return switchExpr(mod, scope, rl, node.castTag(.Switch).?),
282284
283285 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
284 .Range => return mod.failNode(scope, node, "TODO implement astgen.expr for .Range", .{}),
285286 .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),
286287 .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
287288 .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
......@@ -289,7 +290,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
289290 .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),
290291 .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),
291292 .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}),
292 .Switch => return mod.failNode(scope, node, "TODO implement astgen.expr for .Switch", .{}),
293293 .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),
294294 .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}),
295295 .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
......@@ -1561,6 +1561,245 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)
15611561 return &for_block.base;
15621562}
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
1575fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.Switch) InnerError!*zir.Inst {
1576 var block_scope: Scope.GenZIR = .{
1577 .parent = scope,
1578 .decl = scope.decl().?,
1579 .arena = scope.arena(),
1580 .instructions = .{},
1581 };
1582 defer block_scope.instructions.deinit(mod.gpa);
1583
1584 const tree = scope.tree();
1585 const switch_src = tree.token_locs[switch_node.switch_token].start;
1586 const target_ptr = try expr(mod, &block_scope.base, .ref, switch_node.expr);
1587 const target = try addZIRUnOp(mod, &block_scope.base, target_ptr.src, .deref, target_ptr);
1588 // Add the switch instruction here so that it comes before any range checks.
1589 const switch_inst = (try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{
1590 .target_ptr = target_ptr,
1591 .cases = undefined, // populated below
1592 .items = &[_]*zir.Inst{}, // populated below
1593 .else_body = undefined, // populated below
1594 }, .{})).castTag(.switchbr).?;
1595
1596 var items = std.ArrayList(*zir.Inst).init(mod.gpa);
1597 defer items.deinit();
1598 var cases = std.ArrayList(zir.Inst.SwitchBr.Case).init(mod.gpa);
1599 defer cases.deinit();
1600
1601 // Add comptime block containing all prong items first,
1602 const item_block = try addZIRInstBlock(mod, scope, switch_src, .block_comptime_flat, .{
1603 .instructions = undefined, // populated below
1604 });
1605 // then add block containing the switch.
1606 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
1607 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
1608 });
1609
1610 // Most result location types can be forwarded directly; however
1611 // if we need to write to a pointer which has an inferred type,
1612 // proper type inference requires peer type resolution on the switch case.
1613 const case_rl: ResultLoc = switch (rl) {
1614 .discard, .none, .ty, .ptr, .ref => rl,
1615 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
1616 };
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
1626 var case_scope: Scope.GenZIR = .{
1627 .parent = scope,
1628 .decl = block_scope.decl,
1629 .arena = block_scope.arena,
1630 .instructions = .{},
1631 };
1632 defer case_scope.instructions.deinit(mod.gpa);
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
1642 // first we gather all the switch items and check else/'_' prongs
1643 var else_src: ?usize = null;
1644 var underscore_src: ?usize = null;
1645 var first_range: ?*zir.Inst = null;
1646 var special_case: ?*ast.Node.SwitchCase = null;
1647 for (switch_node.cases()) |uncasted_case| {
1648 const case = uncasted_case.castTag(.SwitchCase).?;
1649 const case_src = tree.token_locs[case.firstToken()].start;
1650 // reset without freeing to reduce allocations.
1651 case_scope.instructions.items.len = 0;
1652 assert(case.items_len != 0);
1653
1654 // Check for else/_ prong, those are handled last.
1655 if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) {
1656 if (else_src) |src| {
1657 return mod.fail(scope, case_src, "multiple else prongs in switch expression", .{});
1658 // TODO notes "previous else prong is here"
1659 }
1660 else_src = case_src;
1661 special_case = case;
1662 continue;
1663 } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and
1664 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))
1665 {
1666 if (underscore_src) |src| {
1667 return mod.fail(scope, case_src, "multiple '_' prongs in switch expression", .{});
1668 // TODO notes "previous '_' prong is here"
1669 }
1670 underscore_src = case_src;
1671 special_case = case;
1672 continue;
1673 }
1674
1675 if (else_src) |some_else| {
1676 if (underscore_src) |some_underscore| {
1677 return mod.fail(scope, switch_src, "else and '_' prong in switch expression", .{});
1678 // TODO notes "else prong is here"
1679 // TODO notes "'_' prong is here"
1680 }
1681 }
1682
1683 // If this is a simple one item prong then it is handled by the switchbr.
1684 if (case.items_len == 1 and getRangeNode(case.items()[0]) == null) {
1685 const item = try expr(mod, &item_scope.base, .none, case.items()[0]);
1686 try items.append(item);
1687 try switchCaseExpr(mod, &case_scope.base, case_rl, block, case);
1688
1689 try cases.append(.{
1690 .item = item,
1691 .body = .{ .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items) },
1692 });
1693 continue;
1694 }
1695
1696 // TODO if the case has few items and no ranges it might be better
1697 // to just handle them as switch prongs.
1698
1699 // Check if the target matches any of the items.
1700 // 1, 2, 3..6 will result in
1701 // target == 1 or target == 2 or (target >= 3 and target <= 6)
1702 var any_ok: ?*zir.Inst = null;
1703 for (case.items()) |item| {
1704 if (getRangeNode(item)) |range| {
1705 const start = try expr(mod, &item_scope.base, .none, range.lhs);
1706 const end = try expr(mod, &item_scope.base, .none, range.rhs);
1707 const range_src = tree.token_locs[range.op_token].start;
1708 const range_inst = try addZIRBinOp(mod, &item_scope.base, range_src, .switch_range, start, end);
1709 try items.append(range_inst);
1710 if (first_range == null) first_range = range_inst;
1711
1712 // target >= start and target <= end
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);
1716
1717 if (any_ok) |some| {
1718 any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .boolor, some, range_ok);
1719 } else {
1720 any_ok = range_ok;
1721 }
1722 continue;
1723 }
1724
1725 const item_inst = try expr(mod, &item_scope.base, .none, item);
1726 try items.append(item_inst);
1727 const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst);
1728
1729 if (any_ok) |some| {
1730 any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .boolor, some, cpm_ok);
1731 } else {
1732 any_ok = cpm_ok;
1733 }
1734 }
1735
1736 const condbr = try addZIRInstSpecial(mod, &case_scope.base, case_src, zir.Inst.CondBr, .{
1737 .condition = any_ok.?,
1738 .then_body = undefined, // populated below
1739 .else_body = undefined, // populated below
1740 }, .{});
1741 const cond_block = try addZIRInstBlock(mod, &else_scope.base, case_src, .block, .{
1742 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
1743 });
1744
1745 // reset cond_scope for then_body
1746 case_scope.instructions.items.len = 0;
1747 try switchCaseExpr(mod, &case_scope.base, case_rl, block, case);
1748 condbr.positionals.then_body = .{
1749 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
1750 };
1751
1752 // reset cond_scope for else_body
1753 case_scope.instructions.items.len = 0;
1754 _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.BreakVoid, .{
1755 .block = cond_block,
1756 }, .{});
1757 condbr.positionals.else_body = .{
1758 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
1759 };
1760 }
1761
1762 // Generate else block or a break last to finish the block.
1763 if (special_case) |case| {
1764 try switchCaseExpr(mod, &else_scope.base, case_rl, block, case);
1765 } else {
1766 // Not handling all possible cases is a compile error.
1767 _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreach_nocheck);
1768 }
1769
1770 // All items have been generated, add the instructions to the comptime block.
1771 item_block.positionals.body = .{
1772 .instructions = try block_scope.arena.dupe(*zir.Inst, item_scope.instructions.items),
1773 };
1774
1775 // Actually populate switch instruction values.
1776 if (else_src != null) switch_inst.kw_args.special_prong = .@"else";
1777 if (underscore_src != null) switch_inst.kw_args.special_prong = .underscore;
1778 switch_inst.positionals.cases = try block_scope.arena.dupe(zir.Inst.SwitchBr.Case, cases.items);
1779 switch_inst.positionals.items = try block_scope.arena.dupe(*zir.Inst, items.items);
1780 switch_inst.kw_args.range = first_range;
1781 switch_inst.positionals.else_body = .{
1782 .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
1783 };
1784 return &block.base;
1785}
1786
1787fn switchCaseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, block: *zir.Inst.Block, case: *ast.Node.SwitchCase) !void {
1788 const tree = scope.tree();
1789 const case_src = tree.token_locs[case.firstToken()].start;
1790 if (case.payload != null) {
1791 return mod.fail(scope, case_src, "TODO switch case payload capture", .{});
1792 }
1793
1794 const case_body = try expr(mod, scope, rl, case.expr);
1795 if (!case_body.tag.isNoReturn()) {
1796 _ = try addZIRInst(mod, scope, case_src, zir.Inst.Break, .{
1797 .block = block,
1798 .operand = case_body,
1799 }, .{});
1800 }
1801}
1802
15641803fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
15651804 const tree = scope.tree();
15661805 const src = tree.token_locs[cfe.ltoken].start;
src/codegen.zig+24
......@@ -758,6 +758,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
758758 .br => return self.genBr(inst.castTag(.br).?),
759759 .breakpoint => return self.genBreakpoint(inst.src),
760760 .brvoid => return self.genBrVoid(inst.castTag(.brvoid).?),
761 .booland => return self.genBoolOp(inst.castTag(.booland).?),
762 .boolor => return self.genBoolOp(inst.castTag(.boolor).?),
761763 .call => return self.genCall(inst.castTag(.call).?),
762764 .cmp_lt => return self.genCmp(inst.castTag(.cmp_lt).?, .lt),
763765 .cmp_lte => return self.genCmp(inst.castTag(.cmp_lte).?, .lte),
......@@ -782,6 +784,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
782784 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),
783785 .store => return self.genStore(inst.castTag(.store).?),
784786 .sub => return self.genSub(inst.castTag(.sub).?),
787 .switchbr => return self.genSwitch(inst.castTag(.switchbr).?),
785788 .unreach => return MCValue{ .unreach = {} },
786789 .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?),
787790 .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
......@@ -1989,6 +1992,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
19891992 return @bitCast(MCValue, inst.codegen.mcv);
19901993 }
19911994
1995 fn genSwitch(self: *Self, inst: *ir.Inst.SwitchBr) !MCValue {
1996 switch (arch) {
1997 else => return self.fail(inst.base.src, "TODO genSwitch for {}", .{self.target.cpu.arch}),
1998 }
1999 }
2000
19922001 fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {
19932002 switch (reloc) {
19942003 .rel32 => |pos| {
......@@ -2023,6 +2032,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
20232032 return self.brVoid(inst.base.src, inst.block);
20242033 }
20252034
2035 fn genBoolOp(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
2036 if (inst.base.isUnused())
2037 return MCValue.dead;
2038 switch (arch) {
2039 .x86_64 => if (inst.base.tag == .booland) {
2040 // lhs AND rhs
2041 return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 4, 0x20);
2042 } else {
2043 // lhs OR rhs
2044 return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 1, 0x08);
2045 },
2046 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
2047 }
2048 }
2049
20262050 fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue {
20272051 // Emit a jump with a relocation. It will be patched up after the block ends.
20282052 try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);
src/ir.zig+47
......@@ -74,6 +74,8 @@ pub const Inst = struct {
7474 isnonnull,
7575 isnull,
7676 iserr,
77 booland,
78 boolor,
7779 /// Read a value from a pointer.
7880 load,
7981 loop,
......@@ -91,6 +93,7 @@ pub const Inst = struct {
9193 intcast,
9294 unwrap_optional,
9395 wrap_optional,
96 switchbr,
9497
9598 pub fn Type(tag: Tag) type {
9699 return switch (tag) {
......@@ -125,6 +128,8 @@ pub const Inst = struct {
125128 .cmp_gt,
126129 .cmp_neq,
127130 .store,
131 .booland,
132 .boolor,
128133 => BinOp,
129134
130135 .arg => Arg,
......@@ -137,6 +142,7 @@ pub const Inst = struct {
137142 .constant => Constant,
138143 .loop => Loop,
139144 .varptr => VarPtr,
145 .switchbr => SwitchBr,
140146 };
141147 }
142148
......@@ -458,6 +464,47 @@ pub const Inst = struct {
458464 return null;
459465 }
460466 };
467
468 pub const SwitchBr = struct {
469 pub const base_tag = Tag.switchbr;
470
471 base: Inst,
472 target_ptr: *Inst,
473 cases: []Case,
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, ... ].
476 deaths: [*]*Inst = undefined,
477 else_index: u32 = 0,
478 else_deaths: u32 = 0,
479 else_body: Body,
480
481 pub const Case = struct {
482 item: Value,
483 body: Body,
484 index: u32 = 0,
485 deaths: u32 = 0,
486 };
487
488 pub fn operandCount(self: *const SwitchBr) usize {
489 return 1;
490 }
491 pub fn getOperand(self: *const SwitchBr, index: usize) ?*Inst {
492 var i = index;
493
494 if (i < 1)
495 return self.target_ptr;
496 i -= 1;
497
498 return null;
499 }
500 pub fn caseDeaths(self: *const SwitchBr, case_index: usize) []*Inst {
501 const case = self.cases[case_index];
502 return (self.deaths + case.index)[0..case.deaths];
503 }
504 pub fn elseDeaths(self: *const SwitchBr) []*Inst {
505 return (self.deaths + self.else_index)[0..self.else_deaths];
506 }
507 };
461508};
462509
463510pub const Body = struct {
src/liveness.zig+86
......@@ -144,6 +144,92 @@ fn analyzeInst(
144144 // instruction, and the deaths flag for the CondBr instruction will indicate whether the
145145 // condition's lifetime ends immediately before entering any branch.
146146 },
147 .switchbr => {
148 const inst = base.castTag(.switchbr).?;
149
150 const Table = std.AutoHashMap(*ir.Inst, void);
151 const case_tables = try table.allocator.alloc(Table, inst.cases.len + 1); // +1 for else
152 defer table.allocator.free(case_tables);
153
154 std.mem.set(Table, case_tables, Table.init(table.allocator));
155 defer for (case_tables) |*ct| ct.deinit();
156
157 for (inst.cases) |case, i| {
158 try analyzeWithTable(arena, table, &case_tables[i], case.body);
159
160 // Reset the table back to its state from before the case.
161 var it = case_tables[i].iterator();
162 while (it.next()) |entry| {
163 table.removeAssertDiscard(entry.key);
164 }
165 }
166 { // else
167 try analyzeWithTable(arena, table, &case_tables[case_tables.len - 1], inst.else_body);
168
169 // Reset the table back to its state from before the case.
170 var it = case_tables[case_tables.len - 1].iterator();
171 while (it.next()) |entry| {
172 table.removeAssertDiscard(entry.key);
173 }
174 }
175
176 const List = std.ArrayList(*ir.Inst);
177 const case_deaths = try table.allocator.alloc(List, case_tables.len); // +1 for else
178 defer table.allocator.free(case_deaths);
179
180 std.mem.set(List, case_deaths, List.init(table.allocator));
181 defer for (case_deaths) |*cd| cd.deinit();
182
183 var total_deaths: u32 = 0;
184 for (case_tables) |*ct, i| {
185 total_deaths += ct.count();
186 var it = ct.iterator();
187 while (it.next()) |entry| {
188 const case_death = entry.key;
189 for (case_tables) |*ct_inner, j| {
190 if (i == j) continue;
191 if (!ct_inner.contains(case_death)) {
192 // instruction is not referenced in this case
193 try case_deaths[j].append(case_death);
194 }
195 }
196 // undo resetting the table
197 _ = try table.put(case_death, {});
198 }
199 }
200
201 // Now we have to correctly populate new_set.
202 if (new_set) |ns| {
203 try ns.ensureCapacity(@intCast(u32, ns.count() + total_deaths));
204 for (case_tables) |*ct| {
205 var it = ct.iterator();
206 while (it.next()) |entry| {
207 _ = ns.putAssumeCapacity(entry.key, {});
208 }
209 }
210 }
211
212 total_deaths = 0;
213 for (case_deaths[0 .. case_deaths.len - 1]) |*ct, i| {
214 inst.cases[i].index = total_deaths;
215 const len = std.math.cast(@TypeOf(inst.else_deaths), ct.items.len) catch return error.OutOfMemory;
216 inst.cases[i].deaths = len;
217 total_deaths += len;
218 }
219 { // else
220 const else_deaths = std.math.cast(@TypeOf(inst.else_deaths), case_deaths[case_deaths.len - 1].items.len) catch return error.OutOfMemory;
221 inst.else_index = total_deaths;
222 inst.else_deaths = else_deaths;
223 total_deaths += else_deaths;
224 }
225
226 const allocated_slice = try arena.alloc(*ir.Inst, total_deaths);
227 inst.deaths = allocated_slice.ptr;
228 for (case_deaths[0 .. case_deaths.len - 1]) |*cd, i| {
229 std.mem.copy(*ir.Inst, inst.caseDeaths(i), cd.items);
230 }
231 std.mem.copy(*ir.Inst, inst.elseDeaths(), case_deaths[case_deaths.len - 1].items);
232 },
147233 else => {},
148234 }
149235
src/main.zig+1
......@@ -2421,6 +2421,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
24212421 var stdin_flag: bool = false;
24222422 var check_flag: bool = false;
24232423 var input_files = ArrayList([]const u8).init(gpa);
2424 defer input_files.deinit();
24242425
24252426 {
24262427 var i: usize = 0;
src/test.zig+4-4
......@@ -463,10 +463,10 @@ pub const TestContext = struct {
463463
464464 var cache_dir = try tmp.dir.makeOpenPath("zig-cache", .{});
465465 defer cache_dir.close();
466 const bogus_path = "bogus"; // TODO this will need to be fixed before we can test LLVM extensions
466 const tmp_path = try std.fs.path.join(arena, &[_][]const u8{ ".", "zig-cache", "tmp", &tmp.sub_path });
467467 const zig_cache_directory: Compilation.Directory = .{
468468 .handle = cache_dir,
469 .path = try std.fs.path.join(arena, &[_][]const u8{ bogus_path, "zig-cache" }),
469 .path = try std.fs.path.join(arena, &[_][]const u8{ tmp_path, "zig-cache" }),
470470 };
471471
472472 const tmp_src_path = switch (case.extension) {
......@@ -475,7 +475,7 @@ pub const TestContext = struct {
475475 };
476476
477477 var root_pkg: Package = .{
478 .root_src_directory = .{ .path = bogus_path, .handle = tmp.dir },
478 .root_src_directory = .{ .path = tmp_path, .handle = tmp.dir },
479479 .root_src_path = tmp_src_path,
480480 };
481481
......@@ -488,7 +488,7 @@ pub const TestContext = struct {
488488 });
489489
490490 const emit_directory: Compilation.Directory = .{
491 .path = bogus_path,
491 .path = tmp_path,
492492 .handle = tmp.dir,
493493 };
494494 const emit_bin: Compilation.EmitLoc = .{
src/type.zig+72
......@@ -2863,6 +2863,78 @@ pub const Type = extern union {
28632863 };
28642864 }
28652865
2866 /// Asserts that self.zigTypeTag() == .Int.
2867 pub fn minInt(self: Type, arena: *std.heap.ArenaAllocator, target: Target) !Value {
2868 assert(self.zigTypeTag() == .Int);
2869 const info = self.intInfo(target);
2870
2871 if (!info.signed) {
2872 return Value.initTag(.zero);
2873 }
2874
2875 if ((info.bits - 1) <= std.math.maxInt(u6)) {
2876 const payload = try arena.allocator.create(Value.Payload.Int_i64);
2877 payload.* = .{
2878 .int = -(@as(i64, 1) << @truncate(u6, info.bits - 1)),
2879 };
2880 return Value.initPayload(&payload.base);
2881 }
2882
2883 var res = try std.math.big.int.Managed.initSet(&arena.allocator, 1);
2884 try res.shiftLeft(res, info.bits - 1);
2885 res.negate();
2886
2887 const res_const = res.toConst();
2888 if (res_const.positive) {
2889 const val_payload = try arena.allocator.create(Value.Payload.IntBigPositive);
2890 val_payload.* = .{ .limbs = res_const.limbs };
2891 return Value.initPayload(&val_payload.base);
2892 } else {
2893 const val_payload = try arena.allocator.create(Value.Payload.IntBigNegative);
2894 val_payload.* = .{ .limbs = res_const.limbs };
2895 return Value.initPayload(&val_payload.base);
2896 }
2897 }
2898
2899 /// Asserts that self.zigTypeTag() == .Int.
2900 pub fn maxInt(self: Type, arena: *std.heap.ArenaAllocator, target: Target) !Value {
2901 assert(self.zigTypeTag() == .Int);
2902 const info = self.intInfo(target);
2903
2904 if (info.signed and (info.bits - 1) <= std.math.maxInt(u6)) {
2905 const payload = try arena.allocator.create(Value.Payload.Int_i64);
2906 payload.* = .{
2907 .int = (@as(i64, 1) << @truncate(u6, info.bits - 1)) - 1,
2908 };
2909 return Value.initPayload(&payload.base);
2910 } else if (!info.signed and info.bits <= std.math.maxInt(u6)) {
2911 const payload = try arena.allocator.create(Value.Payload.Int_u64);
2912 payload.* = .{
2913 .int = (@as(u64, 1) << @truncate(u6, info.bits)) - 1,
2914 };
2915 return Value.initPayload(&payload.base);
2916 }
2917
2918 var res = try std.math.big.int.Managed.initSet(&arena.allocator, 1);
2919 try res.shiftLeft(res, info.bits - @boolToInt(info.signed));
2920 const one = std.math.big.int.Const{
2921 .limbs = &[_]std.math.big.Limb{1},
2922 .positive = true,
2923 };
2924 res.sub(res.toConst(), one) catch unreachable;
2925
2926 const res_const = res.toConst();
2927 if (res_const.positive) {
2928 const val_payload = try arena.allocator.create(Value.Payload.IntBigPositive);
2929 val_payload.* = .{ .limbs = res_const.limbs };
2930 return Value.initPayload(&val_payload.base);
2931 } else {
2932 const val_payload = try arena.allocator.create(Value.Payload.IntBigNegative);
2933 val_payload.* = .{ .limbs = res_const.limbs };
2934 return Value.initPayload(&val_payload.base);
2935 }
2936 }
2937
28662938 /// This enum does not directly correspond to `std.builtin.TypeId` because
28672939 /// it has extra enum tags in it, as a way of using less memory. For example,
28682940 /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
src/value.zig+257-6
......@@ -565,7 +565,7 @@ pub const Value = extern union {
565565 .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(),
566566 .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(),
567567 .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(),
568 .int_big_negative => return self.cast(Payload.IntBigPositive).?.asBigInt(),
568 .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt(),
569569 }
570570 }
571571
......@@ -1233,15 +1233,170 @@ pub const Value = extern union {
12331233 }
12341234
12351235 pub fn eql(a: Value, b: Value) bool {
1236 if (a.tag() == b.tag() and a.tag() == .enum_literal) {
1237 const a_name = @fieldParentPtr(Payload.Bytes, "base", a.ptr_otherwise).data;
1238 const b_name = @fieldParentPtr(Payload.Bytes, "base", b.ptr_otherwise).data;
1239 return std.mem.eql(u8, a_name, b_name);
1236 if (a.tag() == b.tag()) {
1237 if (a.tag() == .void_value or a.tag() == .null_value) {
1238 return true;
1239 } else if (a.tag() == .enum_literal) {
1240 const a_name = @fieldParentPtr(Payload.Bytes, "base", a.ptr_otherwise).data;
1241 const b_name = @fieldParentPtr(Payload.Bytes, "base", b.ptr_otherwise).data;
1242 return std.mem.eql(u8, a_name, b_name);
1243 }
1244 }
1245 if (a.isType() and b.isType()) {
1246 // 128 bytes should be enough to hold both types
1247 var buf: [128]u8 = undefined;
1248 var fib = std.heap.FixedBufferAllocator.init(&buf);
1249 const a_type = a.toType(&fib.allocator) catch unreachable;
1250 const b_type = b.toType(&fib.allocator) catch unreachable;
1251 return a_type.eql(b_type);
12401252 }
1241 // TODO non numerical comparisons
12421253 return compare(a, .eq, b);
12431254 }
12441255
1256 pub fn hash(self: Value) u64 {
1257 var hasher = std.hash.Wyhash.init(0);
1258
1259 switch (self.tag()) {
1260 .u8_type,
1261 .i8_type,
1262 .u16_type,
1263 .i16_type,
1264 .u32_type,
1265 .i32_type,
1266 .u64_type,
1267 .i64_type,
1268 .usize_type,
1269 .isize_type,
1270 .c_short_type,
1271 .c_ushort_type,
1272 .c_int_type,
1273 .c_uint_type,
1274 .c_long_type,
1275 .c_ulong_type,
1276 .c_longlong_type,
1277 .c_ulonglong_type,
1278 .c_longdouble_type,
1279 .f16_type,
1280 .f32_type,
1281 .f64_type,
1282 .f128_type,
1283 .c_void_type,
1284 .bool_type,
1285 .void_type,
1286 .type_type,
1287 .anyerror_type,
1288 .comptime_int_type,
1289 .comptime_float_type,
1290 .noreturn_type,
1291 .null_type,
1292 .undefined_type,
1293 .fn_noreturn_no_args_type,
1294 .fn_void_no_args_type,
1295 .fn_naked_noreturn_no_args_type,
1296 .fn_ccc_void_no_args_type,
1297 .single_const_pointer_to_comptime_int_type,
1298 .const_slice_u8_type,
1299 .enum_literal_type,
1300 .anyframe_type,
1301 .ty,
1302 => {
1303 // Directly return Type.hash, toType can only fail for .int_type and .error_set.
1304 var allocator = std.heap.FixedBufferAllocator.init(&[_]u8{});
1305 return (self.toType(&allocator.allocator) catch unreachable).hash();
1306 },
1307 .error_set => {
1308 // Payload.decl should be same for all instances of the type.
1309 const payload = @fieldParentPtr(Payload.ErrorSet, "base", self.ptr_otherwise);
1310 std.hash.autoHash(&hasher, payload.decl);
1311 },
1312 .int_type => {
1313 const payload = self.cast(Payload.IntType).?;
1314 if (payload.signed) {
1315 var new = Type.Payload.IntSigned{ .bits = payload.bits };
1316 return Type.initPayload(&new.base).hash();
1317 } else {
1318 var new = Type.Payload.IntUnsigned{ .bits = payload.bits };
1319 return Type.initPayload(&new.base).hash();
1320 }
1321 },
1322
1323 .empty_struct_value,
1324 .empty_array,
1325 => {},
1326
1327 .undef,
1328 .null_value,
1329 .void_value,
1330 .unreachable_value,
1331 => std.hash.autoHash(&hasher, self.tag()),
1332
1333 .zero, .bool_false => std.hash.autoHash(&hasher, @as(u64, 0)),
1334 .one, .bool_true => std.hash.autoHash(&hasher, @as(u64, 1)),
1335
1336 .float_16, .float_32, .float_64, .float_128 => {},
1337 .enum_literal, .bytes => {
1338 const payload = @fieldParentPtr(Payload.Bytes, "base", self.ptr_otherwise);
1339 hasher.update(payload.data);
1340 },
1341 .int_u64 => {
1342 const payload = @fieldParentPtr(Payload.Int_u64, "base", self.ptr_otherwise);
1343 std.hash.autoHash(&hasher, payload.int);
1344 },
1345 .int_i64 => {
1346 const payload = @fieldParentPtr(Payload.Int_i64, "base", self.ptr_otherwise);
1347 std.hash.autoHash(&hasher, payload.int);
1348 },
1349 .repeated => {
1350 const payload = @fieldParentPtr(Payload.Repeated, "base", self.ptr_otherwise);
1351 std.hash.autoHash(&hasher, payload.val.hash());
1352 },
1353 .ref_val => {
1354 const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);
1355 std.hash.autoHash(&hasher, payload.val.hash());
1356 },
1357 .int_big_positive, .int_big_negative => {
1358 var space: BigIntSpace = undefined;
1359 const big = self.toBigInt(&space);
1360 if (big.limbs.len == 1) {
1361 // handle like {u,i}64 to ensure same hash as with Int{i,u}64
1362 if (big.positive) {
1363 std.hash.autoHash(&hasher, @as(u64, big.limbs[0]));
1364 } else {
1365 std.hash.autoHash(&hasher, @as(u64, @bitCast(usize, -@bitCast(isize, big.limbs[0]))));
1366 }
1367 } else {
1368 std.hash.autoHash(&hasher, big.positive);
1369 for (big.limbs) |limb| {
1370 std.hash.autoHash(&hasher, limb);
1371 }
1372 }
1373 },
1374 .elem_ptr => {
1375 const payload = @fieldParentPtr(Payload.ElemPtr, "base", self.ptr_otherwise);
1376 std.hash.autoHash(&hasher, payload.array_ptr.hash());
1377 std.hash.autoHash(&hasher, payload.index);
1378 },
1379 .decl_ref => {
1380 const payload = @fieldParentPtr(Payload.DeclRef, "base", self.ptr_otherwise);
1381 std.hash.autoHash(&hasher, payload.decl);
1382 },
1383 .function => {
1384 const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
1385 std.hash.autoHash(&hasher, payload.func);
1386 },
1387 .variable => {
1388 const payload = @fieldParentPtr(Payload.Variable, "base", self.ptr_otherwise);
1389 std.hash.autoHash(&hasher, payload.variable);
1390 },
1391 .@"error" => {
1392 const payload = @fieldParentPtr(Payload.Error, "base", self.ptr_otherwise);
1393 hasher.update(payload.name);
1394 std.hash.autoHash(&hasher, payload.value);
1395 },
1396 }
1397 return hasher.final();
1398 }
1399
12451400 /// Asserts the value is a pointer and dereferences it.
12461401 /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis.
12471402 pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value {
......@@ -1521,6 +1676,87 @@ pub const Value = extern union {
15211676 };
15221677 }
15231678
1679 /// Valid for all types. Asserts the value is not undefined.
1680 pub fn isType(self: Value) bool {
1681 return switch (self.tag()) {
1682 .ty,
1683 .int_type,
1684 .u8_type,
1685 .i8_type,
1686 .u16_type,
1687 .i16_type,
1688 .u32_type,
1689 .i32_type,
1690 .u64_type,
1691 .i64_type,
1692 .usize_type,
1693 .isize_type,
1694 .c_short_type,
1695 .c_ushort_type,
1696 .c_int_type,
1697 .c_uint_type,
1698 .c_long_type,
1699 .c_ulong_type,
1700 .c_longlong_type,
1701 .c_ulonglong_type,
1702 .c_longdouble_type,
1703 .f16_type,
1704 .f32_type,
1705 .f64_type,
1706 .f128_type,
1707 .c_void_type,
1708 .bool_type,
1709 .void_type,
1710 .type_type,
1711 .anyerror_type,
1712 .comptime_int_type,
1713 .comptime_float_type,
1714 .noreturn_type,
1715 .null_type,
1716 .undefined_type,
1717 .fn_noreturn_no_args_type,
1718 .fn_void_no_args_type,
1719 .fn_naked_noreturn_no_args_type,
1720 .fn_ccc_void_no_args_type,
1721 .single_const_pointer_to_comptime_int_type,
1722 .const_slice_u8_type,
1723 .enum_literal_type,
1724 .anyframe_type,
1725 .error_set,
1726 => true,
1727
1728 .zero,
1729 .one,
1730 .empty_array,
1731 .bool_true,
1732 .bool_false,
1733 .function,
1734 .variable,
1735 .int_u64,
1736 .int_i64,
1737 .int_big_positive,
1738 .int_big_negative,
1739 .ref_val,
1740 .decl_ref,
1741 .elem_ptr,
1742 .bytes,
1743 .repeated,
1744 .float_16,
1745 .float_32,
1746 .float_64,
1747 .float_128,
1748 .void_value,
1749 .enum_literal,
1750 .@"error",
1751 .empty_struct_value,
1752 .null_value,
1753 => false,
1754
1755 .undef => unreachable,
1756 .unreachable_value => unreachable,
1757 };
1758 }
1759
15241760 /// This type is not copyable since it may contain pointers to its inner data.
15251761 pub const Payload = struct {
15261762 tag: Tag,
......@@ -1655,3 +1891,18 @@ pub const Value = extern union {
16551891 limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb,
16561892 };
16571893};
1894
1895test "hash same value different representation" {
1896 const zero_1 = Value.initTag(.zero);
1897 var payload_1 = Value.Payload.Int_u64{ .int = 0 };
1898 const zero_2 = Value.initPayload(&payload_1.base);
1899 std.testing.expectEqual(zero_1.hash(), zero_2.hash());
1900
1901 var payload_2 = Value.Payload.Int_i64{ .int = 0 };
1902 const zero_3 = Value.initPayload(&payload_2.base);
1903 std.testing.expectEqual(zero_2.hash(), zero_3.hash());
1904
1905 var payload_3 = Value.Payload.IntBigNegative{ .limbs = &[_]std.math.big.Limb{0} };
1906 const zero_4 = Value.initPayload(&payload_3.base);
1907 std.testing.expectEqual(zero_3.hash(), zero_4.hash());
1908}
src/zir.zig+195-3
......@@ -85,8 +85,12 @@ pub const Inst = struct {
8585 block_comptime,
8686 /// Same as `block_flat` but additionally makes the inner instructions execute at comptime.
8787 block_comptime_flat,
88 /// Boolean AND. See also `bitand`.
89 booland,
8890 /// Boolean NOT. See also `bitnot`.
8991 boolnot,
92 /// Boolean OR. See also `bitor`.
93 boolor,
9094 /// Return a value from a `Block`.
9195 @"break",
9296 breakpoint,
......@@ -272,6 +276,12 @@ pub const Inst = struct {
272276 ensure_err_payload_void,
273277 /// Enum literal
274278 enum_literal,
279 /// A switch expression.
280 switchbr,
281 /// A range in a switch case, `lhs...rhs`.
282 /// Only checks that `lhs >= rhs` if they are ints, everything else is
283 /// validated by the .switch instruction.
284 switch_range,
275285
276286 pub fn Type(tag: Tag) type {
277287 return switch (tag) {
......@@ -327,6 +337,8 @@ pub const Inst = struct {
327337 .array_type,
328338 .bitand,
329339 .bitor,
340 .booland,
341 .boolor,
330342 .div,
331343 .mod_rem,
332344 .mul,
......@@ -351,6 +363,7 @@ pub const Inst = struct {
351363 .error_union_type,
352364 .merge_error_sets,
353365 .slice_start,
366 .switch_range,
354367 => BinOp,
355368
356369 .block,
......@@ -389,6 +402,7 @@ pub const Inst = struct {
389402 .enum_literal => EnumLiteral,
390403 .error_set => ErrorSet,
391404 .slice => Slice,
405 .switchbr => SwitchBr,
392406 };
393407 }
394408
......@@ -417,6 +431,8 @@ pub const Inst = struct {
417431 .block_comptime,
418432 .block_comptime_flat,
419433 .boolnot,
434 .booland,
435 .boolor,
420436 .breakpoint,
421437 .call,
422438 .cmp_lt,
......@@ -493,6 +509,7 @@ pub const Inst = struct {
493509 .slice,
494510 .slice_start,
495511 .import,
512 .switch_range,
496513 => false,
497514
498515 .@"break",
......@@ -504,6 +521,7 @@ pub const Inst = struct {
504521 .unreach_nocheck,
505522 .@"unreachable",
506523 .loop,
524 .switchbr,
507525 => true,
508526 };
509527 }
......@@ -987,6 +1005,33 @@ pub const Inst = struct {
9871005 sentinel: ?*Inst = null,
9881006 },
9891007 };
1008
1009 pub const SwitchBr = struct {
1010 pub const base_tag = Tag.switchbr;
1011 base: Inst,
1012
1013 positionals: struct {
1014 target_ptr: *Inst,
1015 /// List of all individual items and ranges
1016 items: []*Inst,
1017 cases: []Case,
1018 else_body: Module.Body,
1019 },
1020 kw_args: struct {
1021 /// Pointer to first range if such exists.
1022 range: ?*Inst = null,
1023 special_prong: enum {
1024 none,
1025 @"else",
1026 underscore,
1027 } = .none,
1028 },
1029
1030 pub const Case = struct {
1031 item: *Inst,
1032 body: Module.Body,
1033 };
1034 };
9901035};
9911036
9921037pub const ErrorMsg = struct {
......@@ -1218,8 +1263,8 @@ const Writer = struct {
12181263 bool => return stream.writeByte("01"[@boolToInt(param)]),
12191264 []u8, []const u8 => return stream.print("\"{Z}\"", .{param}),
12201265 BigIntConst, usize => return stream.print("{}", .{param}),
1221 TypedValue => unreachable, // this is a special case
1222 *IrModule.Decl => unreachable, // this is a special case
1266 TypedValue => return stream.print("TypedValue{{ .ty = {}, .val = {}}}", .{ param.ty, param.val }),
1267 *IrModule.Decl => return stream.print("Decl({s})", .{param.name}),
12231268 *Inst.Block => {
12241269 const name = self.block_table.get(param).?;
12251270 return stream.print("\"{Z}\"", .{name});
......@@ -1238,6 +1283,26 @@ const Writer = struct {
12381283 }
12391284 try stream.writeByte(']');
12401285 },
1286 []Inst.SwitchBr.Case => {
1287 if (param.len == 0) {
1288 return stream.writeAll("{}");
1289 }
1290 try stream.writeAll("{\n");
1291 for (param) |*case, i| {
1292 if (i != 0) {
1293 try stream.writeAll(",\n");
1294 }
1295 try stream.writeByteNTimes(' ', self.indent);
1296 self.indent += 2;
1297 try self.writeParamToStream(stream, &case.item);
1298 try stream.writeAll(" => ");
1299 try self.writeParamToStream(stream, &case.body);
1300 self.indent -= 2;
1301 }
1302 try stream.writeByte('\n');
1303 try stream.writeByteNTimes(' ', self.indent - 2);
1304 try stream.writeByte('}');
1305 },
12411306 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
12421307 }
12431308 }
......@@ -1650,6 +1715,26 @@ const Parser = struct {
16501715 try requireEatBytes(self, "]");
16511716 return strings.toOwnedSlice();
16521717 },
1718 []Inst.SwitchBr.Case => {
1719 try requireEatBytes(self, "{");
1720 skipSpace(self);
1721 if (eatByte(self, '}')) return &[0]Inst.SwitchBr.Case{};
1722
1723 var cases = std.ArrayList(Inst.SwitchBr.Case).init(&self.arena.allocator);
1724 while (true) {
1725 const cur = try cases.addOne();
1726 skipSpace(self);
1727 cur.item = try self.parseParameterGeneric(*Inst, body_ctx);
1728 skipSpace(self);
1729 try requireEatBytes(self, "=>");
1730 cur.body = try self.parseBody(body_ctx);
1731 skipSpace(self);
1732 if (!eatByte(self, ',')) break;
1733 }
1734 skipSpace(self);
1735 try requireEatBytes(self, "}");
1736 return cases.toOwnedSlice();
1737 },
16531738 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
16541739 }
16551740 return self.fail("TODO parse parameter {}", .{@typeName(T)});
......@@ -1747,7 +1832,7 @@ pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void {
17471832 .arena = std.heap.ArenaAllocator.init(allocator),
17481833 .old_module = &old_module,
17491834 .next_auto_name = 0,
1750 .names = std.StringHashMap(void).init(allocator),
1835 .names = std.StringArrayHashMap(void).init(allocator),
17511836 .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator),
17521837 .indent = 0,
17531838 .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator),
......@@ -2244,6 +2329,8 @@ const EmitZIR = struct {
22442329 .cmp_gte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gte).?, .cmp_gte),
22452330 .cmp_gt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gt).?, .cmp_gt),
22462331 .cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq),
2332 .booland => try self.emitBinOp(inst.src, new_body, inst.castTag(.booland).?, .booland),
2333 .boolor => try self.emitBinOp(inst.src, new_body, inst.castTag(.boolor).?, .boolor),
22472334
22482335 .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, .bitcast),
22492336 .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast),
......@@ -2470,7 +2557,63 @@ const EmitZIR = struct {
24702557 };
24712558 break :blk &new_inst.base;
24722559 },
2560 .switchbr => blk: {
2561 const old_inst = inst.castTag(.switchbr).?;
2562 const cases = try self.arena.allocator.alloc(Inst.SwitchBr.Case, old_inst.cases.len);
2563 const new_inst = try self.arena.allocator.create(Inst.SwitchBr);
2564 new_inst.* = .{
2565 .base = .{
2566 .src = inst.src,
2567 .tag = Inst.SwitchBr.base_tag,
2568 },
2569 .positionals = .{
2570 .target_ptr = try self.resolveInst(new_body, old_inst.target_ptr),
2571 .cases = cases,
2572 .items = &[_]*Inst{}, // TODO this should actually be populated
2573 .else_body = undefined, // populated below
2574 },
2575 .kw_args = .{},
2576 };
24732577
2578 var body_tmp = std.ArrayList(*Inst).init(self.allocator);
2579 defer body_tmp.deinit();
2580
2581 for (old_inst.cases) |*case, i| {
2582 body_tmp.items.len = 0;
2583
2584 const case_deaths = try self.arena.allocator.alloc(*Inst, old_inst.caseDeaths(i).len);
2585 for (old_inst.caseDeaths(i)) |death, j| {
2586 case_deaths[j] = try self.resolveInst(new_body, death);
2587 }
2588 try self.body_metadata.put(&cases[i].body, .{ .deaths = case_deaths });
2589
2590 try self.emitBody(case.body, inst_table, &body_tmp);
2591 const item = (try self.emitTypedValue(inst.src, .{
2592 .ty = old_inst.target_ptr.ty.elemType(),
2593 .val = case.item,
2594 })).inst;
2595
2596 cases[i] = .{
2597 .item = item,
2598 .body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) },
2599 };
2600 }
2601 { // else
2602 const else_deaths = try self.arena.allocator.alloc(*Inst, old_inst.elseDeaths().len);
2603 for (old_inst.elseDeaths()) |death, j| {
2604 else_deaths[j] = try self.resolveInst(new_body, death);
2605 }
2606 try self.body_metadata.put(&new_inst.positionals.else_body, .{ .deaths = else_deaths });
2607
2608 body_tmp.items.len = 0;
2609 try self.emitBody(old_inst.else_body, inst_table, &body_tmp);
2610 new_inst.positionals.else_body = .{
2611 .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items),
2612 };
2613 }
2614
2615 break :blk &new_inst.base;
2616 },
24742617 .varptr => @panic("TODO"),
24752618 };
24762619 try self.metadata.put(new_inst, .{
......@@ -2703,3 +2846,52 @@ const EmitZIR = struct {
27032846 return decl;
27042847 }
27052848};
2849
2850/// For debugging purposes, like dumpFn but for unanalyzed zir blocks
2851pub fn dumpZir(allocator: *Allocator, kind: []const u8, decl_name: [*:0]const u8, instructions: []*Inst) !void {
2852 var fib = std.heap.FixedBufferAllocator.init(&[_]u8{});
2853 var module = Module{
2854 .decls = &[_]*Decl{},
2855 .arena = std.heap.ArenaAllocator.init(&fib.allocator),
2856 .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(&fib.allocator),
2857 .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(&fib.allocator),
2858 };
2859 var write = Writer{
2860 .module = &module,
2861 .inst_table = InstPtrTable.init(allocator),
2862 .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator),
2863 .loop_table = std.AutoHashMap(*Inst.Loop, []const u8).init(allocator),
2864 .arena = std.heap.ArenaAllocator.init(allocator),
2865 .indent = 4,
2866 .next_instr_index = 0,
2867 };
2868 defer write.arena.deinit();
2869 defer write.inst_table.deinit();
2870 defer write.block_table.deinit();
2871 defer write.loop_table.deinit();
2872
2873 try write.inst_table.ensureCapacity(@intCast(u32, instructions.len));
2874
2875 const stderr = std.io.getStdErr().outStream();
2876 try stderr.print("{} {s} {{ // unanalyzed\n", .{ kind, decl_name });
2877
2878 for (instructions) |inst| {
2879 const my_i = write.next_instr_index;
2880 write.next_instr_index += 1;
2881
2882 if (inst.cast(Inst.Block)) |block| {
2883 const name = try std.fmt.allocPrint(&write.arena.allocator, "label_{}", .{my_i});
2884 try write.block_table.put(block, name);
2885 } else if (inst.cast(Inst.Loop)) |loop| {
2886 const name = try std.fmt.allocPrint(&write.arena.allocator, "loop_{}", .{my_i});
2887 try write.loop_table.put(loop, name);
2888 }
2889
2890 try write.inst_table.putNoClobber(inst, .{ .inst = inst, .index = my_i, .name = "inst" });
2891 try stderr.print(" %{} ", .{my_i});
2892 try write.writeInstToStream(stderr, inst);
2893 try stderr.writeByte('\n');
2894 }
2895
2896 try stderr.print("}} // {} {s}\n\n", .{ kind, decl_name });
2897}
src/zir_sema.zig+256-7
......@@ -135,6 +135,10 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
135135 .slice => return analyzeInstSlice(mod, scope, old_inst.castTag(.slice).?),
136136 .slice_start => return analyzeInstSliceStart(mod, scope, old_inst.castTag(.slice_start).?),
137137 .import => return analyzeInstImport(mod, scope, old_inst.castTag(.import).?),
138 .switchbr => return analyzeInstSwitchBr(mod, scope, old_inst.castTag(.switchbr).?),
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).?),
138142 }
139143}
140144
......@@ -551,10 +555,13 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c
551555
552556 try analyzeBody(mod, &child_block.base, inst.positionals.body);
553557
554 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);
555 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
558 try parent_block.instructions.appendSlice(mod.gpa, child_block.instructions.items);
556559
557 return copied_instructions[copied_instructions.len - 1];
560 // comptime blocks won't generate any runtime values
561 if (child_block.instructions.items.len == 0)
562 return mod.constVoid(scope, inst.base.src);
563
564 return parent_block.instructions.items[parent_block.instructions.items.len - 1];
558565}
559566
560567fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst {
......@@ -1204,13 +1211,233 @@ fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn
12041211 return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null);
12051212}
12061213
1214fn analyzeInstSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
1215 const start = try resolveInst(mod, scope, inst.positionals.lhs);
1216 const end = try resolveInst(mod, scope, inst.positionals.rhs);
1217
1218 switch (start.ty.zigTypeTag()) {
1219 .Int, .ComptimeInt => {},
1220 else => return mod.constVoid(scope, inst.base.src),
1221 }
1222 switch (end.ty.zigTypeTag()) {
1223 .Int, .ComptimeInt => {},
1224 else => return mod.constVoid(scope, inst.base.src),
1225 }
1226 if (start.value()) |start_val| {
1227 if (end.value()) |end_val| {
1228 if (start_val.compare(.gte, end_val)) {
1229 return mod.fail(scope, inst.base.src, "range start value must be smaller than the end value", .{});
1230 }
1231 }
1232 }
1233 return mod.constVoid(scope, inst.base.src);
1234}
1235
1236fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError!*Inst {
1237 const target_ptr = try resolveInst(mod, scope, inst.positionals.target_ptr);
1238 const target = try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target_ptr.src);
1239 try validateSwitch(mod, scope, target, inst);
1240
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 }
1255
1256 if (inst.positionals.cases.len == 0) {
1257 // no cases just analyze else_branch
1258 try analyzeBody(mod, scope, inst.positionals.else_body);
1259 return mod.constNoReturn(scope, inst.base.src);
1260 }
1261
1262 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
1263 const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len);
1264
1265 var case_block: Scope.Block = .{
1266 .parent = parent_block,
1267 .func = parent_block.func,
1268 .decl = parent_block.decl,
1269 .instructions = .{},
1270 .arena = parent_block.arena,
1271 .is_comptime = parent_block.is_comptime,
1272 };
1273 defer case_block.instructions.deinit(mod.gpa);
1274
1275 for (inst.positionals.cases) |case, i| {
1276 // Reset without freeing.
1277 case_block.instructions.items.len = 0;
1278
1279 const resolved = try resolveInst(mod, scope, case.item);
1280 const casted = try mod.coerce(scope, target.ty, resolved);
1281 const item = try mod.resolveConstValue(scope, casted);
1282
1283 try analyzeBody(mod, &case_block.base, case.body);
1284
1285 cases[i] = .{
1286 .item = item,
1287 .body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items) },
1288 };
1289 }
1290
1291 case_block.instructions.items.len = 0;
1292 try analyzeBody(mod, &case_block.base, inst.positionals.else_body);
1293
1294 const else_body: ir.Body = .{
1295 .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items),
1296 };
1297
1298 return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases, else_body);
1299}
1300
1301fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void {
1302 // validate usage of '_' prongs
1303 if (inst.kw_args.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) {
1304 return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{});
1305 // TODO notes "'_' prong here" inst.positionals.cases[last].src
1306 }
1307
1308 // check that target type supports ranges
1309 if (inst.kw_args.range) |range_inst| {
1310 switch (target.ty.zigTypeTag()) {
1311 .Int, .ComptimeInt => {},
1312 else => {
1313 return mod.fail(scope, target.src, "ranges not allowed when switching on type {}", .{target.ty});
1314 // TODO notes "range used here" range_inst.src
1315 },
1316 }
1317 }
1318
1319 // validate for duplicate items/missing else prong
1320 switch (target.ty.zigTypeTag()) {
1321 .Enum => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Enum", .{}),
1322 .ErrorSet => return mod.fail(scope, inst.base.src, "TODO validateSwitch .ErrorSet", .{}),
1323 .Union => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Union", .{}),
1324 .Int, .ComptimeInt => {
1325 var range_set = @import("RangeSet.zig").init(mod.gpa);
1326 defer range_set.deinit();
1327
1328 for (inst.positionals.items) |item| {
1329 const maybe_src = if (item.castTag(.switch_range)) |range| blk: {
1330 const start_resolved = try resolveInst(mod, scope, range.positionals.lhs);
1331 const start_casted = try mod.coerce(scope, target.ty, start_resolved);
1332 const end_resolved = try resolveInst(mod, scope, range.positionals.rhs);
1333 const end_casted = try mod.coerce(scope, target.ty, end_resolved);
1334
1335 break :blk try range_set.add(
1336 try mod.resolveConstValue(scope, start_casted),
1337 try mod.resolveConstValue(scope, end_casted),
1338 item.src,
1339 );
1340 } else blk: {
1341 const resolved = try resolveInst(mod, scope, item);
1342 const casted = try mod.coerce(scope, target.ty, resolved);
1343 const value = try mod.resolveConstValue(scope, casted);
1344 break :blk try range_set.add(value, value, item.src);
1345 };
1346
1347 if (maybe_src) |previous_src| {
1348 return mod.fail(scope, item.src, "duplicate switch value", .{});
1349 // TODO notes "previous value is here" previous_src
1350 }
1351 }
1352
1353 if (target.ty.zigTypeTag() == .Int) {
1354 var arena = std.heap.ArenaAllocator.init(mod.gpa);
1355 defer arena.deinit();
1356
1357 const start = try target.ty.minInt(&arena, mod.getTarget());
1358 const end = try target.ty.maxInt(&arena, mod.getTarget());
1359 if (try range_set.spans(start, end)) {
1360 if (inst.kw_args.special_prong == .@"else") {
1361 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});
1362 }
1363 return;
1364 }
1365 }
1366
1367 if (inst.kw_args.special_prong != .@"else") {
1368 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});
1369 }
1370 },
1371 .Bool => {
1372 var true_count: u8 = 0;
1373 var false_count: u8 = 0;
1374 for (inst.positionals.items) |item| {
1375 const resolved = try resolveInst(mod, scope, item);
1376 const casted = try mod.coerce(scope, Type.initTag(.bool), resolved);
1377 if ((try mod.resolveConstValue(scope, casted)).toBool()) {
1378 true_count += 1;
1379 } else {
1380 false_count += 1;
1381 }
1382
1383 if (true_count + false_count > 2) {
1384 return mod.fail(scope, item.src, "duplicate switch value", .{});
1385 }
1386 }
1387 if ((true_count + false_count < 2) and inst.kw_args.special_prong != .@"else") {
1388 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});
1389 }
1390 if ((true_count + false_count == 2) and inst.kw_args.special_prong == .@"else") {
1391 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});
1392 }
1393 },
1394 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {
1395 if (inst.kw_args.special_prong != .@"else") {
1396 return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty});
1397 }
1398
1399 var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(mod.gpa);
1400 defer seen_values.deinit();
1401
1402 for (inst.positionals.items) |item| {
1403 const resolved = try resolveInst(mod, scope, item);
1404 const casted = try mod.coerce(scope, target.ty, resolved);
1405 const val = try mod.resolveConstValue(scope, casted);
1406
1407 if (try seen_values.fetchPut(val, item.src)) |prev| {
1408 return mod.fail(scope, item.src, "duplicate switch value", .{});
1409 // TODO notes "previous value here" prev.value
1410 }
1411 }
1412 },
1413
1414 .ErrorUnion,
1415 .NoReturn,
1416 .Array,
1417 .Struct,
1418 .Undefined,
1419 .Null,
1420 .Optional,
1421 .BoundFn,
1422 .Opaque,
1423 .Vector,
1424 .Frame,
1425 .AnyFrame,
1426 .ComptimeFloat,
1427 .Float,
1428 => {
1429 return mod.fail(scope, target.src, "invalid switch target type '{}'", .{target.ty});
1430 },
1431 }
1432}
1433
12071434fn analyzeInstImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
12081435 const operand = try resolveConstString(mod, scope, inst.positionals.operand);
12091436
12101437 const file_scope = mod.analyzeImport(scope, inst.base.src, operand) catch |err| switch (err) {
1211 // error.ImportOutsidePkgPath => {
1212 // return mod.fail(scope, inst.base.src, "import of file outside package path: '{}'", .{operand});
1213 // },
1438 error.ImportOutsidePkgPath => {
1439 return mod.fail(scope, inst.base.src, "import of file outside package path: '{}'", .{operand});
1440 },
12141441 error.FileNotFound => {
12151442 return mod.fail(scope, inst.base.src, "unable to find '{}'", .{operand});
12161443 },
......@@ -1456,6 +1683,28 @@ fn analyzeInstBoolNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerEr
14561683 return mod.addUnOp(b, inst.base.src, bool_type, .not, operand);
14571684}
14581685
1686fn analyzeInstBoolOp(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
1687 const bool_type = Type.initTag(.bool);
1688 const uncasted_lhs = try resolveInst(mod, scope, inst.positionals.lhs);
1689 const lhs = try mod.coerce(scope, bool_type, uncasted_lhs);
1690 const uncasted_rhs = try resolveInst(mod, scope, inst.positionals.rhs);
1691 const rhs = try mod.coerce(scope, bool_type, uncasted_rhs);
1692
1693 const is_bool_or = inst.base.tag == .boolor;
1694
1695 if (lhs.value()) |lhs_val| {
1696 if (rhs.value()) |rhs_val| {
1697 if (is_bool_or) {
1698 return mod.constBool(scope, inst.base.src, lhs_val.toBool() or rhs_val.toBool());
1699 } else {
1700 return mod.constBool(scope, inst.base.src, lhs_val.toBool() and rhs_val.toBool());
1701 }
1702 }
1703 }
1704 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
1705 return mod.addBinOp(b, inst.base.src, bool_type, if (is_bool_or) .boolor else .booland, lhs, rhs);
1706}
1707
14591708fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst {
14601709 const operand = try resolveInst(mod, scope, inst.positionals.operand);
14611710 return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic);
......@@ -1473,7 +1722,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
14731722 if (try mod.resolveDefinedValue(scope, cond)) |cond_val| {
14741723 const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;
14751724 try analyzeBody(mod, scope, body.*);
1476 return mod.constVoid(scope, inst.base.src);
1725 return mod.constNoReturn(scope, inst.base.src);
14771726 }
14781727
14791728 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
test/stage2/test.zig+37
......@@ -974,6 +974,43 @@ pub fn addCases(ctx: *TestContext) !void {
974974 ,
975975 "hello\nhello\nhello\nhello\nhello\n",
976976 );
977
978 // comptime switch
979
980 // Basic for loop
981 case.addCompareOutput(
982 \\pub export fn _start() noreturn {
983 \\ assert(foo() == 1);
984 \\ exit();
985 \\}
986 \\
987 \\fn foo() u32 {
988 \\ const a: comptime_int = 1;
989 \\ var b: u32 = 0;
990 \\ switch (a) {
991 \\ 1 => b = 1,
992 \\ 2 => b = 2,
993 \\ else => unreachable,
994 \\ }
995 \\ return b;
996 \\}
997 \\
998 \\pub fn assert(ok: bool) void {
999 \\ if (!ok) unreachable; // assertion failure
1000 \\}
1001 \\
1002 \\fn exit() noreturn {
1003 \\ asm volatile ("syscall"
1004 \\ :
1005 \\ : [number] "{rax}" (231),
1006 \\ [arg1] "{rdi}" (0)
1007 \\ : "rcx", "r11", "memory"
1008 \\ );
1009 \\ unreachable;
1010 \\}
1011 ,
1012 "",
1013 );
9771014 }
9781015
9791016 {