authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-10-11 23:52:08+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-10-30 15:58:11+02:00
logad32e46bceb03c0b0d67fe73e0de0e308f0675e6
tree296539931ed2c3dcd39fa4453074ed0661b2241c
parenta1d7f0053d6fa56bcc879e83987babd42bb21a20
signaturelock-open Commit is signed but in an unrecognized format.

stage2: switch astgen


3 files changed, 228 insertions(+), 2 deletions(-)

src/astgen.zig+152-2
...@@ -183,6 +183,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -183,6 +183,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
183 .VarDecl => unreachable, // Handled in `blockExpr`.183 .VarDecl => unreachable, // Handled in `blockExpr`.
184 .SwitchCase => unreachable, // Handled in `switchExpr`.184 .SwitchCase => unreachable, // Handled in `switchExpr`.
185 .SwitchElse => unreachable, // Handled in `switchExpr`.185 .SwitchElse => unreachable, // Handled in `switchExpr`.
186 .Range => unreachable, // Handled in `switchExpr`.
186 .Else => unreachable, // Handled explicitly the control flow expression functions.187 .Else => unreachable, // Handled explicitly the control flow expression functions.
187 .Payload => unreachable, // Handled explicitly.188 .Payload => unreachable, // Handled explicitly.
188 .PointerPayload => unreachable, // Handled explicitly.189 .PointerPayload => unreachable, // Handled explicitly.
...@@ -279,9 +280,9 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -279,9 +280,9 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
279 .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),280 .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),
280 .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),281 .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),
281 .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),282 .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),
283 .Switch => return switchExpr(mod, scope, rl, node.castTag(.Switch).?),
282284
283 .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),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 .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),286 .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),
286 .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),287 .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
287 .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),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,7 +290,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
289 .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),290 .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),
290 .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),291 .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),
291 .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}),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 .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),293 .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),
294 .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}),294 .Continue => return mod.failNode(scope, node, "TODO implement astgen.expr for .Continue", .{}),
295 .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),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,6 +1561,156 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For)
1561 return &for_block.base;1561 return &for_block.base;
1562}1562}
15631563
1564fn 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.
1704fn 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
1564fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {1714fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
1565 const tree = scope.tree();1715 const tree = scope.tree();
1566 const src = tree.token_locs[cfe.ltoken].start;1716 const src = tree.token_locs[cfe.ltoken].start;
src/zir.zig+75
...@@ -272,6 +272,10 @@ pub const Inst = struct {...@@ -272,6 +272,10 @@ pub const Inst = struct {
272 ensure_err_payload_void,272 ensure_err_payload_void,
273 /// Enum literal273 /// Enum literal
274 enum_literal,274 enum_literal,
275 /// A switch expression.
276 @"switch",
277 /// A range in a switch case, `lhs...rhs`.
278 switch_range,
275279
276 pub fn Type(tag: Tag) type {280 pub fn Type(tag: Tag) type {
277 return switch (tag) {281 return switch (tag) {
...@@ -351,6 +355,7 @@ pub const Inst = struct {...@@ -351,6 +355,7 @@ pub const Inst = struct {
351 .error_union_type,355 .error_union_type,
352 .merge_error_sets,356 .merge_error_sets,
353 .slice_start,357 .slice_start,
358 .switch_range,
354 => BinOp,359 => BinOp,
355360
356 .block,361 .block,
...@@ -389,6 +394,7 @@ pub const Inst = struct {...@@ -389,6 +394,7 @@ pub const Inst = struct {
389 .enum_literal => EnumLiteral,394 .enum_literal => EnumLiteral,
390 .error_set => ErrorSet,395 .error_set => ErrorSet,
391 .slice => Slice,396 .slice => Slice,
397 .@"switch" => Switch,
392 };398 };
393 }399 }
394400
...@@ -493,6 +499,7 @@ pub const Inst = struct {...@@ -493,6 +499,7 @@ pub const Inst = struct {
493 .slice,499 .slice,
494 .slice_start,500 .slice_start,
495 .import,501 .import,
502 .switch_range,
496 => false,503 => false,
497504
498 .@"break",505 .@"break",
...@@ -504,6 +511,7 @@ pub const Inst = struct {...@@ -504,6 +511,7 @@ pub const Inst = struct {
504 .unreach_nocheck,511 .unreach_nocheck,
505 .@"unreachable",512 .@"unreachable",
506 .loop,513 .loop,
514 .@"switch",
507 => true,515 => true,
508 };516 };
509 }517 }
...@@ -987,6 +995,33 @@ pub const Inst = struct {...@@ -987,6 +995,33 @@ pub const Inst = struct {
987 sentinel: ?*Inst = null,995 sentinel: ?*Inst = null,
988 },996 },
989 };997 };
998
999 pub const Switch = struct {
1000 pub const base_tag = Tag.@"switch";
1001 base: Inst,
1002
1003 positionals: struct {
1004 target_ptr: *Inst,
1005 cases: []Case,
1006 },
1007 kw_args: struct {
1008 /// if not null target must support ranges, (be int)
1009 support_range: ?*Inst = null,
1010 special_case: enum {
1011 /// all of positionals.cases are regular cases
1012 none,
1013 /// last case in positionals.cases is an else case
1014 @"else",
1015 /// last case in positionals.cases is an underscore case
1016 underscore,
1017 } = .none,
1018 },
1019
1020 pub const Case = struct {
1021 values: []*Inst,
1022 body: Module.Body,
1023 };
1024 };
990};1025};
9911026
992pub const ErrorMsg = struct {1027pub const ErrorMsg = struct {
...@@ -1238,6 +1273,26 @@ const Writer = struct {...@@ -1238,6 +1273,26 @@ const Writer = struct {
1238 }1273 }
1239 try stream.writeByte(']');1274 try stream.writeByte(']');
1240 },1275 },
1276 []Inst.Switch.Case => {
1277 if (param.len == 0) {
1278 return stream.writeAll("{}");
1279 }
1280 try stream.writeAll("{\n");
1281 self.indent += 2;
1282 for (param) |*case, i| {
1283 if (i != 0) {
1284 try stream.writeAll(",\n");
1285 }
1286 try stream.writeByteNTimes(' ', self.indent);
1287 try self.writeParamToStream(stream, &case.values);
1288 try stream.writeAll(" => ");
1289 try self.writeParamToStream(stream, &case.body);
1290 }
1291 try stream.writeByte('\n');
1292 self.indent -= 2;
1293 try stream.writeByteNTimes(' ', self.indent);
1294 try stream.writeByte('}');
1295 },
1241 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),1296 else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)),
1242 }1297 }
1243 }1298 }
...@@ -1650,6 +1705,26 @@ const Parser = struct {...@@ -1650,6 +1705,26 @@ const Parser = struct {
1650 try requireEatBytes(self, "]");1705 try requireEatBytes(self, "]");
1651 return strings.toOwnedSlice();1706 return strings.toOwnedSlice();
1652 },1707 },
1708 []Inst.Switch.Case => {
1709 try requireEatBytes(self, "{");
1710 skipSpace(self);
1711 if (eatByte(self, '}')) return &[0]Inst.Switch.Case{};
1712
1713 var cases = std.ArrayList(Inst.Switch.Case).init(&self.arena.allocator);
1714 while (true) {
1715 const cur = try cases.addOne();
1716 skipSpace(self);
1717 cur.values = try self.parseParameterGeneric([]*Inst, body_ctx);
1718 skipSpace(self);
1719 try requireEatBytes(self, "=>");
1720 cur.body = try self.parseBody(body_ctx);
1721 skipSpace(self);
1722 if (!eatByte(self, ',')) break;
1723 }
1724 skipSpace(self);
1725 try requireEatBytes(self, "}");
1726 return cases.toOwnedSlice();
1727 },
1653 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),1728 else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)),
1654 }1729 }
1655 return self.fail("TODO parse parameter {}", .{@typeName(T)});1730 return self.fail("TODO parse parameter {}", .{@typeName(T)});
src/zir_sema.zig+1
...@@ -135,6 +135,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -135,6 +135,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
135 .slice => return analyzeInstSlice(mod, scope, old_inst.castTag(.slice).?),135 .slice => return analyzeInstSlice(mod, scope, old_inst.castTag(.slice).?),
136 .slice_start => return analyzeInstSliceStart(mod, scope, old_inst.castTag(.slice_start).?),136 .slice_start => return analyzeInstSliceStart(mod, scope, old_inst.castTag(.slice_start).?),
137 .import => return analyzeInstImport(mod, scope, old_inst.castTag(.import).?),137 .import => return analyzeInstImport(mod, scope, old_inst.castTag(.import).?),
138 .@"switch", .switch_range => @panic("TODO switch sema"),
138 }139 }
139}140}
140141