| author | |
| committer | |
| log | 7db17a2d89c866efadf9a487acf2f9b0535ba859 |
| tree | ccbb5f3b3cb15162c7e6ec0880f1673e7f8b7b9d |
| parent | 95467f324909055d014e989ca9be7b0bb04237c4 |
| signature |
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 | 2122 | src: usize, |
| 2123 | 2123 | target_ptr: *Inst, |
| 2124 | 2124 | cases: []Inst.SwitchBr.Case, |
| 2125 | else_body: ?Module.Body, | |
| 2126 | 2125 | ) !*Inst { |
| 2127 | 2126 | const inst = try block.arena.create(Inst.SwitchBr); |
| 2128 | 2127 | inst.* = .{ |
| 2129 | 2128 | .base = .{ |
| 2130 | 2129 | .tag = .switchbr, |
| 2131 | .ty = Type.initTag(.noreturn), | |
| 2130 | .ty = Type.initTag(.void), | |
| 2132 | 2131 | .src = src, |
| 2133 | 2132 | }, |
| 2134 | 2133 | .target_ptr = target_ptr, |
| 2135 | 2134 | .cases = cases, |
| 2136 | .@"else" = else_body, | |
| 2137 | 2135 | }; |
| 2138 | 2136 | try block.instructions.append(self.gpa, &inst.base); |
| 2139 | 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 | 1570 | }; |
| 1571 | 1571 | defer block_scope.instructions.deinit(mod.gpa); |
| 1572 | 1572 | |
| 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 | 1581 | const tree = scope.tree(); |
| 1574 | 1582 | const switch_src = tree.token_locs[switch_node.switch_token].start; |
| 1575 | 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); | |
| 1577 | var kw_args: std.meta.fieldInfo(zir.Inst.SwitchBr, "kw_args").field_type = .{}; | |
| 1584 | // Add the switch instruction here so that it comes before any range checks. | |
| 1585 | const switch_inst = (try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{ | |
| 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(); | |
| 1578 | 1595 | |
| 1579 | 1596 | // first we gather all the switch items and check else/'_' prongs |
| 1580 | var case_index: usize = 0; | |
| 1581 | 1597 | var else_src: ?usize = null; |
| 1582 | 1598 | var underscore_src: ?usize = null; |
| 1599 | var range_inst: ?*zir.Inst = null; | |
| 1583 | 1600 | for (switch_node.cases()) |uncasted_case| { |
| 1584 | 1601 | const case = uncasted_case.castTag(.SwitchCase).?; |
| 1585 | 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 | 1610 | return mod.fail(scope, case_src, "multiple else prongs in switch expression", .{}); |
| 1594 | 1611 | // TODO notes "previous else prong is here" |
| 1595 | 1612 | } |
| 1596 | kw_args.special_case = .@"else"; | |
| 1597 | 1613 | else_src = case_src; |
| 1598 | cases[cases.len - 1] = .{ | |
| 1599 | .items = &[0]*zir.Inst{}, | |
| 1600 | .body = undefined, // filled below | |
| 1601 | }; | |
| 1602 | 1614 | continue; |
| 1603 | 1615 | } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and |
| 1604 | 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 | 1619 | return mod.fail(scope, case_src, "multiple '_' prongs in switch expression", .{}); |
| 1608 | 1620 | // TODO notes "previous '_' prong is here" |
| 1609 | 1621 | } |
| 1610 | kw_args.special_case = .underscore; | |
| 1611 | 1622 | underscore_src = case_src; |
| 1612 | cases[cases.len - 1] = .{ | |
| 1613 | .items = &[0]*zir.Inst{}, | |
| 1614 | .body = undefined, // filled below | |
| 1615 | }; | |
| 1616 | 1623 | continue; |
| 1617 | 1624 | } |
| 1618 | 1625 | |
| 1619 | 1626 | if (else_src) |some_else| { |
| 1620 | 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 | 1629 | // TODO notes "else prong is here" |
| 1623 | 1630 | // TODO notes "'_' prong is here" |
| 1624 | 1631 | } |
| 1625 | 1632 | } |
| 1626 | 1633 | |
| 1627 | // Regular case, we need to fill `items`. | |
| 1628 | const items = try block_scope.arena.alloc(*zir.Inst, case.items_len); | |
| 1629 | for (case.items()) |item, i| { | |
| 1630 | if (item.castTag(.Range)) |range| { | |
| 1631 | items[i] = try switchRange(mod, &block_scope.base, range); | |
| 1632 | if (kw_args.support_range == null) | |
| 1633 | kw_args.support_range = items[i]; | |
| 1634 | } else { | |
| 1635 | items[i] = try expr(mod, &block_scope.base, .none, item); | |
| 1636 | } | |
| 1634 | // TODO and not range | |
| 1635 | if (case.items_len == 1) { | |
| 1636 | const item = try expr(mod, &item_scope.base, .none, case.items()[0]); | |
| 1637 | try cases.append(.{ | |
| 1638 | .item = item, | |
| 1639 | .body = undefined, // populated below | |
| 1640 | }); | |
| 1641 | continue; | |
| 1637 | 1642 | } |
| 1638 | cases[case_index] = .{ | |
| 1639 | .items = items, | |
| 1640 | .body = undefined, // filled below | |
| 1641 | }; | |
| 1642 | case_index += 1; | |
| 1643 | return mod.fail(scope, case_src, "TODO switch ranges", .{}); | |
| 1643 | 1644 | } |
| 1644 | 1645 | |
| 1645 | // Then we add the switch instruction to finish the block. | |
| 1646 | _ = try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{ | |
| 1647 | .target_ptr = target_ptr, | |
| 1648 | .cases = cases, | |
| 1649 | }, kw_args); | |
| 1646 | // Actually populate switch instruction values. | |
| 1647 | if (else_src != null) switch_inst.kw_args.special_prong = .@"else"; | |
| 1648 | if (underscore_src != null) switch_inst.kw_args.special_prong = .underscore; | |
| 1649 | switch_inst.positionals.cases = try block_scope.arena.dupe(zir.Inst.SwitchBr.Case, cases.items); | |
| 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 | 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 | }); |
| 1653 | 1661 | |
| 1654 | 1662 | // 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 | 1676 | defer case_scope.instructions.deinit(mod.gpa); |
| 1669 | 1677 | |
| 1670 | 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 | 1681 | for (switch_node.cases()) |uncasted_case| { |
| 1673 | 1682 | const case = uncasted_case.castTag(.SwitchCase).?; |
| 1674 | 1683 | const case_src = tree.token_locs[case.firstToken()].start; |
| 1675 | 1684 | // reset without freeing to reduce allocations. |
| 1676 | 1685 | defer case_scope.instructions.items.len = 0; |
| 1677 | 1686 | |
| 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 | 1687 | if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) { |
| 1682 | // validated above | |
| 1683 | cur_index = cases.len - 1; | |
| 1688 | // validated earlier | |
| 1689 | special_case = case; | |
| 1690 | continue; | |
| 1684 | 1691 | } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and |
| 1685 | 1692 | mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) |
| 1686 | 1693 | { |
| 1687 | // validated above | |
| 1688 | cur_index = cases.len - 1; | |
| 1694 | // validated earlier | |
| 1695 | special_case = case; | |
| 1696 | continue; | |
| 1689 | 1697 | } |
| 1690 | 1698 | |
| 1691 | // Generate the body of this case. | |
| 1692 | const case_body = try expr(mod, &case_scope.base, case_rl, case.expr); | |
| 1699 | if (case.items_len == 1) { | |
| 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 | 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 | 1723 | .block = block, |
| 1696 | 1724 | .operand = case_body, |
| 1697 | 1725 | }, .{}); |
| 1698 | 1726 | } |
| 1699 | cases[cur_index].body = .{ | |
| 1700 | .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), | |
| 1701 | }; | |
| 1727 | } else { | |
| 1728 | _ = try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.BreakVoid, .{ | |
| 1729 | .block = block, | |
| 1730 | }, .{}); | |
| 1702 | 1731 | } |
| 1703 | 1732 | |
| 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 | 1737 | return &block.base; |
| 1705 | 1738 | } |
| 1706 | 1739 |
src/ir.zig+1-7| ... | ... | @@ -467,15 +467,12 @@ pub const Inst = struct { |
| 467 | 467 | base: Inst, |
| 468 | 468 | target_ptr: *Inst, |
| 469 | 469 | cases: []Case, |
| 470 | @"else": ?Body, | |
| 471 | 470 | /// Set of instructions whose lifetimes end at the start of one of the cases. |
| 472 | 471 | /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... , case_n_count ... else_count]. |
| 473 | 472 | deaths: [*]*Inst = undefined, |
| 474 | else_index: u32 = 0, | |
| 475 | else_deaths: u32 = 0, | |
| 476 | 473 | |
| 477 | 474 | pub const Case = struct { |
| 478 | items: []Value, | |
| 475 | item: Value, | |
| 479 | 476 | body: Body, |
| 480 | 477 | index: u32 = 0, |
| 481 | 478 | deaths: u32 = 0, |
| ... | ... | @@ -497,9 +494,6 @@ pub const Inst = struct { |
| 497 | 494 | const case = self.cases[case_index]; |
| 498 | 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 | }; |
| 505 | 499 |
src/zir.zig+19-35| ... | ... | @@ -501,7 +501,7 @@ pub const Inst = struct { |
| 501 | 501 | .slice, |
| 502 | 502 | .slice_start, |
| 503 | 503 | .import, |
| 504 | .switch_range, | |
| 504 | .switchbr, | |
| 505 | 505 | => false, |
| 506 | 506 | |
| 507 | 507 | .@"break", |
| ... | ... | @@ -513,7 +513,7 @@ pub const Inst = struct { |
| 513 | 513 | .unreach_nocheck, |
| 514 | 514 | .@"unreachable", |
| 515 | 515 | .loop, |
| 516 | .switchbr, | |
| 516 | .switch_range, | |
| 517 | 517 | => true, |
| 518 | 518 | }; |
| 519 | 519 | } |
| ... | ... | @@ -1005,22 +1005,21 @@ pub const Inst = struct { |
| 1005 | 1005 | positionals: struct { |
| 1006 | 1006 | target_ptr: *Inst, |
| 1007 | 1007 | cases: []Case, |
| 1008 | /// List of all individual items and ranges | |
| 1009 | items: []*Inst, | |
| 1008 | 1010 | }, |
| 1009 | 1011 | kw_args: struct { |
| 1010 | /// if not null target must support ranges, (be int) | |
| 1011 | support_range: ?*Inst = null, | |
| 1012 | special_case: enum { | |
| 1013 | /// all of positionals.cases are regular cases | |
| 1012 | /// Pointer to first range if such exists. | |
| 1013 | range: ?*Inst = null, | |
| 1014 | special_prong: enum { | |
| 1014 | 1015 | none, |
| 1015 | /// last case in positionals.cases is an else case | |
| 1016 | 1016 | @"else", |
| 1017 | /// last case in positionals.cases is an underscore case | |
| 1018 | 1017 | underscore, |
| 1019 | 1018 | } = .none, |
| 1020 | 1019 | }, |
| 1021 | 1020 | |
| 1022 | 1021 | pub const Case = struct { |
| 1023 | items: []*Inst, | |
| 1022 | item: *Inst, | |
| 1024 | 1023 | body: Module.Body, |
| 1025 | 1024 | }; |
| 1026 | 1025 | }; |
| ... | ... | @@ -1286,7 +1285,7 @@ const Writer = struct { |
| 1286 | 1285 | } |
| 1287 | 1286 | try stream.writeByteNTimes(' ', self.indent); |
| 1288 | 1287 | self.indent += 2; |
| 1289 | try self.writeParamToStream(stream, &case.items); | |
| 1288 | try self.writeParamToStream(stream, &case.item); | |
| 1290 | 1289 | try stream.writeAll(" => "); |
| 1291 | 1290 | try self.writeParamToStream(stream, &case.body); |
| 1292 | 1291 | self.indent -= 2; |
| ... | ... | @@ -1716,7 +1715,7 @@ const Parser = struct { |
| 1716 | 1715 | while (true) { |
| 1717 | 1716 | const cur = try cases.addOne(); |
| 1718 | 1717 | skipSpace(self); |
| 1719 | cur.items = try self.parseParameterGeneric([]*Inst, body_ctx); | |
| 1718 | cur.item = try self.parseParameterGeneric(*Inst, body_ctx); | |
| 1720 | 1719 | skipSpace(self); |
| 1721 | 1720 | try requireEatBytes(self, "=>"); |
| 1722 | 1721 | cur.body = try self.parseBody(body_ctx); |
| ... | ... | @@ -2549,8 +2548,7 @@ const EmitZIR = struct { |
| 2549 | 2548 | }, |
| 2550 | 2549 | .switchbr => blk: { |
| 2551 | 2550 | const old_inst = inst.castTag(.switchbr).?; |
| 2552 | const case_count = old_inst.cases.len + @boolToInt(old_inst.@"else" != null); | |
| 2553 | const cases = try self.arena.allocator.alloc(Inst.SwitchBr.Case, case_count); | |
| 2551 | const cases = try self.arena.allocator.alloc(Inst.SwitchBr.Case, old_inst.cases.len); | |
| 2554 | 2552 | const new_inst = try self.arena.allocator.create(Inst.SwitchBr); |
| 2555 | 2553 | new_inst.* = .{ |
| 2556 | 2554 | .base = .{ |
| ... | ... | @@ -2560,11 +2558,9 @@ const EmitZIR = struct { |
| 2560 | 2558 | .positionals = .{ |
| 2561 | 2559 | .target_ptr = try self.resolveInst(new_body, old_inst.target_ptr), |
| 2562 | 2560 | .cases = cases, |
| 2561 | .items = &[_]*Inst{}, // TODO this should actually be populated | |
| 2563 | 2562 | }, |
| 2564 | .kw_args = .{ | |
| 2565 | .special_case = if (old_inst.@"else" != null) .@"else" else .none, | |
| 2566 | .support_range = null, | |
| 2567 | }, | |
| 2563 | .kw_args = .{}, | |
| 2568 | 2564 | }; |
| 2569 | 2565 | |
| 2570 | 2566 | var body_tmp = std.ArrayList(*Inst).init(self.allocator); |
| ... | ... | @@ -2574,25 +2570,13 @@ const EmitZIR = struct { |
| 2574 | 2570 | body_tmp.items.len = 0; |
| 2575 | 2571 | |
| 2576 | 2572 | try self.emitBody(case.body, inst_table, &body_tmp); |
| 2577 | const items = try self.arena.allocator.alloc(*Inst, case.items.len); | |
| 2578 | for (case.items) |item, j| { | |
| 2579 | items[j] = (try self.emitTypedValue(inst.src, .{ | |
| 2580 | .ty = old_inst.target_ptr.ty.elemType(), | |
| 2581 | .val = item, | |
| 2582 | })).inst; | |
| 2583 | } | |
| 2573 | const item = (try self.emitTypedValue(inst.src, .{ | |
| 2574 | .ty = old_inst.target_ptr.ty.elemType(), | |
| 2575 | .val = case.item, | |
| 2576 | })).inst; | |
| 2584 | 2577 | |
| 2585 | 2578 | cases[i] = .{ |
| 2586 | .items = items, | |
| 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{}, | |
| 2579 | .item = item, | |
| 2596 | 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 | 2830 | .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator), |
| 2847 | 2831 | .loop_table = std.AutoHashMap(*Inst.Loop, []const u8).init(allocator), |
| 2848 | 2832 | .arena = std.heap.ArenaAllocator.init(allocator), |
| 2849 | .indent = 2, | |
| 2833 | .indent = 4, | |
| 2850 | 2834 | .next_instr_index = 0, |
| 2851 | 2835 | }; |
| 2852 | 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 | 553 | |
| 554 | 554 | try analyzeBody(mod, &child_block.base, inst.positionals.body); |
| 555 | 555 | |
| 556 | const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items); | |
| 557 | try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); | |
| 556 | try parent_block.instructions.appendSlice(mod.gpa, child_block.instructions.items); | |
| 558 | 557 | |
| 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 | } |
| 561 | 564 | |
| 562 | 565 | fn 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 | 1238 | |
| 1236 | 1239 | // TODO comptime execution |
| 1237 | 1240 | |
| 1238 | // excludes else and '_' cases | |
| 1239 | const case_count = inst.positionals.cases.len - @boolToInt(inst.kw_args.special_case != .none); | |
| 1240 | ||
| 1241 | 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); | |
| 1243 | 1243 | |
| 1244 | 1244 | var case_block: Scope.Block = .{ |
| 1245 | 1245 | .parent = parent_block, |
| ... | ... | @@ -1251,58 +1251,39 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In |
| 1251 | 1251 | }; |
| 1252 | 1252 | defer case_block.instructions.deinit(mod.gpa); |
| 1253 | 1253 | |
| 1254 | var items_tmp = std.ArrayList(Value).init(mod.gpa); | |
| 1255 | defer items_tmp.deinit(); | |
| 1256 | ||
| 1257 | for (inst.positionals.cases[0..case_count]) |case, i| { | |
| 1254 | for (inst.positionals.cases[0..inst.positionals.cases.len]) |case, i| { | |
| 1258 | 1255 | // Reset without freeing. |
| 1259 | 1256 | case_block.instructions.items.len = 0; |
| 1260 | items_tmp.items.len = 0; | |
| 1261 | 1257 | |
| 1262 | for (case.items) |item| { | |
| 1263 | if (item.castTag(.switch_range)) |range| { | |
| 1264 | return mod.fail(scope, item.src, "genSwitch expand range", .{}); | |
| 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 | } | |
| 1258 | const resolved = try resolveInst(mod, scope, case.item); | |
| 1259 | const casted = try mod.coerce(scope, target.ty, resolved); | |
| 1260 | const item = try mod.resolveConstValue(scope, casted); | |
| 1271 | 1261 | |
| 1272 | 1262 | try analyzeBody(mod, &case_block.base, case.body); |
| 1273 | 1263 | |
| 1274 | 1264 | cases[i] = .{ |
| 1275 | .items = try parent_block.arena.dupe(Value, items_tmp.items), | |
| 1265 | .item = item, | |
| 1276 | 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 | } |
| 1291 | 1272 | |
| 1292 | 1273 | fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void { |
| 1293 | 1274 | // 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 | 1276 | return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{}); |
| 1296 | 1277 | // TODO notes "'_' prong here" inst.positionals.cases[last].src |
| 1297 | 1278 | } |
| 1298 | 1279 | |
| 1299 | 1280 | // check that target type supports ranges |
| 1300 | if (inst.kw_args.support_range) |some| { | |
| 1281 | if (inst.kw_args.range) |range_inst| { | |
| 1301 | 1282 | switch (target.ty.zigTypeTag()) { |
| 1302 | 1283 | .Int, .ComptimeInt, .Float, .ComptimeFloat => {}, |
| 1303 | 1284 | else => { |
| 1304 | 1285 | return mod.fail(scope, target.src, "ranges not allowed when switching on type {}", .{target.ty}); |
| 1305 | // TODO notes "range used here" some.src | |
| 1286 | // 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 | 1298 | .Bool => { |
| 1318 | 1299 | var true_count: u8 = 0; |
| 1319 | 1300 | var false_count: u8 = 0; |
| 1320 | for (inst.positionals.cases) |case| { | |
| 1321 | for (case.items) |item| { | |
| 1322 | const resolved = try resolveInst(mod, scope, item); | |
| 1323 | const casted = try mod.coerce(scope, Type.initTag(.bool), resolved); | |
| 1324 | if ((try mod.resolveConstValue(scope, casted)).toBool()) { | |
| 1325 | true_count += 1; | |
| 1326 | } else { | |
| 1327 | false_count += 1; | |
| 1328 | } | |
| 1301 | for (inst.positionals.items) |item| { | |
| 1302 | const resolved = try resolveInst(mod, scope, item); | |
| 1303 | const casted = try mod.coerce(scope, Type.initTag(.bool), resolved); | |
| 1304 | if ((try mod.resolveConstValue(scope, casted)).toBool()) { | |
| 1305 | true_count += 1; | |
| 1306 | } else { | |
| 1307 | false_count += 1; | |
| 1308 | } | |
| 1329 | 1309 | |
| 1330 | if (true_count > 1 or false_count > 1) { | |
| 1331 | return mod.fail(scope, item.src, "duplicate switch value", .{}); | |
| 1332 | } | |
| 1310 | if (true_count > 1 or false_count > 1) { | |
| 1311 | return mod.fail(scope, item.src, "duplicate switch value", .{}); | |
| 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 | 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 | 1318 | return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); |
| 1340 | 1319 | } |
| 1341 | 1320 | }, |
| 1342 | 1321 | .EnumLiteral, .Void, .Fn, .Pointer, .Type => { |
| 1343 | if (inst.kw_args.special_case != .@"else") { | |
| 1322 | if (inst.kw_args.special_prong != .@"else") { | |
| 1344 | 1323 | return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty}); |
| 1345 | 1324 | } |
| 1346 | 1325 | |
| 1347 | 1326 | var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(mod.gpa); |
| 1348 | 1327 | defer seen_values.deinit(); |
| 1349 | 1328 | |
| 1350 | for (inst.positionals.cases) |case| { | |
| 1351 | for (case.items) |item| { | |
| 1352 | const resolved = try resolveInst(mod, scope, item); | |
| 1353 | const casted = try mod.coerce(scope, target.ty, resolved); | |
| 1354 | const val = try mod.resolveConstValue(scope, casted); | |
| 1329 | for (inst.positionals.items) |item| { | |
| 1330 | const resolved = try resolveInst(mod, scope, item); | |
| 1331 | const casted = try mod.coerce(scope, target.ty, resolved); | |
| 1332 | const val = try mod.resolveConstValue(scope, casted); | |
| 1355 | 1333 | |
| 1356 | if (try seen_values.fetchPut(val, item.src)) |prev| { | |
| 1357 | return mod.fail(scope, item.src, "duplicate switch value", .{}); | |
| 1358 | // TODO notes "previous value here" prev.value | |
| 1359 | } | |
| 1334 | if (try seen_values.fetchPut(val, item.src)) |prev| { | |
| 1335 | return mod.fail(scope, item.src, "duplicate switch value", .{}); | |
| 1336 | // TODO notes "previous value here" prev.value | |
| 1360 | 1337 | } |
| 1361 | 1338 | } |
| 1362 | 1339 | }, |