authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-10-16 17:01:05+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-10-30 15:58:12+02:00
log7db17a2d89c866efadf9a487acf2f9b0535ba859
treeccbb5f3b3cb15162c7e6ec0880f1673e7f8b7b9d
parent95467f324909055d014e989ca9be7b0bb04237c4
signaturelock-open Commit is signed but in an unrecognized format.

stage2: redesign switchbr

Switchbr now only handles single item prongs. Ranges and multi item prongs are checked with condbrs after the switchbr.

5 files changed, 139 insertions(+), 153 deletions(-)

src/Module.zig+1-3
...@@ -2122,18 +2122,16 @@ pub fn addSwitchBr(...@@ -2122,18 +2122,16 @@ pub fn addSwitchBr(
2122 src: usize,2122 src: usize,
2123 target_ptr: *Inst,2123 target_ptr: *Inst,
2124 cases: []Inst.SwitchBr.Case,2124 cases: []Inst.SwitchBr.Case,
2125 else_body: ?Module.Body,
2126) !*Inst {2125) !*Inst {
2127 const inst = try block.arena.create(Inst.SwitchBr);2126 const inst = try block.arena.create(Inst.SwitchBr);
2128 inst.* = .{2127 inst.* = .{
2129 .base = .{2128 .base = .{
2130 .tag = .switchbr,2129 .tag = .switchbr,
2131 .ty = Type.initTag(.noreturn),2130 .ty = Type.initTag(.void),
2132 .src = src,2131 .src = src,
2133 },2132 },
2134 .target_ptr = target_ptr,2133 .target_ptr = target_ptr,
2135 .cases = cases,2134 .cases = cases,
2136 .@"else" = else_body,
2137 };2135 };
2138 try block.instructions.append(self.gpa, &inst.base);2136 try block.instructions.append(self.gpa, &inst.base);
2139 return &inst.base;2137 return &inst.base;
src/astgen.zig+82-49
...@@ -1570,16 +1570,33 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1570,16 +1570,33 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1570 };1570 };
1571 defer block_scope.instructions.deinit(mod.gpa);1571 defer block_scope.instructions.deinit(mod.gpa);
15721572
1573 var item_scope: Scope.GenZIR = .{
1574 .parent = scope,
1575 .decl = scope.decl().?,
1576 .arena = scope.arena(),
1577 .instructions = .{},
1578 };
1579 defer item_scope.instructions.deinit(mod.gpa);
1580
1573 const tree = scope.tree();1581 const tree = scope.tree();
1574 const switch_src = tree.token_locs[switch_node.switch_token].start;1582 const switch_src = tree.token_locs[switch_node.switch_token].start;
1575 const target_ptr = try expr(mod, &block_scope.base, .ref, switch_node.expr);1583 const target_ptr = try expr(mod, &block_scope.base, .ref, switch_node.expr);
1576 const cases = try scope.arena().alloc(zir.Inst.SwitchBr.Case, switch_node.cases_len);1584 // Add the switch instruction here so that it comes before any range checks.
1577 var kw_args: std.meta.fieldInfo(zir.Inst.SwitchBr, "kw_args").field_type = .{};1585 const switch_inst = (try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{
1586 .target_ptr = target_ptr,
1587 .cases = undefined, // populated below
1588 .items = &[_]*zir.Inst{}, // populated below
1589 }, .{})).castTag(.switchbr).?;
1590
1591 var items = std.ArrayList(*zir.Inst).init(mod.gpa);
1592 defer items.deinit();
1593 var cases = std.ArrayList(zir.Inst.SwitchBr.Case).init(mod.gpa);
1594 defer cases.deinit();
15781595
1579 // first we gather all the switch items and check else/'_' prongs1596 // first we gather all the switch items and check else/'_' prongs
1580 var case_index: usize = 0;
1581 var else_src: ?usize = null;1597 var else_src: ?usize = null;
1582 var underscore_src: ?usize = null;1598 var underscore_src: ?usize = null;
1599 var range_inst: ?*zir.Inst = null;
1583 for (switch_node.cases()) |uncasted_case| {1600 for (switch_node.cases()) |uncasted_case| {
1584 const case = uncasted_case.castTag(.SwitchCase).?;1601 const case = uncasted_case.castTag(.SwitchCase).?;
1585 const case_src = tree.token_locs[case.firstToken()].start;1602 const case_src = tree.token_locs[case.firstToken()].start;
...@@ -1593,12 +1610,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1593,12 +1610,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1593 return mod.fail(scope, case_src, "multiple else prongs in switch expression", .{});1610 return mod.fail(scope, case_src, "multiple else prongs in switch expression", .{});
1594 // TODO notes "previous else prong is here"1611 // TODO notes "previous else prong is here"
1595 }1612 }
1596 kw_args.special_case = .@"else";
1597 else_src = case_src;1613 else_src = case_src;
1598 cases[cases.len - 1] = .{
1599 .items = &[0]*zir.Inst{},
1600 .body = undefined, // filled below
1601 };
1602 continue;1614 continue;
1603 } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and1615 } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and
1604 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))1616 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))
...@@ -1607,48 +1619,44 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1607,48 +1619,44 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1607 return mod.fail(scope, case_src, "multiple '_' prongs in switch expression", .{});1619 return mod.fail(scope, case_src, "multiple '_' prongs in switch expression", .{});
1608 // TODO notes "previous '_' prong is here"1620 // TODO notes "previous '_' prong is here"
1609 }1621 }
1610 kw_args.special_case = .underscore;
1611 underscore_src = case_src;1622 underscore_src = case_src;
1612 cases[cases.len - 1] = .{
1613 .items = &[0]*zir.Inst{},
1614 .body = undefined, // filled below
1615 };
1616 continue;1623 continue;
1617 }1624 }
16181625
1619 if (else_src) |some_else| {1626 if (else_src) |some_else| {
1620 if (underscore_src) |some_underscore| {1627 if (underscore_src) |some_underscore| {
1621 return mod.fail(scope, case_src, "else and '_' prong in switch expression", .{});1628 return mod.fail(scope, switch_src, "else and '_' prong in switch expression", .{});
1622 // TODO notes "else prong is here"1629 // TODO notes "else prong is here"
1623 // TODO notes "'_' prong is here"1630 // TODO notes "'_' prong is here"
1624 }1631 }
1625 }1632 }
16261633
1627 // Regular case, we need to fill `items`.1634 // TODO and not range
1628 const items = try block_scope.arena.alloc(*zir.Inst, case.items_len);1635 if (case.items_len == 1) {
1629 for (case.items()) |item, i| {1636 const item = try expr(mod, &item_scope.base, .none, case.items()[0]);
1630 if (item.castTag(.Range)) |range| {1637 try cases.append(.{
1631 items[i] = try switchRange(mod, &block_scope.base, range);1638 .item = item,
1632 if (kw_args.support_range == null)1639 .body = undefined, // populated below
1633 kw_args.support_range = items[i];1640 });
1634 } else {1641 continue;
1635 items[i] = try expr(mod, &block_scope.base, .none, item);
1636 }
1637 }1642 }
1638 cases[case_index] = .{1643 return mod.fail(scope, case_src, "TODO switch ranges", .{});
1639 .items = items,
1640 .body = undefined, // filled below
1641 };
1642 case_index += 1;
1643 }1644 }
16441645
1645 // Then we add the switch instruction to finish the block.1646 // Actually populate switch instruction values.
1646 _ = try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{1647 if (else_src != null) switch_inst.kw_args.special_prong = .@"else";
1647 .target_ptr = target_ptr,1648 if (underscore_src != null) switch_inst.kw_args.special_prong = .underscore;
1648 .cases = cases,1649 switch_inst.positionals.cases = try block_scope.arena.dupe(zir.Inst.SwitchBr.Case, cases.items);
1649 }, kw_args);1650 switch_inst.positionals.items = try block_scope.arena.dupe(*zir.Inst, items.items);
1651 switch_inst.kw_args.range = range_inst;
1652
1653 // Add comptime block containing all prong items first,
1654 _ = try addZIRInstBlock(mod, scope, switch_src, .block_comptime_flat, .{
1655 .instructions = try block_scope.arena.dupe(*zir.Inst, item_scope.instructions.items),
1656 });
1657 // then add block containing the switch.
1650 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{1658 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
1651 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),1659 .instructions = undefined, // populated below
1652 });1660 });
16531661
1654 // Most result location types can be forwarded directly; however1662 // Most result location types can be forwarded directly; however
...@@ -1668,39 +1676,64 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1668,39 +1676,64 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1668 defer case_scope.instructions.deinit(mod.gpa);1676 defer case_scope.instructions.deinit(mod.gpa);
16691677
1670 // And finally we fill generate the bodies of each case.1678 // And finally we fill generate the bodies of each case.
1671 case_index = 0;1679 var case_index: usize = 0;
1680 var special_case: ?*ast.Node.SwitchCase = null;
1672 for (switch_node.cases()) |uncasted_case| {1681 for (switch_node.cases()) |uncasted_case| {
1673 const case = uncasted_case.castTag(.SwitchCase).?;1682 const case = uncasted_case.castTag(.SwitchCase).?;
1674 const case_src = tree.token_locs[case.firstToken()].start;1683 const case_src = tree.token_locs[case.firstToken()].start;
1675 // reset without freeing to reduce allocations.1684 // reset without freeing to reduce allocations.
1676 defer case_scope.instructions.items.len = 0;1685 defer case_scope.instructions.items.len = 0;
16771686
1678 // What index in positionals.cases should this one be placed at.
1679 // For special cases it will be at the end.
1680 var cur_index = case_index;
1681 if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) {1687 if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) {
1682 // validated above1688 // validated earlier
1683 cur_index = cases.len - 1;1689 special_case = case;
1690 continue;
1684 } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and1691 } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and
1685 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))1692 mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))
1686 {1693 {
1687 // validated above1694 // validated earlier
1688 cur_index = cases.len - 1;1695 special_case = case;
1696 continue;
1689 }1697 }
16901698
1691 // Generate the body of this case.1699 if (case.items_len == 1) {
1692 const case_body = try expr(mod, &case_scope.base, case_rl, case.expr);1700 // Generate the body of this case.
1701 const case_body = try expr(mod, &case_scope.base, case_rl, case.expr);
1702 if (!case_body.tag.isNoReturn()) {
1703 _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.Break, .{
1704 .block = block,
1705 .operand = case_body,
1706 }, .{});
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 }
1716
1717 // Generate else block or a break last to finish the block.
1718 if (special_case) |case| {
1719 const case_src = tree.token_locs[case.firstToken()].start;
1720 const case_body = try expr(mod, &block_scope.base, case_rl, case.expr);
1693 if (!case_body.tag.isNoReturn()) {1721 if (!case_body.tag.isNoReturn()) {
1694 _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.Break, .{1722 _ = try addZIRInst(mod, &block_scope.base, case_src, zir.Inst.Break, .{
1695 .block = block,1723 .block = block,
1696 .operand = case_body,1724 .operand = case_body,
1697 }, .{});1725 }, .{});
1698 }1726 }
1699 cases[cur_index].body = .{1727 } else {
1700 .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),1728 _ = try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.BreakVoid, .{
1701 };1729 .block = block,
1730 }, .{});
1702 }1731 }
17031732
1733 // Set block instructions now that it is finished.
1734 block.positionals.body = .{
1735 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
1736 };
1704 return &block.base;1737 return &block.base;
1705}1738}
17061739
src/ir.zig+1-7
...@@ -467,15 +467,12 @@ pub const Inst = struct {...@@ -467,15 +467,12 @@ pub const Inst = struct {
467 base: Inst,467 base: Inst,
468 target_ptr: *Inst,468 target_ptr: *Inst,
469 cases: []Case,469 cases: []Case,
470 @"else": ?Body,
471 /// Set of instructions whose lifetimes end at the start of one of the cases.470 /// Set of instructions whose lifetimes end at the start of one of the cases.
472 /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... , case_n_count ... else_count].471 /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... , case_n_count ... else_count].
473 deaths: [*]*Inst = undefined,472 deaths: [*]*Inst = undefined,
474 else_index: u32 = 0,
475 else_deaths: u32 = 0,
476473
477 pub const Case = struct {474 pub const Case = struct {
478 items: []Value,475 item: Value,
479 body: Body,476 body: Body,
480 index: u32 = 0,477 index: u32 = 0,
481 deaths: u32 = 0,478 deaths: u32 = 0,
...@@ -497,9 +494,6 @@ pub const Inst = struct {...@@ -497,9 +494,6 @@ pub const Inst = struct {
497 const case = self.cases[case_index];494 const case = self.cases[case_index];
498 return (self.deaths + case.index)[0..case.deaths];495 return (self.deaths + case.index)[0..case.deaths];
499 }496 }
500 pub fn elseDeaths(self: *const SwitchBr) []*Inst {
501 return (self.deaths + self.else_deaths)[0..self.else_deaths];
502 }
503 };497 };
504};498};
505499
src/zir.zig+19-35
...@@ -501,7 +501,7 @@ pub const Inst = struct {...@@ -501,7 +501,7 @@ pub const Inst = struct {
501 .slice,501 .slice,
502 .slice_start,502 .slice_start,
503 .import,503 .import,
504 .switch_range,504 .switchbr,
505 => false,505 => false,
506506
507 .@"break",507 .@"break",
...@@ -513,7 +513,7 @@ pub const Inst = struct {...@@ -513,7 +513,7 @@ pub const Inst = struct {
513 .unreach_nocheck,513 .unreach_nocheck,
514 .@"unreachable",514 .@"unreachable",
515 .loop,515 .loop,
516 .switchbr,516 .switch_range,
517 => true,517 => true,
518 };518 };
519 }519 }
...@@ -1005,22 +1005,21 @@ pub const Inst = struct {...@@ -1005,22 +1005,21 @@ pub const Inst = struct {
1005 positionals: struct {1005 positionals: struct {
1006 target_ptr: *Inst,1006 target_ptr: *Inst,
1007 cases: []Case,1007 cases: []Case,
1008 /// List of all individual items and ranges
1009 items: []*Inst,
1008 },1010 },
1009 kw_args: struct {1011 kw_args: struct {
1010 /// if not null target must support ranges, (be int)1012 /// Pointer to first range if such exists.
1011 support_range: ?*Inst = null,1013 range: ?*Inst = null,
1012 special_case: enum {1014 special_prong: enum {
1013 /// all of positionals.cases are regular cases
1014 none,1015 none,
1015 /// last case in positionals.cases is an else case
1016 @"else",1016 @"else",
1017 /// last case in positionals.cases is an underscore case
1018 underscore,1017 underscore,
1019 } = .none,1018 } = .none,
1020 },1019 },
10211020
1022 pub const Case = struct {1021 pub const Case = struct {
1023 items: []*Inst,1022 item: *Inst,
1024 body: Module.Body,1023 body: Module.Body,
1025 };1024 };
1026 };1025 };
...@@ -1286,7 +1285,7 @@ const Writer = struct {...@@ -1286,7 +1285,7 @@ const Writer = struct {
1286 }1285 }
1287 try stream.writeByteNTimes(' ', self.indent);1286 try stream.writeByteNTimes(' ', self.indent);
1288 self.indent += 2;1287 self.indent += 2;
1289 try self.writeParamToStream(stream, &case.items);1288 try self.writeParamToStream(stream, &case.item);
1290 try stream.writeAll(" => ");1289 try stream.writeAll(" => ");
1291 try self.writeParamToStream(stream, &case.body);1290 try self.writeParamToStream(stream, &case.body);
1292 self.indent -= 2;1291 self.indent -= 2;
...@@ -1716,7 +1715,7 @@ const Parser = struct {...@@ -1716,7 +1715,7 @@ const Parser = struct {
1716 while (true) {1715 while (true) {
1717 const cur = try cases.addOne();1716 const cur = try cases.addOne();
1718 skipSpace(self);1717 skipSpace(self);
1719 cur.items = try self.parseParameterGeneric([]*Inst, body_ctx);1718 cur.item = try self.parseParameterGeneric(*Inst, body_ctx);
1720 skipSpace(self);1719 skipSpace(self);
1721 try requireEatBytes(self, "=>");1720 try requireEatBytes(self, "=>");
1722 cur.body = try self.parseBody(body_ctx);1721 cur.body = try self.parseBody(body_ctx);
...@@ -2549,8 +2548,7 @@ const EmitZIR = struct {...@@ -2549,8 +2548,7 @@ const EmitZIR = struct {
2549 },2548 },
2550 .switchbr => blk: {2549 .switchbr => blk: {
2551 const old_inst = inst.castTag(.switchbr).?;2550 const old_inst = inst.castTag(.switchbr).?;
2552 const case_count = old_inst.cases.len + @boolToInt(old_inst.@"else" != null);2551 const cases = try self.arena.allocator.alloc(Inst.SwitchBr.Case, old_inst.cases.len);
2553 const cases = try self.arena.allocator.alloc(Inst.SwitchBr.Case, case_count);
2554 const new_inst = try self.arena.allocator.create(Inst.SwitchBr);2552 const new_inst = try self.arena.allocator.create(Inst.SwitchBr);
2555 new_inst.* = .{2553 new_inst.* = .{
2556 .base = .{2554 .base = .{
...@@ -2560,11 +2558,9 @@ const EmitZIR = struct {...@@ -2560,11 +2558,9 @@ const EmitZIR = struct {
2560 .positionals = .{2558 .positionals = .{
2561 .target_ptr = try self.resolveInst(new_body, old_inst.target_ptr),2559 .target_ptr = try self.resolveInst(new_body, old_inst.target_ptr),
2562 .cases = cases,2560 .cases = cases,
2561 .items = &[_]*Inst{}, // TODO this should actually be populated
2563 },2562 },
2564 .kw_args = .{2563 .kw_args = .{},
2565 .special_case = if (old_inst.@"else" != null) .@"else" else .none,
2566 .support_range = null,
2567 },
2568 };2564 };
25692565
2570 var body_tmp = std.ArrayList(*Inst).init(self.allocator);2566 var body_tmp = std.ArrayList(*Inst).init(self.allocator);
...@@ -2574,25 +2570,13 @@ const EmitZIR = struct {...@@ -2574,25 +2570,13 @@ const EmitZIR = struct {
2574 body_tmp.items.len = 0;2570 body_tmp.items.len = 0;
25752571
2576 try self.emitBody(case.body, inst_table, &body_tmp);2572 try self.emitBody(case.body, inst_table, &body_tmp);
2577 const items = try self.arena.allocator.alloc(*Inst, case.items.len);2573 const item = (try self.emitTypedValue(inst.src, .{
2578 for (case.items) |item, j| {2574 .ty = old_inst.target_ptr.ty.elemType(),
2579 items[j] = (try self.emitTypedValue(inst.src, .{2575 .val = case.item,
2580 .ty = old_inst.target_ptr.ty.elemType(),2576 })).inst;
2581 .val = item,
2582 })).inst;
2583 }
25842577
2585 cases[i] = .{2578 cases[i] = .{
2586 .items = items,2579 .item = item,
2587 .body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) },
2588 };
2589 }
2590 if (old_inst.@"else") |some| {
2591 body_tmp.items.len = 0;
2592
2593 try self.emitBody(some, inst_table, &body_tmp);
2594 cases[cases.len - 1] = .{
2595 .items = &[0]*Inst{},
2596 .body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) },2580 .body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) },
2597 };2581 };
2598 }2582 }
...@@ -2846,7 +2830,7 @@ pub fn dumpZir(allocator: *Allocator, kind: []const u8, decl_name: [*:0]const u8...@@ -2846,7 +2830,7 @@ pub fn dumpZir(allocator: *Allocator, kind: []const u8, decl_name: [*:0]const u8
2846 .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator),2830 .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator),
2847 .loop_table = std.AutoHashMap(*Inst.Loop, []const u8).init(allocator),2831 .loop_table = std.AutoHashMap(*Inst.Loop, []const u8).init(allocator),
2848 .arena = std.heap.ArenaAllocator.init(allocator),2832 .arena = std.heap.ArenaAllocator.init(allocator),
2849 .indent = 2,2833 .indent = 4,
2850 .next_instr_index = 0,2834 .next_instr_index = 0,
2851 };2835 };
2852 defer write.arena.deinit();2836 defer write.arena.deinit();
src/zir_sema.zig+36-59
...@@ -553,10 +553,13 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c...@@ -553,10 +553,13 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c
553553
554 try analyzeBody(mod, &child_block.base, inst.positionals.body);554 try analyzeBody(mod, &child_block.base, inst.positionals.body);
555555
556 const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items);556 try parent_block.instructions.appendSlice(mod.gpa, child_block.instructions.items);
557 try parent_block.instructions.appendSlice(mod.gpa, copied_instructions);
558557
559 return copied_instructions[copied_instructions.len - 1];558 // comptime blocks won't generate any runtime values
559 if (child_block.instructions.items.len == 0)
560 return mod.constVoid(scope, inst.base.src);
561
562 return parent_block.instructions.items[parent_block.instructions.items.len - 1];
560}563}
561564
562fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst {565fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst {
...@@ -1235,11 +1238,8 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In...@@ -1235,11 +1238,8 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
12351238
1236 // TODO comptime execution1239 // TODO comptime execution
12371240
1238 // excludes else and '_' cases
1239 const case_count = inst.positionals.cases.len - @boolToInt(inst.kw_args.special_case != .none);
1240
1241 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);1241 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
1242 const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, case_count);1242 const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len);
12431243
1244 var case_block: Scope.Block = .{1244 var case_block: Scope.Block = .{
1245 .parent = parent_block,1245 .parent = parent_block,
...@@ -1251,58 +1251,39 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In...@@ -1251,58 +1251,39 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
1251 };1251 };
1252 defer case_block.instructions.deinit(mod.gpa);1252 defer case_block.instructions.deinit(mod.gpa);
12531253
1254 var items_tmp = std.ArrayList(Value).init(mod.gpa);1254 for (inst.positionals.cases[0..inst.positionals.cases.len]) |case, i| {
1255 defer items_tmp.deinit();
1256
1257 for (inst.positionals.cases[0..case_count]) |case, i| {
1258 // Reset without freeing.1255 // Reset without freeing.
1259 case_block.instructions.items.len = 0;1256 case_block.instructions.items.len = 0;
1260 items_tmp.items.len = 0;
12611257
1262 for (case.items) |item| {1258 const resolved = try resolveInst(mod, scope, case.item);
1263 if (item.castTag(.switch_range)) |range| {1259 const casted = try mod.coerce(scope, target.ty, resolved);
1264 return mod.fail(scope, item.src, "genSwitch expand range", .{});1260 const item = try mod.resolveConstValue(scope, casted);
1265 }
1266 const resolved = try resolveInst(mod, scope, item);
1267 const casted = try mod.coerce(scope, target.ty, resolved);
1268 const val = try mod.resolveConstValue(scope, casted);
1269 try items_tmp.append(val);
1270 }
12711261
1272 try analyzeBody(mod, &case_block.base, case.body);1262 try analyzeBody(mod, &case_block.base, case.body);
12731263
1274 cases[i] = .{1264 cases[i] = .{
1275 .items = try parent_block.arena.dupe(Value, items_tmp.items),1265 .item = item,
1276 .body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items) },1266 .body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items) },
1277 };1267 };
1278 }1268 }
1279
1280 const else_body = if (inst.kw_args.special_case != .none) blk: {
1281 case_block.instructions.items.len = 0;
1282
1283 try analyzeBody(mod, &case_block.base, inst.positionals.cases[case_count].body);
1284 break: blk Body{
1285 .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items),
1286 };
1287 } else null;
1288 1269
1289 return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases, else_body);1270 return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases);
1290}1271}
12911272
1292fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void {1273fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void {
1293 // validate usage of '_' prongs1274 // validate usage of '_' prongs
1294 if (inst.kw_args.special_case == .underscore and target.ty.zigTypeTag() != .Enum) {1275 if (inst.kw_args.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) {
1295 return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{});1276 return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{});
1296 // TODO notes "'_' prong here" inst.positionals.cases[last].src1277 // TODO notes "'_' prong here" inst.positionals.cases[last].src
1297 }1278 }
12981279
1299 // check that target type supports ranges1280 // check that target type supports ranges
1300 if (inst.kw_args.support_range) |some| {1281 if (inst.kw_args.range) |range_inst| {
1301 switch (target.ty.zigTypeTag()) {1282 switch (target.ty.zigTypeTag()) {
1302 .Int, .ComptimeInt, .Float, .ComptimeFloat => {},1283 .Int, .ComptimeInt, .Float, .ComptimeFloat => {},
1303 else => {1284 else => {
1304 return mod.fail(scope, target.src, "ranges not allowed when switching on type {}", .{target.ty});1285 return mod.fail(scope, target.src, "ranges not allowed when switching on type {}", .{target.ty});
1305 // TODO notes "range used here" some.src1286 // TODO notes "range used here" range_inst.src
1306 },1287 },
1307 }1288 }
1308 }1289 }
...@@ -1317,46 +1298,42 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw...@@ -1317,46 +1298,42 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw
1317 .Bool => {1298 .Bool => {
1318 var true_count: u8 = 0;1299 var true_count: u8 = 0;
1319 var false_count: u8 = 0;1300 var false_count: u8 = 0;
1320 for (inst.positionals.cases) |case| {1301 for (inst.positionals.items) |item| {
1321 for (case.items) |item| {1302 const resolved = try resolveInst(mod, scope, item);
1322 const resolved = try resolveInst(mod, scope, item);1303 const casted = try mod.coerce(scope, Type.initTag(.bool), resolved);
1323 const casted = try mod.coerce(scope, Type.initTag(.bool), resolved);1304 if ((try mod.resolveConstValue(scope, casted)).toBool()) {
1324 if ((try mod.resolveConstValue(scope, casted)).toBool()) {1305 true_count += 1;
1325 true_count += 1;1306 } else {
1326 } else {1307 false_count += 1;
1327 false_count += 1;1308 }
1328 }
13291309
1330 if (true_count > 1 or false_count > 1) {1310 if (true_count > 1 or false_count > 1) {
1331 return mod.fail(scope, item.src, "duplicate switch value", .{});1311 return mod.fail(scope, item.src, "duplicate switch value", .{});
1332 }
1333 }1312 }
1334 }1313 }
1335 if ((true_count == 0 or false_count == 0) and inst.kw_args.special_case != .@"else") {1314 if ((true_count == 0 or false_count == 0) and inst.kw_args.special_prong != .@"else") {
1336 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});1315 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});
1337 }1316 }
1338 if ((true_count == 1 and false_count == 1) and inst.kw_args.special_case == .@"else") {1317 if ((true_count == 1 and false_count == 1) and inst.kw_args.special_prong == .@"else") {
1339 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});1318 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});
1340 }1319 }
1341 },1320 },
1342 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {1321 .EnumLiteral, .Void, .Fn, .Pointer, .Type => {
1343 if (inst.kw_args.special_case != .@"else") {1322 if (inst.kw_args.special_prong != .@"else") {
1344 return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty});1323 return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty});
1345 }1324 }
13461325
1347 var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(mod.gpa);1326 var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(mod.gpa);
1348 defer seen_values.deinit();1327 defer seen_values.deinit();
13491328
1350 for (inst.positionals.cases) |case| {1329 for (inst.positionals.items) |item| {
1351 for (case.items) |item| {1330 const resolved = try resolveInst(mod, scope, item);
1352 const resolved = try resolveInst(mod, scope, item);1331 const casted = try mod.coerce(scope, target.ty, resolved);
1353 const casted = try mod.coerce(scope, target.ty, resolved);1332 const val = try mod.resolveConstValue(scope, casted);
1354 const val = try mod.resolveConstValue(scope, casted);
13551333
1356 if (try seen_values.fetchPut(val, item.src)) |prev| {1334 if (try seen_values.fetchPut(val, item.src)) |prev| {
1357 return mod.fail(scope, item.src, "duplicate switch value", .{});1335 return mod.fail(scope, item.src, "duplicate switch value", .{});
1358 // TODO notes "previous value here" prev.value1336 // TODO notes "previous value here" prev.value
1359 }
1360 }1337 }
1361 }1338 }
1362 },1339 },