| ... | ... | @@ -183,6 +183,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 183 | 183 | .VarDecl => unreachable, // Handled in `blockExpr`. |
| 184 | 184 | .SwitchCase => unreachable, // Handled in `switchExpr`. |
| 185 | 185 | .SwitchElse => unreachable, // Handled in `switchExpr`. |
| 186 | .Range => unreachable, // Handled in `switchExpr`. |
| 186 | 187 | .Else => unreachable, // Handled explicitly the control flow expression functions. |
| 187 | 188 | .Payload => unreachable, // Handled explicitly. |
| 188 | 189 | .PointerPayload => unreachable, // Handled explicitly. |
| ... | ... | @@ -279,9 +280,9 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 279 | 280 | .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?), |
| 280 | 281 | .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?), |
| 281 | 282 | .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?), |
| 283 | .Switch => return switchExpr(mod, scope, rl, node.castTag(.Switch).?), |
| 282 | 284 | |
| 283 | 285 | .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", .{}), |
| 285 | 286 | .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}), |
| 286 | 287 | .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}), |
| 287 | 288 | .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 |
| 289 | 290 | .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}), |
| 290 | 291 | .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}), |
| 291 | 292 | .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", .{}), |
| 293 | 293 | .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}), |
| 294 | 294 | .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}), |
| 295 | 295 | .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}), |
| ... | ... | @@ -1561,6 +1561,156 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) |
| 1561 | 1561 | return &for_block.base; |
| 1562 | 1562 | } |
| 1563 | 1563 | |
| 1564 | fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.Switch) InnerError!*zir.Inst { |
| 1565 | var block_scope: Scope.GenZIR = .{ |
| 1566 | .parent = scope, |
| 1567 | .decl = scope.decl().?, |
| 1568 | .arena = scope.arena(), |
| 1569 | .instructions = .{}, |
| 1570 | }; |
| 1571 | defer block_scope.instructions.deinit(mod.gpa); |
| 1572 | |
| 1573 | const tree = scope.tree(); |
| 1574 | 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); |
| 1576 | const cases = try scope.arena().alloc(zir.Inst.Switch.Case, switch_node.cases_len); |
| 1577 | var kw_args: std.meta.fieldInfo(zir.Inst.Switch, "kw_args").field_type = .{}; |
| 1578 | |
| 1579 | // first we gather all the switch items and check else/'_' prongs |
| 1580 | var case_index: usize = 0; |
| 1581 | var else_src: ?usize = null; |
| 1582 | var underscore_src: ?usize = null; |
| 1583 | for (switch_node.cases()) |uncasted_case| { |
| 1584 | const case = uncasted_case.castTag(.SwitchCase).?; |
| 1585 | const case_src = tree.token_locs[case.firstToken()].start; |
| 1586 | |
| 1587 | if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) { |
| 1588 | if (else_src) |src| { |
| 1589 | return mod.fail(scope, case_src, "multiple else prongs in switch expression", .{}); |
| 1590 | // TODO notes "previous else prong is here" |
| 1591 | } |
| 1592 | kw_args.special_case = .@"else"; |
| 1593 | else_src = case_src; |
| 1594 | cases[cases.len - 1] = .{ |
| 1595 | .values = &[_]*zir.Inst{}, |
| 1596 | .body = undefined, // filled below |
| 1597 | }; |
| 1598 | continue; |
| 1599 | } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and |
| 1600 | mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) |
| 1601 | { |
| 1602 | if (underscore_src) |src| { |
| 1603 | return mod.fail(scope, case_src, "multiple '_' prongs in switch expression", .{}); |
| 1604 | // TODO notes "previous '_' prong is here" |
| 1605 | } |
| 1606 | kw_args.special_case = .underscore; |
| 1607 | underscore_src = case_src; |
| 1608 | cases[cases.len - 1] = .{ |
| 1609 | .values = &[_]*zir.Inst{}, |
| 1610 | .body = undefined, // filled below |
| 1611 | }; |
| 1612 | continue; |
| 1613 | } |
| 1614 | |
| 1615 | if (else_src) |some_else| { |
| 1616 | if (underscore_src) |some_underscore| { |
| 1617 | return mod.fail(scope, case_src, "else and '_' prong in switch expression", .{}); |
| 1618 | // TODO notes "else prong is here" |
| 1619 | // TODO notes "'_' prong is here" |
| 1620 | } |
| 1621 | } |
| 1622 | |
| 1623 | // Regular case, we need to fill `values`. |
| 1624 | const values = try block_scope.arena.alloc(*zir.Inst, case.items_len); |
| 1625 | for (case.items()) |item, i| { |
| 1626 | if (item.castTag(.Range)) |range| { |
| 1627 | values[i] = try switchRange(mod, &block_scope.base, range); |
| 1628 | if (kw_args.support_range == null) |
| 1629 | kw_args.support_range = values[i]; |
| 1630 | } else { |
| 1631 | values[i] = try expr(mod, &block_scope.base, .none, item); |
| 1632 | } |
| 1633 | } |
| 1634 | cases[case_index] = .{ |
| 1635 | .values = values, |
| 1636 | .body = undefined, // filled below |
| 1637 | }; |
| 1638 | case_index += 1; |
| 1639 | } |
| 1640 | |
| 1641 | // Then we add the switch instruction to finish the block. |
| 1642 | _ = try addZIRInst(mod, scope, switch_src, zir.Inst.Switch, .{ |
| 1643 | .target_ptr = target_ptr, |
| 1644 | .cases = cases, |
| 1645 | }, kw_args); |
| 1646 | const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{ |
| 1647 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), |
| 1648 | }); |
| 1649 | |
| 1650 | // Most result location types can be forwarded directly; however |
| 1651 | // if we need to write to a pointer which has an inferred type, |
| 1652 | // proper type inference requires peer type resolution on the switch case. |
| 1653 | const case_rl: ResultLoc = switch (rl) { |
| 1654 | .discard, .none, .ty, .ptr, .ref => rl, |
| 1655 | .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block }, |
| 1656 | }; |
| 1657 | |
| 1658 | var case_scope: Scope.GenZIR = .{ |
| 1659 | .parent = scope, |
| 1660 | .decl = block_scope.decl, |
| 1661 | .arena = block_scope.arena, |
| 1662 | .instructions = .{}, |
| 1663 | }; |
| 1664 | defer case_scope.instructions.deinit(mod.gpa); |
| 1665 | |
| 1666 | // And finally we fill generate the bodies of each case. |
| 1667 | case_index = 0; |
| 1668 | for (switch_node.cases()) |uncasted_case| { |
| 1669 | const case = uncasted_case.castTag(.SwitchCase).?; |
| 1670 | const case_src = tree.token_locs[case.firstToken()].start; |
| 1671 | // reset without freeing to reduce allocations. |
| 1672 | defer case_scope.instructions.items.len = 0; |
| 1673 | |
| 1674 | // What index in positionals.cases should this one be placed at. |
| 1675 | // For special cases it will be at the end. |
| 1676 | var cur_index = case_index; |
| 1677 | if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) { |
| 1678 | // validated above |
| 1679 | cur_index = cases.len - 1; |
| 1680 | } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and |
| 1681 | mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) |
| 1682 | { |
| 1683 | // validated above |
| 1684 | cur_index = cases.len - 1; |
| 1685 | } |
| 1686 | |
| 1687 | // Generate the body of this case. |
| 1688 | const case_body = try expr(mod, &case_scope.base, case_rl, case.expr); |
| 1689 | if (!case_body.tag.isNoReturn()) { |
| 1690 | _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.Break, .{ |
| 1691 | .block = block, |
| 1692 | .operand = case_body, |
| 1693 | }, .{}); |
| 1694 | } |
| 1695 | cases[cur_index].body = .{ |
| 1696 | .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), |
| 1697 | }; |
| 1698 | } |
| 1699 | |
| 1700 | return &block.base; |
| 1701 | } |
| 1702 | |
| 1703 | /// Only used for `a...b` in switches. |
| 1704 | fn switchRange(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst { |
| 1705 | const tree = scope.tree(); |
| 1706 | const src = tree.token_locs[node.op_token].start; |
| 1707 | |
| 1708 | const start = try expr(mod, scope, .none, node.lhs); |
| 1709 | const end = try expr(mod, scope, .none, node.rhs); |
| 1710 | |
| 1711 | return try addZIRBinOp(mod, scope, src, .switch_range, start, end); |
| 1712 | } |
| 1713 | |
| 1564 | 1714 | fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst { |
| 1565 | 1715 | const tree = scope.tree(); |
| 1566 | 1716 | const src = tree.token_locs[cfe.ltoken].start; |