aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2020-07-16 05:57:32 +0000
committerGitHub <noreply@github.com>2020-07-16 05:57:32 +0000
commite079fdeee78d37a50c4e2a9fafe77903d62dbc34 (patch)
treec7b06e08f3696ae2b4f70a6783a7573aef8347b6
parent82562b205f9d99c27c4d5224311734e141bf2fda (diff)
parentd29dd5834b9d7386bb88e44bd2852428863cae81 (diff)
downloadzig-e079fdeee78d37a50c4e2a9fafe77903d62dbc34.tar.gz
zig-e079fdeee78d37a50c4e2a9fafe77903d62dbc34.zip
Merge pull request #5885 from ziglang/stage2-locals
self-hosted compiler local consts
-rw-r--r--lib/std/zig/ast.zig546
-rw-r--r--lib/std/zig/parse.zig314
-rw-r--r--lib/std/zig/render.zig222
-rw-r--r--src-self-hosted/Module.zig567
-rw-r--r--src-self-hosted/astgen.zig643
-rw-r--r--src-self-hosted/codegen.zig6
-rw-r--r--src-self-hosted/ir.zig5
-rw-r--r--src-self-hosted/translate_c.zig188
-rw-r--r--src-self-hosted/zir.zig15
9 files changed, 1504 insertions, 1002 deletions
diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig
index 7f066f541b..b91cac7865 100644
--- a/lib/std/zig/ast.zig
+++ b/lib/std/zig/ast.zig
@@ -323,8 +323,8 @@ pub const Error = union(enum) {
node: *Node,
pub fn render(self: *const ExpectedCall, tokens: []const Token.Id, stream: anytype) !void {
- return stream.print("expected " ++ @tagName(Node.Id.Call) ++ ", found {}", .{
- @tagName(self.node.id),
+ return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ ", found {}", .{
+ @tagName(self.node.tag),
});
}
};
@@ -333,8 +333,8 @@ pub const Error = union(enum) {
node: *Node,
pub fn render(self: *const ExpectedCallOrFnProto, tokens: []const Token.Id, stream: anytype) !void {
- return stream.print("expected " ++ @tagName(Node.Id.Call) ++ " or " ++
- @tagName(Node.Id.FnProto) ++ ", found {}", .{@tagName(self.node.id)});
+ return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ " or " ++
+ @tagName(Node.Tag.FnProto) ++ ", found {}", .{@tagName(self.node.tag)});
}
};
@@ -396,9 +396,9 @@ pub const Error = union(enum) {
};
pub const Node = struct {
- id: Id,
+ tag: Tag,
- pub const Id = enum {
+ pub const Tag = enum {
// Top level
Root,
Use,
@@ -408,8 +408,54 @@ pub const Node = struct {
VarDecl,
Defer,
- // Operators
- InfixOp,
+ // Infix operators
+ Catch,
+
+ // SimpleInfixOp
+ Add,
+ AddWrap,
+ ArrayCat,
+ ArrayMult,
+ Assign,
+ AssignBitAnd,
+ AssignBitOr,
+ AssignBitShiftLeft,
+ AssignBitShiftRight,
+ AssignBitXor,
+ AssignDiv,
+ AssignSub,
+ AssignSubWrap,
+ AssignMod,
+ AssignAdd,
+ AssignAddWrap,
+ AssignMul,
+ AssignMulWrap,
+ BangEqual,
+ BitAnd,
+ BitOr,
+ BitShiftLeft,
+ BitShiftRight,
+ BitXor,
+ BoolAnd,
+ BoolOr,
+ Div,
+ EqualEqual,
+ ErrorUnion,
+ GreaterOrEqual,
+ GreaterThan,
+ LessOrEqual,
+ LessThan,
+ MergeErrorSets,
+ Mod,
+ Mul,
+ MulWrap,
+ Period,
+ Range,
+ Sub,
+ SubWrap,
+ UnwrapOptional,
+
+ // SimplePrefixOp
AddressOf,
Await,
BitNot,
@@ -419,6 +465,7 @@ pub const Node = struct {
NegationWrap,
Resume,
Try,
+
ArrayType,
/// ArrayType but has a sentinel node.
ArrayTypeSentinel,
@@ -484,49 +531,177 @@ pub const Node = struct {
ContainerField,
ErrorTag,
FieldInitializer,
+
+ pub fn Type(tag: Tag) type {
+ return switch (tag) {
+ .Root => Root,
+ .Use => Use,
+ .TestDecl => TestDecl,
+ .VarDecl => VarDecl,
+ .Defer => Defer,
+ .Catch => Catch,
+
+ .Add,
+ .AddWrap,
+ .ArrayCat,
+ .ArrayMult,
+ .Assign,
+ .AssignBitAnd,
+ .AssignBitOr,
+ .AssignBitShiftLeft,
+ .AssignBitShiftRight,
+ .AssignBitXor,
+ .AssignDiv,
+ .AssignSub,
+ .AssignSubWrap,
+ .AssignMod,
+ .AssignAdd,
+ .AssignAddWrap,
+ .AssignMul,
+ .AssignMulWrap,
+ .BangEqual,
+ .BitAnd,
+ .BitOr,
+ .BitShiftLeft,
+ .BitShiftRight,
+ .BitXor,
+ .BoolAnd,
+ .BoolOr,
+ .Div,
+ .EqualEqual,
+ .ErrorUnion,
+ .GreaterOrEqual,
+ .GreaterThan,
+ .LessOrEqual,
+ .LessThan,
+ .MergeErrorSets,
+ .Mod,
+ .Mul,
+ .MulWrap,
+ .Period,
+ .Range,
+ .Sub,
+ .SubWrap,
+ .UnwrapOptional,
+ => SimpleInfixOp,
+
+ .AddressOf,
+ .Await,
+ .BitNot,
+ .BoolNot,
+ .OptionalType,
+ .Negation,
+ .NegationWrap,
+ .Resume,
+ .Try,
+ => SimplePrefixOp,
+
+ .ArrayType => ArrayType,
+ .ArrayTypeSentinel => ArrayTypeSentinel,
+
+ .PtrType => PtrType,
+ .SliceType => SliceType,
+ .SuffixOp => SuffixOp,
+
+ .ArrayInitializer => ArrayInitializer,
+ .ArrayInitializerDot => ArrayInitializerDot,
+
+ .StructInitializer => StructInitializer,
+ .StructInitializerDot => StructInitializerDot,
+
+ .Call => Call,
+ .Switch => Switch,
+ .While => While,
+ .For => For,
+ .If => If,
+ .ControlFlowExpression => ControlFlowExpression,
+ .Suspend => Suspend,
+ .AnyType => AnyType,
+ .ErrorType => ErrorType,
+ .FnProto => FnProto,
+ .AnyFrameType => AnyFrameType,
+ .IntegerLiteral => IntegerLiteral,
+ .FloatLiteral => FloatLiteral,
+ .EnumLiteral => EnumLiteral,
+ .StringLiteral => StringLiteral,
+ .MultilineStringLiteral => MultilineStringLiteral,
+ .CharLiteral => CharLiteral,
+ .BoolLiteral => BoolLiteral,
+ .NullLiteral => NullLiteral,
+ .UndefinedLiteral => UndefinedLiteral,
+ .Unreachable => Unreachable,
+ .Identifier => Identifier,
+ .GroupedExpression => GroupedExpression,
+ .BuiltinCall => BuiltinCall,
+ .ErrorSetDecl => ErrorSetDecl,
+ .ContainerDecl => ContainerDecl,
+ .Asm => Asm,
+ .Comptime => Comptime,
+ .Nosuspend => Nosuspend,
+ .Block => Block,
+ .DocComment => DocComment,
+ .SwitchCase => SwitchCase,
+ .SwitchElse => SwitchElse,
+ .Else => Else,
+ .Payload => Payload,
+ .PointerPayload => PointerPayload,
+ .PointerIndexPayload => PointerIndexPayload,
+ .ContainerField => ContainerField,
+ .ErrorTag => ErrorTag,
+ .FieldInitializer => FieldInitializer,
+ };
+ }
};
+ /// Prefer `castTag` to this.
pub fn cast(base: *Node, comptime T: type) ?*T {
- if (base.id == comptime typeToId(T)) {
- return @fieldParentPtr(T, "base", base);
+ if (std.meta.fieldInfo(T, "base").default_value) |default_base| {
+ return base.castTag(default_base.tag);
+ }
+ inline for (@typeInfo(Tag).Enum.fields) |field| {
+ const tag = @intToEnum(Tag, field.value);
+ if (base.tag == tag) {
+ if (T == tag.Type()) {
+ return @fieldParentPtr(T, "base", base);
+ }
+ return null;
+ }
+ }
+ unreachable;
+ }
+
+ pub fn castTag(base: *Node, comptime tag: Tag) ?*tag.Type() {
+ if (base.tag == tag) {
+ return @fieldParentPtr(tag.Type(), "base", base);
}
return null;
}
pub fn iterate(base: *Node, index: usize) ?*Node {
- inline for (@typeInfo(Id).Enum.fields) |f| {
- if (base.id == @field(Id, f.name)) {
- const T = @field(Node, f.name);
- return @fieldParentPtr(T, "base", base).iterate(index);
+ inline for (@typeInfo(Tag).Enum.fields) |field| {
+ const tag = @intToEnum(Tag, field.value);
+ if (base.tag == tag) {
+ return @fieldParentPtr(tag.Type(), "base", base).iterate(index);
}
}
unreachable;
}
pub fn firstToken(base: *const Node) TokenIndex {
- inline for (@typeInfo(Id).Enum.fields) |f| {
- if (base.id == @field(Id, f.name)) {
- const T = @field(Node, f.name);
- return @fieldParentPtr(T, "base", base).firstToken();
+ inline for (@typeInfo(Tag).Enum.fields) |field| {
+ const tag = @intToEnum(Tag, field.value);
+ if (base.tag == tag) {
+ return @fieldParentPtr(tag.Type(), "base", base).firstToken();
}
}
unreachable;
}
pub fn lastToken(base: *const Node) TokenIndex {
- inline for (@typeInfo(Id).Enum.fields) |f| {
- if (base.id == @field(Id, f.name)) {
- const T = @field(Node, f.name);
- return @fieldParentPtr(T, "base", base).lastToken();
- }
- }
- unreachable;
- }
-
- pub fn typeToId(comptime T: type) Id {
- inline for (@typeInfo(Id).Enum.fields) |f| {
- if (T == @field(Node, f.name)) {
- return @field(Id, f.name);
+ inline for (@typeInfo(Tag).Enum.fields) |field| {
+ const tag = @intToEnum(Tag, field.value);
+ if (base.tag == tag) {
+ return @fieldParentPtr(tag.Type(), "base", base).lastToken();
}
}
unreachable;
@@ -535,7 +710,7 @@ pub const Node = struct {
pub fn requireSemiColon(base: *const Node) bool {
var n = base;
while (true) {
- switch (n.id) {
+ switch (n.tag) {
.Root,
.ContainerField,
.Block,
@@ -556,7 +731,7 @@ pub const Node = struct {
continue;
}
- return while_node.body.id != .Block;
+ return while_node.body.tag != .Block;
},
.For => {
const for_node = @fieldParentPtr(For, "base", n);
@@ -565,7 +740,7 @@ pub const Node = struct {
continue;
}
- return for_node.body.id != .Block;
+ return for_node.body.tag != .Block;
},
.If => {
const if_node = @fieldParentPtr(If, "base", n);
@@ -574,7 +749,7 @@ pub const Node = struct {
continue;
}
- return if_node.body.id != .Block;
+ return if_node.body.tag != .Block;
},
.Else => {
const else_node = @fieldParentPtr(Else, "base", n);
@@ -583,23 +758,23 @@ pub const Node = struct {
},
.Defer => {
const defer_node = @fieldParentPtr(Defer, "base", n);
- return defer_node.expr.id != .Block;
+ return defer_node.expr.tag != .Block;
},
.Comptime => {
const comptime_node = @fieldParentPtr(Comptime, "base", n);
- return comptime_node.expr.id != .Block;
+ return comptime_node.expr.tag != .Block;
},
.Suspend => {
const suspend_node = @fieldParentPtr(Suspend, "base", n);
if (suspend_node.body) |body| {
- return body.id != .Block;
+ return body.tag != .Block;
}
return true;
},
.Nosuspend => {
const nosuspend_node = @fieldParentPtr(Nosuspend, "base", n);
- return nosuspend_node.expr.id != .Block;
+ return nosuspend_node.expr.tag != .Block;
},
else => return true,
}
@@ -613,7 +788,7 @@ pub const Node = struct {
std.debug.warn(" ", .{});
}
}
- std.debug.warn("{}\n", .{@tagName(self.id)});
+ std.debug.warn("{}\n", .{@tagName(self.tag)});
var child_i: usize = 0;
while (self.iterate(child_i)) |child| : (child_i += 1) {
@@ -623,7 +798,7 @@ pub const Node = struct {
/// The decls data follows this struct in memory as an array of Node pointers.
pub const Root = struct {
- base: Node = Node{ .id = .Root },
+ base: Node = Node{ .tag = .Root },
eof_token: TokenIndex,
decls_len: NodeIndex,
@@ -678,7 +853,7 @@ pub const Node = struct {
/// Trailed in memory by possibly many things, with each optional thing
/// determined by a bit in `trailer_flags`.
pub const VarDecl = struct {
- base: Node = Node{ .id = .VarDecl },
+ base: Node = Node{ .tag = .VarDecl },
trailer_flags: TrailerFlags,
mut_token: TokenIndex,
name_token: TokenIndex,
@@ -779,7 +954,7 @@ pub const Node = struct {
};
pub const Use = struct {
- base: Node = Node{ .id = .Use },
+ base: Node = Node{ .tag = .Use },
doc_comments: ?*DocComment,
visib_token: ?TokenIndex,
use_token: TokenIndex,
@@ -806,7 +981,7 @@ pub const Node = struct {
};
pub const ErrorSetDecl = struct {
- base: Node = Node{ .id = .ErrorSetDecl },
+ base: Node = Node{ .tag = .ErrorSetDecl },
error_token: TokenIndex,
rbrace_token: TokenIndex,
decls_len: NodeIndex,
@@ -856,7 +1031,7 @@ pub const Node = struct {
/// The fields and decls Node pointers directly follow this struct in memory.
pub const ContainerDecl = struct {
- base: Node = Node{ .id = .ContainerDecl },
+ base: Node = Node{ .tag = .ContainerDecl },
kind_token: TokenIndex,
layout_token: ?TokenIndex,
lbrace_token: TokenIndex,
@@ -925,7 +1100,7 @@ pub const Node = struct {
};
pub const ContainerField = struct {
- base: Node = Node{ .id = .ContainerField },
+ base: Node = Node{ .tag = .ContainerField },
doc_comments: ?*DocComment,
comptime_token: ?TokenIndex,
name_token: TokenIndex,
@@ -976,7 +1151,7 @@ pub const Node = struct {
};
pub const ErrorTag = struct {
- base: Node = Node{ .id = .ErrorTag },
+ base: Node = Node{ .tag = .ErrorTag },
doc_comments: ?*DocComment,
name_token: TokenIndex,
@@ -1001,7 +1176,7 @@ pub const Node = struct {
};
pub const Identifier = struct {
- base: Node = Node{ .id = .Identifier },
+ base: Node = Node{ .tag = .Identifier },
token: TokenIndex,
pub fn iterate(self: *const Identifier, index: usize) ?*Node {
@@ -1020,7 +1195,7 @@ pub const Node = struct {
/// The params are directly after the FnProto in memory.
/// Next, each optional thing determined by a bit in `trailer_flags`.
pub const FnProto = struct {
- base: Node = Node{ .id = .FnProto },
+ base: Node = Node{ .tag = .FnProto },
trailer_flags: TrailerFlags,
fn_token: TokenIndex,
params_len: NodeIndex,
@@ -1230,7 +1405,7 @@ pub const Node = struct {
};
pub const AnyFrameType = struct {
- base: Node = Node{ .id = .AnyFrameType },
+ base: Node = Node{ .tag = .AnyFrameType },
anyframe_token: TokenIndex,
result: ?Result,
@@ -1262,7 +1437,7 @@ pub const Node = struct {
/// The statements of the block follow Block directly in memory.
pub const Block = struct {
- base: Node = Node{ .id = .Block },
+ base: Node = Node{ .tag = .Block },
statements_len: NodeIndex,
lbrace: TokenIndex,
rbrace: TokenIndex,
@@ -1316,7 +1491,7 @@ pub const Node = struct {
};
pub const Defer = struct {
- base: Node = Node{ .id = .Defer },
+ base: Node = Node{ .tag = .Defer },
defer_token: TokenIndex,
payload: ?*Node,
expr: *Node,
@@ -1340,7 +1515,7 @@ pub const Node = struct {
};
pub const Comptime = struct {
- base: Node = Node{ .id = .Comptime },
+ base: Node = Node{ .tag = .Comptime },
doc_comments: ?*DocComment,
comptime_token: TokenIndex,
expr: *Node,
@@ -1364,7 +1539,7 @@ pub const Node = struct {
};
pub const Nosuspend = struct {
- base: Node = Node{ .id = .Nosuspend },
+ base: Node = Node{ .tag = .Nosuspend },
nosuspend_token: TokenIndex,
expr: *Node,
@@ -1387,7 +1562,7 @@ pub const Node = struct {
};
pub const Payload = struct {
- base: Node = Node{ .id = .Payload },
+ base: Node = Node{ .tag = .Payload },
lpipe: TokenIndex,
error_symbol: *Node,
rpipe: TokenIndex,
@@ -1411,7 +1586,7 @@ pub const Node = struct {
};
pub const PointerPayload = struct {
- base: Node = Node{ .id = .PointerPayload },
+ base: Node = Node{ .tag = .PointerPayload },
lpipe: TokenIndex,
ptr_token: ?TokenIndex,
value_symbol: *Node,
@@ -1436,7 +1611,7 @@ pub const Node = struct {
};
pub const PointerIndexPayload = struct {
- base: Node = Node{ .id = .PointerIndexPayload },
+ base: Node = Node{ .tag = .PointerIndexPayload },
lpipe: TokenIndex,
ptr_token: ?TokenIndex,
value_symbol: *Node,
@@ -1467,7 +1642,7 @@ pub const Node = struct {
};
pub const Else = struct {
- base: Node = Node{ .id = .Else },
+ base: Node = Node{ .tag = .Else },
else_token: TokenIndex,
payload: ?*Node,
body: *Node,
@@ -1498,7 +1673,7 @@ pub const Node = struct {
/// The cases node pointers are found in memory after Switch.
/// They must be SwitchCase or SwitchElse nodes.
pub const Switch = struct {
- base: Node = Node{ .id = .Switch },
+ base: Node = Node{ .tag = .Switch },
switch_token: TokenIndex,
rbrace: TokenIndex,
cases_len: NodeIndex,
@@ -1552,7 +1727,7 @@ pub const Node = struct {
/// Items sub-nodes appear in memory directly following SwitchCase.
pub const SwitchCase = struct {
- base: Node = Node{ .id = .SwitchCase },
+ base: Node = Node{ .tag = .SwitchCase },
arrow_token: TokenIndex,
payload: ?*Node,
expr: *Node,
@@ -1610,7 +1785,7 @@ pub const Node = struct {
};
pub const SwitchElse = struct {
- base: Node = Node{ .id = .SwitchElse },
+ base: Node = Node{ .tag = .SwitchElse },
token: TokenIndex,
pub fn iterate(self: *const SwitchElse, index: usize) ?*Node {
@@ -1627,7 +1802,7 @@ pub const Node = struct {
};
pub const While = struct {
- base: Node = Node{ .id = .While },
+ base: Node = Node{ .tag = .While },
label: ?TokenIndex,
inline_token: ?TokenIndex,
while_token: TokenIndex,
@@ -1686,7 +1861,7 @@ pub const Node = struct {
};
pub const For = struct {
- base: Node = Node{ .id = .For },
+ base: Node = Node{ .tag = .For },
label: ?TokenIndex,
inline_token: ?TokenIndex,
for_token: TokenIndex,
@@ -1737,7 +1912,7 @@ pub const Node = struct {
};
pub const If = struct {
- base: Node = Node{ .id = .If },
+ base: Node = Node{ .tag = .If },
if_token: TokenIndex,
condition: *Node,
payload: ?*Node,
@@ -1779,116 +1954,22 @@ pub const Node = struct {
}
};
- pub const InfixOp = struct {
- base: Node = Node{ .id = .InfixOp },
+ pub const Catch = struct {
+ base: Node = Node{ .tag = .Catch },
op_token: TokenIndex,
lhs: *Node,
- op: Op,
rhs: *Node,
+ payload: ?*Node,
- pub const Op = union(enum) {
- Add,
- AddWrap,
- ArrayCat,
- ArrayMult,
- Assign,
- AssignBitAnd,
- AssignBitOr,
- AssignBitShiftLeft,
- AssignBitShiftRight,
- AssignBitXor,
- AssignDiv,
- AssignSub,
- AssignSubWrap,
- AssignMod,
- AssignAdd,
- AssignAddWrap,
- AssignMul,
- AssignMulWrap,
- BangEqual,
- BitAnd,
- BitOr,
- BitShiftLeft,
- BitShiftRight,
- BitXor,
- BoolAnd,
- BoolOr,
- Catch: ?*Node,
- Div,
- EqualEqual,
- ErrorUnion,
- GreaterOrEqual,
- GreaterThan,
- LessOrEqual,
- LessThan,
- MergeErrorSets,
- Mod,
- Mul,
- MulWrap,
- Period,
- Range,
- Sub,
- SubWrap,
- UnwrapOptional,
- };
-
- pub fn iterate(self: *const InfixOp, index: usize) ?*Node {
+ pub fn iterate(self: *const Catch, index: usize) ?*Node {
var i = index;
if (i < 1) return self.lhs;
i -= 1;
- switch (self.op) {
- .Catch => |maybe_payload| {
- if (maybe_payload) |payload| {
- if (i < 1) return payload;
- i -= 1;
- }
- },
-
- .Add,
- .AddWrap,
- .ArrayCat,
- .ArrayMult,
- .Assign,
- .AssignBitAnd,
- .AssignBitOr,
- .AssignBitShiftLeft,
- .AssignBitShiftRight,
- .AssignBitXor,
- .AssignDiv,
- .AssignSub,
- .AssignSubWrap,
- .AssignMod,
- .AssignAdd,
- .AssignAddWrap,
- .AssignMul,
- .AssignMulWrap,
- .BangEqual,
- .BitAnd,
- .BitOr,
- .BitShiftLeft,
- .BitShiftRight,
- .BitXor,
- .BoolAnd,
- .BoolOr,
- .Div,
- .EqualEqual,
- .ErrorUnion,
- .GreaterOrEqual,
- .GreaterThan,
- .LessOrEqual,
- .LessThan,
- .MergeErrorSets,
- .Mod,
- .Mul,
- .MulWrap,
- .Period,
- .Range,
- .Sub,
- .SubWrap,
- .UnwrapOptional,
- => {},
+ if (self.payload) |payload| {
+ if (i < 1) return payload;
+ i -= 1;
}
if (i < 1) return self.rhs;
@@ -1897,50 +1978,65 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: *const InfixOp) TokenIndex {
+ pub fn firstToken(self: *const Catch) TokenIndex {
return self.lhs.firstToken();
}
- pub fn lastToken(self: *const InfixOp) TokenIndex {
+ pub fn lastToken(self: *const Catch) TokenIndex {
return self.rhs.lastToken();
}
};
- pub const AddressOf = SimplePrefixOp(.AddressOf);
- pub const Await = SimplePrefixOp(.Await);
- pub const BitNot = SimplePrefixOp(.BitNot);
- pub const BoolNot = SimplePrefixOp(.BoolNot);
- pub const OptionalType = SimplePrefixOp(.OptionalType);
- pub const Negation = SimplePrefixOp(.Negation);
- pub const NegationWrap = SimplePrefixOp(.NegationWrap);
- pub const Resume = SimplePrefixOp(.Resume);
- pub const Try = SimplePrefixOp(.Try);
+ pub const SimpleInfixOp = struct {
+ base: Node,
+ op_token: TokenIndex,
+ lhs: *Node,
+ rhs: *Node,
- pub fn SimplePrefixOp(comptime tag: Id) type {
- return struct {
- base: Node = Node{ .id = tag },
- op_token: TokenIndex,
- rhs: *Node,
+ pub fn iterate(self: *const SimpleInfixOp, index: usize) ?*Node {
+ var i = index;
- const Self = @This();
+ if (i < 1) return self.lhs;
+ i -= 1;
- pub fn iterate(self: *const Self, index: usize) ?*Node {
- if (index == 0) return self.rhs;
- return null;
- }
+ if (i < 1) return self.rhs;
+ i -= 1;
- pub fn firstToken(self: *const Self) TokenIndex {
- return self.op_token;
- }
+ return null;
+ }
- pub fn lastToken(self: *const Self) TokenIndex {
- return self.rhs.lastToken();
- }
- };
- }
+ pub fn firstToken(self: *const SimpleInfixOp) TokenIndex {
+ return self.lhs.firstToken();
+ }
+
+ pub fn lastToken(self: *const SimpleInfixOp) TokenIndex {
+ return self.rhs.lastToken();
+ }
+ };
+
+ pub const SimplePrefixOp = struct {
+ base: Node,
+ op_token: TokenIndex,
+ rhs: *Node,
+
+ const Self = @This();
+
+ pub fn iterate(self: *const Self, index: usize) ?*Node {
+ if (index == 0) return self.rhs;
+ return null;
+ }
+
+ pub fn firstToken(self: *const Self) TokenIndex {
+ return self.op_token;
+ }
+
+ pub fn lastToken(self: *const Self) TokenIndex {
+ return self.rhs.lastToken();
+ }
+ };
pub const ArrayType = struct {
- base: Node = Node{ .id = .ArrayType },
+ base: Node = Node{ .tag = .ArrayType },
op_token: TokenIndex,
rhs: *Node,
len_expr: *Node,
@@ -1967,7 +2063,7 @@ pub const Node = struct {
};
pub const ArrayTypeSentinel = struct {
- base: Node = Node{ .id = .ArrayTypeSentinel },
+ base: Node = Node{ .tag = .ArrayTypeSentinel },
op_token: TokenIndex,
rhs: *Node,
len_expr: *Node,
@@ -1998,7 +2094,7 @@ pub const Node = struct {
};
pub const PtrType = struct {
- base: Node = Node{ .id = .PtrType },
+ base: Node = Node{ .tag = .PtrType },
op_token: TokenIndex,
rhs: *Node,
/// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents
@@ -2034,7 +2130,7 @@ pub const Node = struct {
};
pub const SliceType = struct {
- base: Node = Node{ .id = .SliceType },
+ base: Node = Node{ .tag = .SliceType },
op_token: TokenIndex,
rhs: *Node,
/// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents
@@ -2070,7 +2166,7 @@ pub const Node = struct {
};
pub const FieldInitializer = struct {
- base: Node = Node{ .id = .FieldInitializer },
+ base: Node = Node{ .tag = .FieldInitializer },
period_token: TokenIndex,
name_token: TokenIndex,
expr: *Node,
@@ -2095,7 +2191,7 @@ pub const Node = struct {
/// Elements occur directly in memory after ArrayInitializer.
pub const ArrayInitializer = struct {
- base: Node = Node{ .id = .ArrayInitializer },
+ base: Node = Node{ .tag = .ArrayInitializer },
rtoken: TokenIndex,
list_len: NodeIndex,
lhs: *Node,
@@ -2148,7 +2244,7 @@ pub const Node = struct {
/// Elements occur directly in memory after ArrayInitializerDot.
pub const ArrayInitializerDot = struct {
- base: Node = Node{ .id = .ArrayInitializerDot },
+ base: Node = Node{ .tag = .ArrayInitializerDot },
dot: TokenIndex,
rtoken: TokenIndex,
list_len: NodeIndex,
@@ -2198,7 +2294,7 @@ pub const Node = struct {
/// Elements occur directly in memory after StructInitializer.
pub const StructInitializer = struct {
- base: Node = Node{ .id = .StructInitializer },
+ base: Node = Node{ .tag = .StructInitializer },
rtoken: TokenIndex,
list_len: NodeIndex,
lhs: *Node,
@@ -2251,7 +2347,7 @@ pub const Node = struct {
/// Elements occur directly in memory after StructInitializerDot.
pub const StructInitializerDot = struct {
- base: Node = Node{ .id = .StructInitializerDot },
+ base: Node = Node{ .tag = .StructInitializerDot },
dot: TokenIndex,
rtoken: TokenIndex,
list_len: NodeIndex,
@@ -2301,7 +2397,7 @@ pub const Node = struct {
/// Parameter nodes directly follow Call in memory.
pub const Call = struct {
- base: Node = Node{ .id = .Call },
+ base: Node = Node{ .tag = .Call },
lhs: *Node,
rtoken: TokenIndex,
params_len: NodeIndex,
@@ -2355,7 +2451,7 @@ pub const Node = struct {
};
pub const SuffixOp = struct {
- base: Node = Node{ .id = .SuffixOp },
+ base: Node = Node{ .tag = .SuffixOp },
op: Op,
lhs: *Node,
rtoken: TokenIndex,
@@ -2415,7 +2511,7 @@ pub const Node = struct {
};
pub const GroupedExpression = struct {
- base: Node = Node{ .id = .GroupedExpression },
+ base: Node = Node{ .tag = .GroupedExpression },
lparen: TokenIndex,
expr: *Node,
rparen: TokenIndex,
@@ -2441,7 +2537,7 @@ pub const Node = struct {
/// TODO break this into separate Break, Continue, Return AST Nodes to save memory.
/// Could be further broken into LabeledBreak, LabeledContinue, and ReturnVoid to save even more.
pub const ControlFlowExpression = struct {
- base: Node = Node{ .id = .ControlFlowExpression },
+ base: Node = Node{ .tag = .ControlFlowExpression },
ltoken: TokenIndex,
kind: Kind,
rhs: ?*Node,
@@ -2496,7 +2592,7 @@ pub const Node = struct {
};
pub const Suspend = struct {
- base: Node = Node{ .id = .Suspend },
+ base: Node = Node{ .tag = .Suspend },
suspend_token: TokenIndex,
body: ?*Node,
@@ -2525,7 +2621,7 @@ pub const Node = struct {
};
pub const IntegerLiteral = struct {
- base: Node = Node{ .id = .IntegerLiteral },
+ base: Node = Node{ .tag = .IntegerLiteral },
token: TokenIndex,
pub fn iterate(self: *const IntegerLiteral, index: usize) ?*Node {
@@ -2542,7 +2638,7 @@ pub const Node = struct {
};
pub const EnumLiteral = struct {
- base: Node = Node{ .id = .EnumLiteral },
+ base: Node = Node{ .tag = .EnumLiteral },
dot: TokenIndex,
name: TokenIndex,
@@ -2560,7 +2656,7 @@ pub const Node = struct {
};
pub const FloatLiteral = struct {
- base: Node = Node{ .id = .FloatLiteral },
+ base: Node = Node{ .tag = .FloatLiteral },
token: TokenIndex,
pub fn iterate(self: *const FloatLiteral, index: usize) ?*Node {
@@ -2578,7 +2674,7 @@ pub const Node = struct {
/// Parameters are in memory following BuiltinCall.
pub const BuiltinCall = struct {
- base: Node = Node{ .id = .BuiltinCall },
+ base: Node = Node{ .tag = .BuiltinCall },
params_len: NodeIndex,
builtin_token: TokenIndex,
rparen_token: TokenIndex,
@@ -2627,7 +2723,7 @@ pub const Node = struct {
};
pub const StringLiteral = struct {
- base: Node = Node{ .id = .StringLiteral },
+ base: Node = Node{ .tag = .StringLiteral },
token: TokenIndex,
pub fn iterate(self: *const StringLiteral, index: usize) ?*Node {
@@ -2645,7 +2741,7 @@ pub const Node = struct {
/// The string literal tokens appear directly in memory after MultilineStringLiteral.
pub const MultilineStringLiteral = struct {
- base: Node = Node{ .id = .MultilineStringLiteral },
+ base: Node = Node{ .tag = .MultilineStringLiteral },
lines_len: TokenIndex,
/// After this the caller must initialize the lines list.
@@ -2687,7 +2783,7 @@ pub const Node = struct {
};
pub const CharLiteral = struct {
- base: Node = Node{ .id = .CharLiteral },
+ base: Node = Node{ .tag = .CharLiteral },
token: TokenIndex,
pub fn iterate(self: *const CharLiteral, index: usize) ?*Node {
@@ -2704,7 +2800,7 @@ pub const Node = struct {
};
pub const BoolLiteral = struct {
- base: Node = Node{ .id = .BoolLiteral },
+ base: Node = Node{ .tag = .BoolLiteral },
token: TokenIndex,
pub fn iterate(self: *const BoolLiteral, index: usize) ?*Node {
@@ -2721,7 +2817,7 @@ pub const Node = struct {
};
pub const NullLiteral = struct {
- base: Node = Node{ .id = .NullLiteral },
+ base: Node = Node{ .tag = .NullLiteral },
token: TokenIndex,
pub fn iterate(self: *const NullLiteral, index: usize) ?*Node {
@@ -2738,7 +2834,7 @@ pub const Node = struct {
};
pub const UndefinedLiteral = struct {
- base: Node = Node{ .id = .UndefinedLiteral },
+ base: Node = Node{ .tag = .UndefinedLiteral },
token: TokenIndex,
pub fn iterate(self: *const UndefinedLiteral, index: usize) ?*Node {
@@ -2755,7 +2851,7 @@ pub const Node = struct {
};
pub const Asm = struct {
- base: Node = Node{ .id = .Asm },
+ base: Node = Node{ .tag = .Asm },
asm_token: TokenIndex,
rparen: TokenIndex,
volatile_token: ?TokenIndex,
@@ -2875,7 +2971,7 @@ pub const Node = struct {
};
pub const Unreachable = struct {
- base: Node = Node{ .id = .Unreachable },
+ base: Node = Node{ .tag = .Unreachable },
token: TokenIndex,
pub fn iterate(self: *const Unreachable, index: usize) ?*Node {
@@ -2892,7 +2988,7 @@ pub const Node = struct {
};
pub const ErrorType = struct {
- base: Node = Node{ .id = .ErrorType },
+ base: Node = Node{ .tag = .ErrorType },
token: TokenIndex,
pub fn iterate(self: *const ErrorType, index: usize) ?*Node {
@@ -2909,7 +3005,7 @@ pub const Node = struct {
};
pub const AnyType = struct {
- base: Node = Node{ .id = .AnyType },
+ base: Node = Node{ .tag = .AnyType },
token: TokenIndex,
pub fn iterate(self: *const AnyType, index: usize) ?*Node {
@@ -2929,7 +3025,7 @@ pub const Node = struct {
/// TODO actually maybe remove entirely in favor of iterating backward from Node.firstToken()
/// and forwards to find same-line doc comments.
pub const DocComment = struct {
- base: Node = Node{ .id = .DocComment },
+ base: Node = Node{ .tag = .DocComment },
/// Points to the first doc comment token. API users are expected to iterate over the
/// tokens array, looking for more doc comments, ignoring line comments, and stopping
/// at the first other token.
@@ -2951,7 +3047,7 @@ pub const Node = struct {
};
pub const TestDecl = struct {
- base: Node = Node{ .id = .TestDecl },
+ base: Node = Node{ .tag = .TestDecl },
doc_comments: ?*DocComment,
test_token: TokenIndex,
name: *Node,
@@ -2996,7 +3092,7 @@ pub const PtrInfo = struct {
test "iterate" {
var root = Node.Root{
- .base = Node{ .id = Node.Id.Root },
+ .base = Node{ .tag = Node.Tag.Root },
.decls_len = 0,
.eof_token = 0,
};
diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig
index 9f2aea390a..b02cdcc1fd 100644
--- a/lib/std/zig/parse.zig
+++ b/lib/std/zig/parse.zig
@@ -1015,7 +1015,7 @@ const Parser = struct {
/// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*
fn parseBoolOrExpr(p: *Parser) !?*Node {
return p.parseBinOpExpr(
- SimpleBinOpParseFn(.Keyword_or, Node.InfixOp.Op.BoolOr),
+ SimpleBinOpParseFn(.Keyword_or, .BoolOr),
parseBoolAndExpr,
.Infinitely,
);
@@ -1128,8 +1128,9 @@ const Parser = struct {
const expr_node = try p.expectNode(parseExpr, .{
.ExpectedExpr = .{ .token = p.tok_i },
});
- const node = try p.arena.allocator.create(Node.Resume);
+ const node = try p.arena.allocator.create(Node.SimplePrefixOp);
node.* = .{
+ .base = .{ .tag = .Resume },
.op_token = token,
.rhs = expr_node,
};
@@ -1404,8 +1405,8 @@ const Parser = struct {
fn parseErrorUnionExpr(p: *Parser) !?*Node {
const suffix_expr = (try p.parseSuffixExpr()) orelse return null;
- if (try SimpleBinOpParseFn(.Bang, Node.InfixOp.Op.ErrorUnion)(p)) |node| {
- const error_union = node.cast(Node.InfixOp).?;
+ if (try SimpleBinOpParseFn(.Bang, .ErrorUnion)(p)) |node| {
+ const error_union = node.castTag(.ErrorUnion).?;
const type_expr = try p.expectNode(parseTypeExpr, .{
.ExpectedTypeExpr = .{ .token = p.tok_i },
});
@@ -1438,10 +1439,56 @@ const Parser = struct {
.ExpectedPrimaryTypeExpr = .{ .token = p.tok_i },
});
+ // TODO pass `res` into `parseSuffixOp` rather than patching it up afterwards.
while (try p.parseSuffixOp()) |node| {
- switch (node.id) {
+ switch (node.tag) {
.SuffixOp => node.cast(Node.SuffixOp).?.lhs = res,
- .InfixOp => node.cast(Node.InfixOp).?.lhs = res,
+ .Catch => node.castTag(.Catch).?.lhs = res,
+
+ .Add,
+ .AddWrap,
+ .ArrayCat,
+ .ArrayMult,
+ .Assign,
+ .AssignBitAnd,
+ .AssignBitOr,
+ .AssignBitShiftLeft,
+ .AssignBitShiftRight,
+ .AssignBitXor,
+ .AssignDiv,
+ .AssignSub,
+ .AssignSubWrap,
+ .AssignMod,
+ .AssignAdd,
+ .AssignAddWrap,
+ .AssignMul,
+ .AssignMulWrap,
+ .BangEqual,
+ .BitAnd,
+ .BitOr,
+ .BitShiftLeft,
+ .BitShiftRight,
+ .BitXor,
+ .BoolAnd,
+ .BoolOr,
+ .Div,
+ .EqualEqual,
+ .ErrorUnion,
+ .GreaterOrEqual,
+ .GreaterThan,
+ .LessOrEqual,
+ .LessThan,
+ .MergeErrorSets,
+ .Mod,
+ .Mul,
+ .MulWrap,
+ .Period,
+ .Range,
+ .Sub,
+ .SubWrap,
+ .UnwrapOptional,
+ => node.cast(Node.SimpleInfixOp).?.lhs = res,
+
else => unreachable,
}
res = node;
@@ -1469,10 +1516,55 @@ const Parser = struct {
var res = expr;
while (true) {
+ // TODO pass `res` into `parseSuffixOp` rather than patching it up afterwards.
if (try p.parseSuffixOp()) |node| {
- switch (node.id) {
+ switch (node.tag) {
.SuffixOp => node.cast(Node.SuffixOp).?.lhs = res,
- .InfixOp => node.cast(Node.InfixOp).?.lhs = res,
+ .Catch => node.castTag(.Catch).?.lhs = res,
+
+ .Add,
+ .AddWrap,
+ .ArrayCat,
+ .ArrayMult,
+ .Assign,
+ .AssignBitAnd,
+ .AssignBitOr,
+ .AssignBitShiftLeft,
+ .AssignBitShiftRight,
+ .AssignBitXor,
+ .AssignDiv,
+ .AssignSub,
+ .AssignSubWrap,
+ .AssignMod,
+ .AssignAdd,
+ .AssignAddWrap,
+ .AssignMul,
+ .AssignMulWrap,
+ .BangEqual,
+ .BitAnd,
+ .BitOr,
+ .BitShiftLeft,
+ .BitShiftRight,
+ .BitXor,
+ .BoolAnd,
+ .BoolOr,
+ .Div,
+ .EqualEqual,
+ .ErrorUnion,
+ .GreaterOrEqual,
+ .GreaterThan,
+ .LessOrEqual,
+ .LessThan,
+ .MergeErrorSets,
+ .Mod,
+ .Mul,
+ .MulWrap,
+ .Period,
+ .Range,
+ .Sub,
+ .SubWrap,
+ .UnwrapOptional,
+ => node.cast(Node.SimpleInfixOp).?.lhs = res,
else => unreachable,
}
res = node;
@@ -1559,11 +1651,11 @@ const Parser = struct {
const global_error_set = try p.createLiteral(Node.ErrorType, token);
if (period == null or identifier == null) return global_error_set;
- const node = try p.arena.allocator.create(Node.InfixOp);
+ const node = try p.arena.allocator.create(Node.SimpleInfixOp);
node.* = .{
+ .base = Node{ .tag = .Period },
.op_token = period.?,
.lhs = global_error_set,
- .op = .Period,
.rhs = identifier.?,
};
return &node.base;
@@ -1660,7 +1752,7 @@ const Parser = struct {
}
if (try p.parseLoopTypeExpr()) |node| {
- switch (node.id) {
+ switch (node.tag) {
.For => node.cast(Node.For).?.label = label,
.While => node.cast(Node.While).?.label = label,
else => unreachable,
@@ -2236,11 +2328,11 @@ const Parser = struct {
.ExpectedExpr = .{ .token = p.tok_i },
});
- const node = try p.arena.allocator.create(Node.InfixOp);
+ const node = try p.arena.allocator.create(Node.SimpleInfixOp);
node.* = .{
+ .base = Node{ .tag = .Range },
.op_token = token,
.lhs = expr,
- .op = .Range,
.rhs = range_end,
};
return &node.base;
@@ -2265,7 +2357,7 @@ const Parser = struct {
/// / EQUAL
fn parseAssignOp(p: *Parser) !?*Node {
const token = p.nextToken();
- const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
+ const op: Node.Tag = switch (p.token_ids[token]) {
.AsteriskEqual => .AssignMul,
.SlashEqual => .AssignDiv,
.PercentEqual => .AssignMod,
@@ -2286,11 +2378,11 @@ const Parser = struct {
},
};
- const node = try p.arena.allocator.create(Node.InfixOp);
+ const node = try p.arena.allocator.create(Node.SimpleInfixOp);
node.* = .{
+ .base = .{ .tag = op },
.op_token = token,
.lhs = undefined, // set by caller
- .op = op,
.rhs = undefined, // set by caller
};
return &node.base;
@@ -2305,7 +2397,7 @@ const Parser = struct {
/// / RARROWEQUAL
fn parseCompareOp(p: *Parser) !?*Node {
const token = p.nextToken();
- const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
+ const op: Node.Tag = switch (p.token_ids[token]) {
.EqualEqual => .EqualEqual,
.BangEqual => .BangEqual,
.AngleBracketLeft => .LessThan,
@@ -2329,12 +2421,22 @@ const Parser = struct {
/// / KEYWORD_catch Payload?
fn parseBitwiseOp(p: *Parser) !?*Node {
const token = p.nextToken();
- const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
+ const op: Node.Tag = switch (p.token_ids[token]) {
.Ampersand => .BitAnd,
.Caret => .BitXor,
.Pipe => .BitOr,
.Keyword_orelse => .UnwrapOptional,
- .Keyword_catch => .{ .Catch = try p.parsePayload() },
+ .Keyword_catch => {
+ const payload = try p.parsePayload();
+ const node = try p.arena.allocator.create(Node.Catch);
+ node.* = .{
+ .op_token = token,
+ .lhs = undefined, // set by caller
+ .rhs = undefined, // set by caller
+ .payload = payload,
+ };
+ return &node.base;
+ },
else => {
p.putBackToken(token);
return null;
@@ -2349,7 +2451,7 @@ const Parser = struct {
/// / RARROW2
fn parseBitShiftOp(p: *Parser) !?*Node {
const token = p.nextToken();
- const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
+ const op: Node.Tag = switch (p.token_ids[token]) {
.AngleBracketAngleBracketLeft => .BitShiftLeft,
.AngleBracketAngleBracketRight => .BitShiftRight,
else => {
@@ -2369,7 +2471,7 @@ const Parser = struct {
/// / MINUSPERCENT
fn parseAdditionOp(p: *Parser) !?*Node {
const token = p.nextToken();
- const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
+ const op: Node.Tag = switch (p.token_ids[token]) {
.Plus => .Add,
.Minus => .Sub,
.PlusPlus => .ArrayCat,
@@ -2393,7 +2495,7 @@ const Parser = struct {
/// / ASTERISKPERCENT
fn parseMultiplyOp(p: *Parser) !?*Node {
const token = p.nextToken();
- const op: Node.InfixOp.Op = switch (p.token_ids[token]) {
+ const op: Node.Tag = switch (p.token_ids[token]) {
.PipePipe => .MergeErrorSets,
.Asterisk => .Mul,
.Slash => .Div,
@@ -2434,9 +2536,10 @@ const Parser = struct {
}
}
- fn allocSimplePrefixOp(p: *Parser, comptime tag: Node.Id, token: TokenIndex) !?*Node {
- const node = try p.arena.allocator.create(Node.SimplePrefixOp(tag));
+ fn allocSimplePrefixOp(p: *Parser, comptime tag: Node.Tag, token: TokenIndex) !?*Node {
+ const node = try p.arena.allocator.create(Node.SimplePrefixOp);
node.* = .{
+ .base = .{ .tag = tag },
.op_token = token,
.rhs = undefined, // set by caller
};
@@ -2457,8 +2560,9 @@ const Parser = struct {
/// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
fn parsePrefixTypeOp(p: *Parser) !?*Node {
if (p.eatToken(.QuestionMark)) |token| {
- const node = try p.arena.allocator.create(Node.OptionalType);
+ const node = try p.arena.allocator.create(Node.SimplePrefixOp);
node.* = .{
+ .base = .{ .tag = .OptionalType },
.op_token = token,
.rhs = undefined, // set by caller
};
@@ -2670,14 +2774,14 @@ const Parser = struct {
if (p.eatToken(.Period)) |period| {
if (try p.parseIdentifier()) |identifier| {
- // TODO: It's a bit weird to return an InfixOp from the SuffixOp parser.
+ // TODO: It's a bit weird to return a SimpleInfixOp from the SuffixOp parser.
// Should there be an Node.SuffixOp.FieldAccess variant? Or should
// this grammar rule be altered?
- const node = try p.arena.allocator.create(Node.InfixOp);
+ const node = try p.arena.allocator.create(Node.SimpleInfixOp);
node.* = .{
+ .base = Node{ .tag = .Period },
.op_token = period,
.lhs = undefined, // set by caller
- .op = .Period,
.rhs = identifier,
};
return &node.base;
@@ -2984,7 +3088,7 @@ const Parser = struct {
}.parse;
}
- fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.InfixOp.Op) NodeParseFn {
+ fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.Tag) NodeParseFn {
return struct {
pub fn parse(p: *Parser) Error!?*Node {
const op_token = if (token == .Keyword_and) switch (p.token_ids[p.tok_i]) {
@@ -2998,11 +3102,11 @@ const Parser = struct {
else => return null,
} else p.eatToken(token) orelse return null;
- const node = try p.arena.allocator.create(Node.InfixOp);
+ const node = try p.arena.allocator.create(Node.SimpleInfixOp);
node.* = .{
+ .base = .{ .tag = op },
.op_token = op_token,
.lhs = undefined, // set by caller
- .op = op,
.rhs = undefined, // set by caller
};
return &node.base;
@@ -3072,7 +3176,6 @@ const Parser = struct {
fn createLiteral(p: *Parser, comptime T: type, token: TokenIndex) !*Node {
const result = try p.arena.allocator.create(T);
result.* = T{
- .base = Node{ .id = Node.typeToId(T) },
.token = token,
};
return &result.base;
@@ -3148,8 +3251,9 @@ const Parser = struct {
fn parseTry(p: *Parser) !?*Node {
const token = p.eatToken(.Keyword_try) orelse return null;
- const node = try p.arena.allocator.create(Node.Try);
+ const node = try p.arena.allocator.create(Node.SimplePrefixOp);
node.* = .{
+ .base = .{ .tag = .Try },
.op_token = token,
.rhs = undefined, // set by caller
};
@@ -3213,58 +3317,19 @@ const Parser = struct {
if (try opParseFn(p)) |first_op| {
var rightmost_op = first_op;
while (true) {
- switch (rightmost_op.id) {
- .AddressOf => {
- if (try opParseFn(p)) |rhs| {
- rightmost_op.cast(Node.AddressOf).?.rhs = rhs;
- rightmost_op = rhs;
- } else break;
- },
- .Await => {
- if (try opParseFn(p)) |rhs| {
- rightmost_op.cast(Node.Await).?.rhs = rhs;
- rightmost_op = rhs;
- } else break;
- },
- .BitNot => {
- if (try opParseFn(p)) |rhs| {
- rightmost_op.cast(Node.BitNot).?.rhs = rhs;
- rightmost_op = rhs;
- } else break;
- },
- .BoolNot => {
- if (try opParseFn(p)) |rhs| {
- rightmost_op.cast(Node.BoolNot).?.rhs = rhs;
- rightmost_op = rhs;
- } else break;
- },
- .OptionalType => {
- if (try opParseFn(p)) |rhs| {
- rightmost_op.cast(Node.OptionalType).?.rhs = rhs;
- rightmost_op = rhs;
- } else break;
- },
- .Negation => {
+ switch (rightmost_op.tag) {
+ .AddressOf,
+ .Await,
+ .BitNot,
+ .BoolNot,
+ .OptionalType,
+ .Negation,
+ .NegationWrap,
+ .Resume,
+ .Try,
+ => {
if (try opParseFn(p)) |rhs| {
- rightmost_op.cast(Node.Negation).?.rhs = rhs;
- rightmost_op = rhs;
- } else break;
- },
- .NegationWrap => {
- if (try opParseFn(p)) |rhs| {
- rightmost_op.cast(Node.NegationWrap).?.rhs = rhs;
- rightmost_op = rhs;
- } else break;
- },
- .Resume => {
- if (try opParseFn(p)) |rhs| {
- rightmost_op.cast(Node.Resume).?.rhs = rhs;
- rightmost_op = rhs;
- } else break;
- },
- .Try => {
- if (try opParseFn(p)) |rhs| {
- rightmost_op.cast(Node.Try).?.rhs = rhs;
+ rightmost_op.cast(Node.SimplePrefixOp).?.rhs = rhs;
rightmost_op = rhs;
} else break;
},
@@ -3310,57 +3375,18 @@ const Parser = struct {
}
// If any prefix op existed, a child node on the RHS is required
- switch (rightmost_op.id) {
- .AddressOf => {
- const prefix_op = rightmost_op.cast(Node.AddressOf).?;
- prefix_op.rhs = try p.expectNode(childParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
- },
- .Await => {
- const prefix_op = rightmost_op.cast(Node.Await).?;
- prefix_op.rhs = try p.expectNode(childParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
- },
- .BitNot => {
- const prefix_op = rightmost_op.cast(Node.BitNot).?;
- prefix_op.rhs = try p.expectNode(childParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
- },
- .BoolNot => {
- const prefix_op = rightmost_op.cast(Node.BoolNot).?;
- prefix_op.rhs = try p.expectNode(childParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
- },
- .OptionalType => {
- const prefix_op = rightmost_op.cast(Node.OptionalType).?;
- prefix_op.rhs = try p.expectNode(childParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
- },
- .Negation => {
- const prefix_op = rightmost_op.cast(Node.Negation).?;
- prefix_op.rhs = try p.expectNode(childParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
- },
- .NegationWrap => {
- const prefix_op = rightmost_op.cast(Node.NegationWrap).?;
- prefix_op.rhs = try p.expectNode(childParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
- },
- .Resume => {
- const prefix_op = rightmost_op.cast(Node.Resume).?;
- prefix_op.rhs = try p.expectNode(childParseFn, .{
- .InvalidToken = .{ .token = p.tok_i },
- });
- },
- .Try => {
- const prefix_op = rightmost_op.cast(Node.Try).?;
+ switch (rightmost_op.tag) {
+ .AddressOf,
+ .Await,
+ .BitNot,
+ .BoolNot,
+ .OptionalType,
+ .Negation,
+ .NegationWrap,
+ .Resume,
+ .Try,
+ => {
+ const prefix_op = rightmost_op.cast(Node.SimplePrefixOp).?;
prefix_op.rhs = try p.expectNode(childParseFn, .{
.InvalidToken = .{ .token = p.tok_i },
});
@@ -3425,9 +3451,13 @@ const Parser = struct {
const left = res;
res = node;
- const op = node.cast(Node.InfixOp).?;
- op.*.lhs = left;
- op.*.rhs = right;
+ if (node.castTag(.Catch)) |op| {
+ op.lhs = left;
+ op.rhs = right;
+ } else if (node.cast(Node.SimpleInfixOp)) |op| {
+ op.lhs = left;
+ op.rhs = right;
+ }
switch (chain) {
.Once => break,
@@ -3438,12 +3468,12 @@ const Parser = struct {
return res;
}
- fn createInfixOp(p: *Parser, index: TokenIndex, op: Node.InfixOp.Op) !*Node {
- const node = try p.arena.allocator.create(Node.InfixOp);
+ fn createInfixOp(p: *Parser, op_token: TokenIndex, tag: Node.Tag) !*Node {
+ const node = try p.arena.allocator.create(Node.SimpleInfixOp);
node.* = .{
- .op_token = index,
+ .base = Node{ .tag = tag },
+ .op_token = op_token,
.lhs = undefined, // set by caller
- .op = op,
.rhs = undefined, // set by caller
};
return &node.base;
diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig
index 4f9eb0e4cd..7f8a18299b 100644
--- a/lib/std/zig/render.zig
+++ b/lib/std/zig/render.zig
@@ -223,7 +223,7 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tre
}
fn renderContainerDecl(allocator: *mem.Allocator, stream: anytype, tree: *ast.Tree, indent: usize, start_col: *usize, decl: *ast.Node, space: Space) (@TypeOf(stream).Error || Error)!void {
- switch (decl.id) {
+ switch (decl.tag) {
.FnProto => {
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
@@ -365,7 +365,7 @@ fn renderExpression(
base: *ast.Node,
space: Space,
) (@TypeOf(stream).Error || Error)!void {
- switch (base.id) {
+ switch (base.tag) {
.Identifier => {
const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);
return renderToken(tree, stream, identifier.token, indent, start_col, space);
@@ -436,13 +436,10 @@ fn renderExpression(
}
},
- .InfixOp => {
- const infix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base);
+ .Catch => {
+ const infix_op_node = @fieldParentPtr(ast.Node.Catch, "base", base);
- const op_space = switch (infix_op_node.op) {
- ast.Node.InfixOp.Op.Period, ast.Node.InfixOp.Op.ErrorUnion, ast.Node.InfixOp.Op.Range => Space.None,
- else => Space.Space,
- };
+ const op_space = Space.Space;
try renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.lhs, op_space);
const after_op_space = blk: {
@@ -458,60 +455,99 @@ fn renderExpression(
start_col.* = indent + indent_delta;
}
- switch (infix_op_node.op) {
- ast.Node.InfixOp.Op.Catch => |maybe_payload| if (maybe_payload) |payload| {
- try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space);
- },
- else => {},
+ if (infix_op_node.payload) |payload| {
+ try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space);
}
return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space);
},
- .BitNot => {
- const bit_not = @fieldParentPtr(ast.Node.BitNot, "base", base);
- try renderToken(tree, stream, bit_not.op_token, indent, start_col, Space.None);
- return renderExpression(allocator, stream, tree, indent, start_col, bit_not.rhs, space);
- },
- .BoolNot => {
- const bool_not = @fieldParentPtr(ast.Node.BoolNot, "base", base);
- try renderToken(tree, stream, bool_not.op_token, indent, start_col, Space.None);
- return renderExpression(allocator, stream, tree, indent, start_col, bool_not.rhs, space);
- },
- .Negation => {
- const negation = @fieldParentPtr(ast.Node.Negation, "base", base);
- try renderToken(tree, stream, negation.op_token, indent, start_col, Space.None);
- return renderExpression(allocator, stream, tree, indent, start_col, negation.rhs, space);
- },
- .NegationWrap => {
- const negation_wrap = @fieldParentPtr(ast.Node.NegationWrap, "base", base);
- try renderToken(tree, stream, negation_wrap.op_token, indent, start_col, Space.None);
- return renderExpression(allocator, stream, tree, indent, start_col, negation_wrap.rhs, space);
- },
- .OptionalType => {
- const opt_type = @fieldParentPtr(ast.Node.OptionalType, "base", base);
- try renderToken(tree, stream, opt_type.op_token, indent, start_col, Space.None);
- return renderExpression(allocator, stream, tree, indent, start_col, opt_type.rhs, space);
- },
- .AddressOf => {
- const addr_of = @fieldParentPtr(ast.Node.AddressOf, "base", base);
- try renderToken(tree, stream, addr_of.op_token, indent, start_col, Space.None);
- return renderExpression(allocator, stream, tree, indent, start_col, addr_of.rhs, space);
- },
- .Try => {
- const try_node = @fieldParentPtr(ast.Node.Try, "base", base);
- try renderToken(tree, stream, try_node.op_token, indent, start_col, Space.Space);
- return renderExpression(allocator, stream, tree, indent, start_col, try_node.rhs, space);
+ .Add,
+ .AddWrap,
+ .ArrayCat,
+ .ArrayMult,
+ .Assign,
+ .AssignBitAnd,
+ .AssignBitOr,
+ .AssignBitShiftLeft,
+ .AssignBitShiftRight,
+ .AssignBitXor,
+ .AssignDiv,
+ .AssignSub,
+ .AssignSubWrap,
+ .AssignMod,
+ .AssignAdd,
+ .AssignAddWrap,
+ .AssignMul,
+ .AssignMulWrap,
+ .BangEqual,
+ .BitAnd,
+ .BitOr,
+ .BitShiftLeft,
+ .BitShiftRight,
+ .BitXor,
+ .BoolAnd,
+ .BoolOr,
+ .Div,
+ .EqualEqual,
+ .ErrorUnion,
+ .GreaterOrEqual,
+ .GreaterThan,
+ .LessOrEqual,
+ .LessThan,
+ .MergeErrorSets,
+ .Mod,
+ .Mul,
+ .MulWrap,
+ .Period,
+ .Range,
+ .Sub,
+ .SubWrap,
+ .UnwrapOptional,
+ => {
+ const infix_op_node = @fieldParentPtr(ast.Node.SimpleInfixOp, "base", base);
+
+ const op_space = switch (base.tag) {
+ .Period, .ErrorUnion, .Range => Space.None,
+ else => Space.Space,
+ };
+ try renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.lhs, op_space);
+
+ const after_op_space = blk: {
+ const loc = tree.tokenLocation(tree.token_locs[infix_op_node.op_token].end, tree.nextToken(infix_op_node.op_token));
+ break :blk if (loc.line == 0) op_space else Space.Newline;
+ };
+
+ try renderToken(tree, stream, infix_op_node.op_token, indent, start_col, after_op_space);
+ if (after_op_space == Space.Newline and
+ tree.token_ids[tree.nextToken(infix_op_node.op_token)] != .MultilineStringLiteralLine)
+ {
+ try stream.writeByteNTimes(' ', indent + indent_delta);
+ start_col.* = indent + indent_delta;
+ }
+
+ return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space);
},
- .Resume => {
- const resume_node = @fieldParentPtr(ast.Node.Resume, "base", base);
- try renderToken(tree, stream, resume_node.op_token, indent, start_col, Space.Space);
- return renderExpression(allocator, stream, tree, indent, start_col, resume_node.rhs, space);
+
+ .BitNot,
+ .BoolNot,
+ .Negation,
+ .NegationWrap,
+ .OptionalType,
+ .AddressOf,
+ => {
+ const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);
+ try renderToken(tree, stream, casted_node.op_token, indent, start_col, Space.None);
+ return renderExpression(allocator, stream, tree, indent, start_col, casted_node.rhs, space);
},
- .Await => {
- const await_node = @fieldParentPtr(ast.Node.Await, "base", base);
- try renderToken(tree, stream, await_node.op_token, indent, start_col, Space.Space);
- return renderExpression(allocator, stream, tree, indent, start_col, await_node.rhs, space);
+
+ .Try,
+ .Resume,
+ .Await,
+ => {
+ const casted_node = @fieldParentPtr(ast.Node.SimplePrefixOp, "base", base);
+ try renderToken(tree, stream, casted_node.op_token, indent, start_col, Space.Space);
+ return renderExpression(allocator, stream, tree, indent, start_col, casted_node.rhs, space);
},
.ArrayType => {
@@ -659,7 +695,7 @@ fn renderExpression(
.ArrayInitializer, .ArrayInitializerDot => {
var rtoken: ast.TokenIndex = undefined;
var exprs: []*ast.Node = undefined;
- const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.id) {
+ const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.tag) {
.ArrayInitializerDot => blk: {
const casted = @fieldParentPtr(ast.Node.ArrayInitializerDot, "base", base);
rtoken = casted.rtoken;
@@ -793,14 +829,14 @@ fn renderExpression(
}
try renderExtraNewline(tree, stream, start_col, next_expr);
- if (next_expr.id != .MultilineStringLiteral) {
+ if (next_expr.tag != .MultilineStringLiteral) {
try stream.writeByteNTimes(' ', new_indent);
}
} else {
try renderExpression(allocator, stream, tree, new_indent, start_col, expr, Space.Comma); // ,
}
}
- if (exprs[exprs.len - 1].id != .MultilineStringLiteral) {
+ if (exprs[exprs.len - 1].tag != .MultilineStringLiteral) {
try stream.writeByteNTimes(' ', indent);
}
return renderToken(tree, stream, rtoken, indent, start_col, space);
@@ -823,7 +859,7 @@ fn renderExpression(
.StructInitializer, .StructInitializerDot => {
var rtoken: ast.TokenIndex = undefined;
var field_inits: []*ast.Node = undefined;
- const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.id) {
+ const lhs: union(enum) { dot: ast.TokenIndex, node: *ast.Node } = switch (base.tag) {
.StructInitializerDot => blk: {
const casted = @fieldParentPtr(ast.Node.StructInitializerDot, "base", base);
rtoken = casted.rtoken;
@@ -877,7 +913,7 @@ fn renderExpression(
if (field_inits.len == 1) blk: {
const field_init = field_inits[0].cast(ast.Node.FieldInitializer).?;
- switch (field_init.expr.id) {
+ switch (field_init.expr.tag) {
.StructInitializer,
.StructInitializerDot,
=> break :blk,
@@ -974,7 +1010,7 @@ fn renderExpression(
const params = call.params();
for (params) |param_node, i| {
- const param_node_new_indent = if (param_node.id == .MultilineStringLiteral) blk: {
+ const param_node_new_indent = if (param_node.tag == .MultilineStringLiteral) blk: {
break :blk indent;
} else blk: {
try stream.writeByteNTimes(' ', new_indent);
@@ -1284,7 +1320,7 @@ fn renderExpression(
// declarations inside are fields
const src_has_only_fields = blk: {
for (fields_and_decls) |decl| {
- if (decl.id != .ContainerField) break :blk false;
+ if (decl.tag != .ContainerField) break :blk false;
}
break :blk true;
};
@@ -1831,7 +1867,7 @@ fn renderExpression(
const rparen = tree.nextToken(for_node.array_expr.lastToken());
- const body_is_block = for_node.body.id == .Block;
+ const body_is_block = for_node.body.tag == .Block;
const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken());
const body_on_same_line = body_is_block or src_one_line_to_body;
@@ -1874,7 +1910,7 @@ fn renderExpression(
try renderExpression(allocator, stream, tree, indent, start_col, if_node.condition, Space.None); // condition
- const body_is_if_block = if_node.body.id == .If;
+ const body_is_if_block = if_node.body.tag == .If;
const body_is_block = nodeIsBlock(if_node.body);
if (body_is_if_block) {
@@ -1978,7 +2014,7 @@ fn renderExpression(
const indent_once = indent + indent_delta;
- if (asm_node.template.id == .MultilineStringLiteral) {
+ if (asm_node.template.tag == .MultilineStringLiteral) {
// After rendering a multiline string literal the cursor is
// already offset by indent
try stream.writeByteNTimes(' ', indent_delta);
@@ -2245,7 +2281,7 @@ fn renderVarDecl(
}
if (var_decl.getTrailer("init_node")) |init_node| {
- const s = if (init_node.id == .MultilineStringLiteral) Space.None else Space.Space;
+ const s = if (init_node.tag == .MultilineStringLiteral) Space.None else Space.Space;
try renderToken(tree, stream, var_decl.getTrailer("eq_token").?, indent, start_col, s); // =
try renderExpression(allocator, stream, tree, indent, start_col, init_node, Space.None);
}
@@ -2287,7 +2323,7 @@ fn renderStatement(
start_col: *usize,
base: *ast.Node,
) (@TypeOf(stream).Error || Error)!void {
- switch (base.id) {
+ switch (base.tag) {
.VarDecl => {
const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
try renderVarDecl(allocator, stream, tree, indent, start_col, var_decl);
@@ -2566,7 +2602,7 @@ fn renderDocCommentsToken(
}
fn nodeIsBlock(base: *const ast.Node) bool {
- return switch (base.id) {
+ return switch (base.tag) {
.Block,
.If,
.For,
@@ -2578,10 +2614,52 @@ fn nodeIsBlock(base: *const ast.Node) bool {
}
fn nodeCausesSliceOpSpace(base: *ast.Node) bool {
- const infix_op = base.cast(ast.Node.InfixOp) orelse return false;
- return switch (infix_op.op) {
- ast.Node.InfixOp.Op.Period => false,
- else => true,
+ return switch (base.tag) {
+ .Catch,
+ .Add,
+ .AddWrap,
+ .ArrayCat,
+ .ArrayMult,
+ .Assign,
+ .AssignBitAnd,
+ .AssignBitOr,
+ .AssignBitShiftLeft,
+ .AssignBitShiftRight,
+ .AssignBitXor,
+ .AssignDiv,
+ .AssignSub,
+ .AssignSubWrap,
+ .AssignMod,
+ .AssignAdd,
+ .AssignAddWrap,
+ .AssignMul,
+ .AssignMulWrap,
+ .BangEqual,
+ .BitAnd,
+ .BitOr,
+ .BitShiftLeft,
+ .BitShiftRight,
+ .BitXor,
+ .BoolAnd,
+ .BoolOr,
+ .Div,
+ .EqualEqual,
+ .ErrorUnion,
+ .GreaterOrEqual,
+ .GreaterThan,
+ .LessOrEqual,
+ .LessThan,
+ .MergeErrorSets,
+ .Mod,
+ .Mul,
+ .MulWrap,
+ .Range,
+ .Sub,
+ .SubWrap,
+ .UnwrapOptional,
+ => true,
+
+ else => false,
};
}
diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig
index 0a8a5152cf..72e5f6cd63 100644
--- a/src-self-hosted/Module.zig
+++ b/src-self-hosted/Module.zig
@@ -19,6 +19,7 @@ const Body = ir.Body;
const ast = std.zig.ast;
const trace = @import("tracy.zig").trace;
const liveness = @import("liveness.zig");
+const astgen = @import("astgen.zig");
/// General-purpose allocator. Used for both temporary and long-term storage.
gpa: *Allocator,
@@ -76,6 +77,8 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
keep_source_files_loaded: bool,
+pub const InnerError = error{ OutOfMemory, AnalysisFail };
+
const WorkItem = union(enum) {
/// Write the machine code for a Decl to the output file.
codegen_decl: *Decl,
@@ -209,6 +212,7 @@ pub const Decl = struct {
},
.block => unreachable,
.gen_zir => unreachable,
+ .local_var => unreachable,
.decl => unreachable,
}
}
@@ -304,6 +308,7 @@ pub const Scope = struct {
.block => return self.cast(Block).?.arena,
.decl => return &self.cast(DeclAnalysis).?.arena.allocator,
.gen_zir => return self.cast(GenZIR).?.arena,
+ .local_var => return self.cast(LocalVar).?.gen_zir.arena,
.zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
.file => unreachable,
}
@@ -315,6 +320,7 @@ pub const Scope = struct {
return switch (self.tag) {
.block => self.cast(Block).?.decl,
.gen_zir => self.cast(GenZIR).?.decl,
+ .local_var => return self.cast(LocalVar).?.gen_zir.decl,
.decl => self.cast(DeclAnalysis).?.decl,
.zir_module => null,
.file => null,
@@ -327,6 +333,7 @@ pub const Scope = struct {
switch (self.tag) {
.block => return self.cast(Block).?.decl.scope,
.gen_zir => return self.cast(GenZIR).?.decl.scope,
+ .local_var => return self.cast(LocalVar).?.gen_zir.decl.scope,
.decl => return self.cast(DeclAnalysis).?.decl.scope,
.zir_module, .file => return self,
}
@@ -339,6 +346,7 @@ pub const Scope = struct {
switch (self.tag) {
.block => unreachable,
.gen_zir => unreachable,
+ .local_var => unreachable,
.decl => unreachable,
.zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
.file => return self.cast(File).?.fullyQualifiedNameHash(name),
@@ -353,9 +361,22 @@ pub const Scope = struct {
.decl => return self.cast(DeclAnalysis).?.decl.scope.cast(File).?.contents.tree,
.block => return self.cast(Block).?.decl.scope.cast(File).?.contents.tree,
.gen_zir => return self.cast(GenZIR).?.decl.scope.cast(File).?.contents.tree,
+ .local_var => return self.cast(LocalVar).?.gen_zir.decl.scope.cast(File).?.contents.tree,
}
}
+ /// Asserts the scope is a child of a `GenZIR` and returns it.
+ pub fn getGenZIR(self: *Scope) *GenZIR {
+ return switch (self.tag) {
+ .block => unreachable,
+ .gen_zir => self.cast(GenZIR).?,
+ .local_var => return self.cast(LocalVar).?.gen_zir,
+ .decl => unreachable,
+ .zir_module => unreachable,
+ .file => unreachable,
+ };
+ }
+
pub fn dumpInst(self: *Scope, inst: *Inst) void {
const zir_module = self.namespace();
const loc = std.zig.findLineColumn(zir_module.source.bytes, inst.src);
@@ -376,6 +397,7 @@ pub const Scope = struct {
.zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
.block => unreachable,
.gen_zir => unreachable,
+ .local_var => unreachable,
.decl => unreachable,
}
}
@@ -386,6 +408,7 @@ pub const Scope = struct {
.zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa),
.block => unreachable,
.gen_zir => unreachable,
+ .local_var => unreachable,
.decl => unreachable,
}
}
@@ -395,6 +418,7 @@ pub const Scope = struct {
.file => return @fieldParentPtr(File, "base", base).getSource(module),
.zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
.gen_zir => unreachable,
+ .local_var => unreachable,
.block => unreachable,
.decl => unreachable,
}
@@ -407,6 +431,7 @@ pub const Scope = struct {
.zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
.block => unreachable,
.gen_zir => unreachable,
+ .local_var => unreachable,
.decl => unreachable,
}
}
@@ -426,6 +451,7 @@ pub const Scope = struct {
},
.block => unreachable,
.gen_zir => unreachable,
+ .local_var => unreachable,
.decl => unreachable,
}
}
@@ -446,6 +472,7 @@ pub const Scope = struct {
block,
decl,
gen_zir,
+ local_var,
};
pub const File = struct {
@@ -673,10 +700,25 @@ pub const Scope = struct {
pub const GenZIR = struct {
pub const base_tag: Tag = .gen_zir;
base: Scope = Scope{ .tag = base_tag },
+ /// Parents can be: `GenZIR`, `ZIRModule`, `File`
+ parent: *Scope,
decl: *Decl,
arena: *Allocator,
+ /// The first N instructions in a function body ZIR are arg instructions.
instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
};
+
+ /// This structure lives as long as the AST generation of the Block
+ /// node that contains the variable.
+ pub const LocalVar = struct {
+ pub const base_tag: Tag = .local_var;
+ base: Scope = Scope{ .tag = base_tag },
+ /// Parents can be: `LocalVar`, `GenZIR`.
+ parent: *Scope,
+ gen_zir: *GenZIR,
+ name: []const u8,
+ inst: *zir.Inst,
+ };
};
pub const AllErrors = struct {
@@ -944,8 +986,6 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors {
};
}
-const InnerError = error{ OutOfMemory, AnalysisFail };
-
pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void {
while (self.work_queue.readItem()) |work_item| switch (work_item) {
.codegen_decl => |decl| switch (decl.analysis) {
@@ -1113,7 +1153,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
const file_scope = decl.scope.cast(Scope.File).?;
const tree = try self.getAstTree(file_scope);
const ast_node = tree.root_node.decls()[decl.src_index];
- switch (ast_node.id) {
+ switch (ast_node.tag) {
.FnProto => {
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node);
@@ -1127,6 +1167,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
var fn_type_scope: Scope.GenZIR = .{
.decl = decl,
.arena = &fn_type_scope_arena.allocator,
+ .parent = decl.scope,
};
defer fn_type_scope.instructions.deinit(self.gpa);
@@ -1140,7 +1181,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
.any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}),
.type_expr => |node| node,
};
- param_types[i] = try self.astGenExpr(&fn_type_scope.base, param_type_node);
+ param_types[i] = try astgen.expr(self, &fn_type_scope.base, param_type_node);
}
if (fn_proto.getTrailer("var_args_token")) |var_args_token| {
return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{});
@@ -1168,7 +1209,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
.Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}),
};
- const return_type_inst = try self.astGenExpr(&fn_type_scope.base, return_type_expr);
+ const return_type_inst = try astgen.expr(self, &fn_type_scope.base, return_type_expr);
const fn_src = tree.token_locs[fn_proto.fn_token].start;
const fn_type_inst = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.FnType, .{
.return_type = return_type_inst,
@@ -1204,12 +1245,32 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
var gen_scope: Scope.GenZIR = .{
.decl = decl,
.arena = &gen_scope_arena.allocator,
+ .parent = decl.scope,
};
defer gen_scope.instructions.deinit(self.gpa);
+ // We need an instruction for each parameter, and they must be first in the body.
+ try gen_scope.instructions.resize(self.gpa, fn_proto.params_len);
+ var params_scope = &gen_scope.base;
+ for (fn_proto.params()) |param, i| {
+ const name_token = param.name_token.?;
+ const src = tree.token_locs[name_token].start;
+ const param_name = tree.tokenSlice(name_token);
+ const arg = try newZIRInst(&gen_scope_arena.allocator, src, zir.Inst.Arg, .{}, .{});
+ gen_scope.instructions.items[i] = &arg.base;
+ const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVar);
+ sub_scope.* = .{
+ .parent = params_scope,
+ .gen_zir = &gen_scope,
+ .name = param_name,
+ .inst = &arg.base,
+ };
+ params_scope = &sub_scope.base;
+ }
+
const body_block = body_node.cast(ast.Node.Block).?;
- try self.astGenBlock(&gen_scope.base, body_block);
+ try astgen.blockExpr(self, params_scope, body_block);
if (!fn_type.fnReturnType().isNoReturn() and (gen_scope.instructions.items.len == 0 or
!gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()))
@@ -1298,465 +1359,6 @@ fn analyzeBodyValueAsType(self: *Module, block_scope: *Scope.Block, body: zir.Mo
unreachable;
}
-fn astGenExpr(self: *Module, scope: *Scope, ast_node: *ast.Node) InnerError!*zir.Inst {
- switch (ast_node.id) {
- .Identifier => return self.astGenIdent(scope, @fieldParentPtr(ast.Node.Identifier, "base", ast_node)),
- .Asm => return self.astGenAsm(scope, @fieldParentPtr(ast.Node.Asm, "base", ast_node)),
- .StringLiteral => return self.astGenStringLiteral(scope, @fieldParentPtr(ast.Node.StringLiteral, "base", ast_node)),
- .IntegerLiteral => return self.astGenIntegerLiteral(scope, @fieldParentPtr(ast.Node.IntegerLiteral, "base", ast_node)),
- .BuiltinCall => return self.astGenBuiltinCall(scope, @fieldParentPtr(ast.Node.BuiltinCall, "base", ast_node)),
- .Call => return self.astGenCall(scope, @fieldParentPtr(ast.Node.Call, "base", ast_node)),
- .Unreachable => return self.astGenUnreachable(scope, @fieldParentPtr(ast.Node.Unreachable, "base", ast_node)),
- .ControlFlowExpression => return self.astGenControlFlowExpression(scope, @fieldParentPtr(ast.Node.ControlFlowExpression, "base", ast_node)),
- .If => return self.astGenIf(scope, @fieldParentPtr(ast.Node.If, "base", ast_node)),
- .InfixOp => return self.astGenInfixOp(scope, @fieldParentPtr(ast.Node.InfixOp, "base", ast_node)),
- .BoolNot => return self.astGenBoolNot(scope, @fieldParentPtr(ast.Node.BoolNot, "base", ast_node)),
- else => return self.failNode(scope, ast_node, "TODO implement astGenExpr for {}", .{@tagName(ast_node.id)}),
- }
-}
-
-fn astGenBoolNot(self: *Module, scope: *Scope, node: *ast.Node.BoolNot) InnerError!*zir.Inst {
- const operand = try self.astGenExpr(scope, node.rhs);
- const tree = scope.tree();
- const src = tree.token_locs[node.op_token].start;
- return self.addZIRInst(scope, src, zir.Inst.BoolNot, .{ .operand = operand }, .{});
-}
-
-fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) InnerError!*zir.Inst {
- switch (infix_node.op) {
- .Assign => {
- if (infix_node.lhs.id == .Identifier) {
- const ident = @fieldParentPtr(ast.Node.Identifier, "base", infix_node.lhs);
- const tree = scope.tree();
- const ident_name = tree.tokenSlice(ident.token);
- if (std.mem.eql(u8, ident_name, "_")) {
- return self.astGenExpr(scope, infix_node.rhs);
- } else {
- return self.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
- }
- } else {
- return self.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
- }
- },
- .Add => {
- const lhs = try self.astGenExpr(scope, infix_node.lhs);
- const rhs = try self.astGenExpr(scope, infix_node.rhs);
-
- const tree = scope.tree();
- const src = tree.token_locs[infix_node.op_token].start;
-
- return self.addZIRInst(scope, src, zir.Inst.Add, .{ .lhs = lhs, .rhs = rhs }, .{});
- },
- .BangEqual,
- .EqualEqual,
- .GreaterThan,
- .GreaterOrEqual,
- .LessThan,
- .LessOrEqual,
- => {
- const lhs = try self.astGenExpr(scope, infix_node.lhs);
- const rhs = try self.astGenExpr(scope, infix_node.rhs);
-
- const tree = scope.tree();
- const src = tree.token_locs[infix_node.op_token].start;
-
- const op: std.math.CompareOperator = switch (infix_node.op) {
- .BangEqual => .neq,
- .EqualEqual => .eq,
- .GreaterThan => .gt,
- .GreaterOrEqual => .gte,
- .LessThan => .lt,
- .LessOrEqual => .lte,
- else => unreachable,
- };
-
- return self.addZIRInst(scope, src, zir.Inst.Cmp, .{
- .lhs = lhs,
- .op = op,
- .rhs = rhs,
- }, .{});
- },
- else => |op| {
- return self.failNode(scope, &infix_node.base, "TODO implement infix operator {}", .{op});
- },
- }
-}
-
-fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.Inst {
- if (if_node.payload) |payload| {
- return self.failNode(scope, payload, "TODO implement astGenIf for optionals", .{});
- }
- if (if_node.@"else") |else_node| {
- if (else_node.payload) |payload| {
- return self.failNode(scope, payload, "TODO implement astGenIf for error unions", .{});
- }
- }
- var block_scope: Scope.GenZIR = .{
- .decl = scope.decl().?,
- .arena = scope.arena(),
- .instructions = .{},
- };
- defer block_scope.instructions.deinit(self.gpa);
-
- const cond = try self.astGenExpr(&block_scope.base, if_node.condition);
-
- const tree = scope.tree();
- const if_src = tree.token_locs[if_node.if_token].start;
- const condbr = try self.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{
- .condition = cond,
- .true_body = undefined, // populated below
- .false_body = undefined, // populated below
- }, .{});
-
- const block = try self.addZIRInstBlock(scope, if_src, .{
- .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
- });
- var then_scope: Scope.GenZIR = .{
- .decl = block_scope.decl,
- .arena = block_scope.arena,
- .instructions = .{},
- };
- defer then_scope.instructions.deinit(self.gpa);
-
- const then_result = try self.astGenExpr(&then_scope.base, if_node.body);
- if (!then_result.tag.isNoReturn()) {
- const then_src = tree.token_locs[if_node.body.lastToken()].start;
- _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{
- .block = block,
- .operand = then_result,
- }, .{});
- }
- condbr.positionals.true_body = .{
- .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
- };
-
- var else_scope: Scope.GenZIR = .{
- .decl = block_scope.decl,
- .arena = block_scope.arena,
- .instructions = .{},
- };
- defer else_scope.instructions.deinit(self.gpa);
-
- if (if_node.@"else") |else_node| {
- const else_result = try self.astGenExpr(&else_scope.base, else_node.body);
- if (!else_result.tag.isNoReturn()) {
- const else_src = tree.token_locs[else_node.body.lastToken()].start;
- _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{
- .block = block,
- .operand = else_result,
- }, .{});
- }
- } else {
- // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
- // by directly allocating the body for this one instruction.
- const else_src = tree.token_locs[if_node.lastToken()].start;
- _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.BreakVoid, .{
- .block = block,
- }, .{});
- }
- condbr.positionals.false_body = .{
- .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
- };
-
- return &block.base;
-}
-
-fn astGenControlFlowExpression(
- self: *Module,
- scope: *Scope,
- cfe: *ast.Node.ControlFlowExpression,
-) InnerError!*zir.Inst {
- switch (cfe.kind) {
- .Break => return self.failNode(scope, &cfe.base, "TODO implement astGenExpr for Break", .{}),
- .Continue => return self.failNode(scope, &cfe.base, "TODO implement astGenExpr for Continue", .{}),
- .Return => {},
- }
- const tree = scope.tree();
- const src = tree.token_locs[cfe.ltoken].start;
- if (cfe.rhs) |rhs_node| {
- const operand = try self.astGenExpr(scope, rhs_node);
- return self.addZIRInst(scope, src, zir.Inst.Return, .{ .operand = operand }, .{});
- } else {
- return self.addZIRInst(scope, src, zir.Inst.ReturnVoid, .{}, .{});
- }
-}
-
-fn astGenIdent(self: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerError!*zir.Inst {
- const tree = scope.tree();
- const ident_name = tree.tokenSlice(ident.token);
- const src = tree.token_locs[ident.token].start;
- if (mem.eql(u8, ident_name, "_")) {
- return self.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});
- }
-
- if (getSimplePrimitiveValue(ident_name)) |typed_value| {
- return self.addZIRInstConst(scope, src, typed_value);
- }
-
- if (ident_name.len >= 2) integer: {
- const first_c = ident_name[0];
- if (first_c == 'i' or first_c == 'u') {
- const is_signed = first_c == 'i';
- const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
- error.Overflow => return self.failNode(
- scope,
- &ident.base,
- "primitive integer type '{}' exceeds maximum bit width of 65535",
- .{ident_name},
- ),
- error.InvalidCharacter => break :integer,
- };
- const val = switch (bit_count) {
- 8 => if (is_signed) Value.initTag(.i8_type) else Value.initTag(.u8_type),
- 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type),
- 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type),
- 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type),
- else => return self.failNode(scope, &ident.base, "TODO implement arbitrary integer bitwidth types", .{}),
- };
- return self.addZIRInstConst(scope, src, .{
- .ty = Type.initTag(.type),
- .val = val,
- });
- }
- }
-
- if (self.lookupDeclName(scope, ident_name)) |decl| {
- return try self.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
- }
-
- // Function parameter
- if (scope.decl()) |decl| {
- if (tree.root_node.decls()[decl.src_index].cast(ast.Node.FnProto)) |fn_proto| {
- for (fn_proto.params()) |param, i| {
- const param_name = tree.tokenSlice(param.name_token.?);
- if (mem.eql(u8, param_name, ident_name)) {
- return try self.addZIRInst(scope, src, zir.Inst.Arg, .{ .index = i }, .{});
- }
- }
- }
- }
-
- return self.failNode(scope, &ident.base, "TODO implement local variable identifier lookup", .{});
-}
-
-fn astGenStringLiteral(self: *Module, scope: *Scope, str_lit: *ast.Node.StringLiteral) InnerError!*zir.Inst {
- const tree = scope.tree();
- const unparsed_bytes = tree.tokenSlice(str_lit.token);
- const arena = scope.arena();
-
- var bad_index: usize = undefined;
- const bytes = std.zig.parseStringLiteral(arena, unparsed_bytes, &bad_index) catch |err| switch (err) {
- error.InvalidCharacter => {
- const bad_byte = unparsed_bytes[bad_index];
- const src = tree.token_locs[str_lit.token].start;
- return self.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
- },
- else => |e| return e,
- };
-
- const src = tree.token_locs[str_lit.token].start;
- return self.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
-}
-
-fn astGenIntegerLiteral(self: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral) InnerError!*zir.Inst {
- const arena = scope.arena();
- const tree = scope.tree();
- const prefixed_bytes = tree.tokenSlice(int_lit.token);
- const base = if (mem.startsWith(u8, prefixed_bytes, "0x"))
- 16
- else if (mem.startsWith(u8, prefixed_bytes, "0o"))
- 8
- else if (mem.startsWith(u8, prefixed_bytes, "0b"))
- 2
- else
- @as(u8, 10);
-
- const bytes = if (base == 10)
- prefixed_bytes
- else
- prefixed_bytes[2..];
-
- if (std.fmt.parseInt(u64, bytes, base)) |small_int| {
- const int_payload = try arena.create(Value.Payload.Int_u64);
- int_payload.* = .{ .int = small_int };
- const src = tree.token_locs[int_lit.token].start;
- return self.addZIRInstConst(scope, src, .{
- .ty = Type.initTag(.comptime_int),
- .val = Value.initPayload(&int_payload.base),
- });
- } else |err| {
- return self.failTok(scope, int_lit.token, "TODO implement int literals that don't fit in a u64", .{});
- }
-}
-
-fn astGenBlock(self: *Module, scope: *Scope, block_node: *ast.Node.Block) !void {
- const tracy = trace(@src());
- defer tracy.end();
-
- if (block_node.label) |label| {
- return self.failTok(scope, label, "TODO implement labeled blocks", .{});
- }
- for (block_node.statements()) |statement| {
- _ = try self.astGenExpr(scope, statement);
- }
-}
-
-fn astGenAsm(self: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
- if (asm_node.outputs.len != 0) {
- return self.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{});
- }
- const arena = scope.arena();
- const tree = scope.tree();
-
- const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len);
- const args = try arena.alloc(*zir.Inst, asm_node.inputs.len);
-
- for (asm_node.inputs) |input, i| {
- // TODO semantically analyze constraints
- inputs[i] = try self.astGenExpr(scope, input.constraint);
- args[i] = try self.astGenExpr(scope, input.expr);
- }
-
- const src = tree.token_locs[asm_node.asm_token].start;
- const return_type = try self.addZIRInstConst(scope, src, .{
- .ty = Type.initTag(.type),
- .val = Value.initTag(.void_type),
- });
- const asm_inst = try self.addZIRInst(scope, src, zir.Inst.Asm, .{
- .asm_source = try self.astGenExpr(scope, asm_node.template),
- .return_type = return_type,
- }, .{
- .@"volatile" = asm_node.volatile_token != null,
- //.clobbers = TODO handle clobbers
- .inputs = inputs,
- .args = args,
- });
- return asm_inst;
-}
-
-fn astGenBuiltinCall(self: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
- const tree = scope.tree();
- const builtin_name = tree.tokenSlice(call.builtin_token);
- const src = tree.token_locs[call.builtin_token].start;
-
- inline for (std.meta.declarations(zir.Inst)) |inst| {
- if (inst.data != .Type) continue;
- const T = inst.data.Type;
- if (!@hasDecl(T, "builtin_name")) continue;
- if (std.mem.eql(u8, builtin_name, T.builtin_name)) {
- var value: T = undefined;
- const positionals = @typeInfo(std.meta.fieldInfo(T, "positionals").field_type).Struct;
- if (positionals.fields.len == 0) {
- return self.addZIRInst(scope, src, T, value.positionals, value.kw_args);
- }
- const arg_count: ?usize = if (positionals.fields[0].field_type == []*zir.Inst) null else positionals.fields.len;
- if (arg_count) |some| {
- if (call.params_len != some) {
- return self.failTok(
- scope,
- call.builtin_token,
- "expected {} parameter{}, found {}",
- .{ some, if (some == 1) "" else "s", call.params_len },
- );
- }
- const params = call.params();
- inline for (positionals.fields) |p, i| {
- @field(value.positionals, p.name) = try self.astGenExpr(scope, params[i]);
- }
- } else {
- return self.failTok(scope, call.builtin_token, "TODO var args builtin '{}'", .{builtin_name});
- }
-
- return self.addZIRInst(scope, src, T, value.positionals, .{});
- }
- }
- return self.failTok(scope, call.builtin_token, "TODO implement builtin call for '{}'", .{builtin_name});
-}
-
-fn astGenCall(self: *Module, scope: *Scope, call: *ast.Node.Call) InnerError!*zir.Inst {
- const tree = scope.tree();
- const lhs = try self.astGenExpr(scope, call.lhs);
-
- const param_nodes = call.params();
- const args = try scope.cast(Scope.GenZIR).?.arena.alloc(*zir.Inst, param_nodes.len);
- for (param_nodes) |param_node, i| {
- args[i] = try self.astGenExpr(scope, param_node);
- }
-
- const src = tree.token_locs[call.lhs.firstToken()].start;
- return self.addZIRInst(scope, src, zir.Inst.Call, .{
- .func = lhs,
- .args = args,
- }, .{});
-}
-
-fn astGenUnreachable(self: *Module, scope: *Scope, unreach_node: *ast.Node.Unreachable) InnerError!*zir.Inst {
- const tree = scope.tree();
- const src = tree.token_locs[unreach_node.token].start;
- return self.addZIRInst(scope, src, zir.Inst.Unreachable, .{}, .{});
-}
-
-fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
- const simple_types = std.ComptimeStringMap(Value.Tag, .{
- .{ "u8", .u8_type },
- .{ "i8", .i8_type },
- .{ "isize", .isize_type },
- .{ "usize", .usize_type },
- .{ "c_short", .c_short_type },
- .{ "c_ushort", .c_ushort_type },
- .{ "c_int", .c_int_type },
- .{ "c_uint", .c_uint_type },
- .{ "c_long", .c_long_type },
- .{ "c_ulong", .c_ulong_type },
- .{ "c_longlong", .c_longlong_type },
- .{ "c_ulonglong", .c_ulonglong_type },
- .{ "c_longdouble", .c_longdouble_type },
- .{ "f16", .f16_type },
- .{ "f32", .f32_type },
- .{ "f64", .f64_type },
- .{ "f128", .f128_type },
- .{ "c_void", .c_void_type },
- .{ "bool", .bool_type },
- .{ "void", .void_type },
- .{ "type", .type_type },
- .{ "anyerror", .anyerror_type },
- .{ "comptime_int", .comptime_int_type },
- .{ "comptime_float", .comptime_float_type },
- .{ "noreturn", .noreturn_type },
- });
- if (simple_types.get(name)) |tag| {
- return TypedValue{
- .ty = Type.initTag(.type),
- .val = Value.initTag(tag),
- };
- }
- if (mem.eql(u8, name, "null")) {
- return TypedValue{
- .ty = Type.initTag(.@"null"),
- .val = Value.initTag(.null_value),
- };
- }
- if (mem.eql(u8, name, "undefined")) {
- return TypedValue{
- .ty = Type.initTag(.@"undefined"),
- .val = Value.initTag(.undef),
- };
- }
- if (mem.eql(u8, name, "true")) {
- return TypedValue{
- .ty = Type.initTag(.bool),
- .val = Value.initTag(.bool_true),
- };
- }
- if (mem.eql(u8, name, "false")) {
- return TypedValue{
- .ty = Type.initTag(.bool),
- .val = Value.initTag(.bool_false),
- };
- }
- return null;
-}
-
fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void {
try depender.dependencies.ensureCapacity(self.gpa, depender.dependencies.items().len + 1);
try dependee.dependants.ensureCapacity(self.gpa, dependee.dependants.items().len + 1);
@@ -2368,7 +1970,7 @@ fn newZIRInst(
return inst;
}
-fn addZIRInstSpecial(
+pub fn addZIRInstSpecial(
self: *Module,
scope: *Scope,
src: usize,
@@ -2376,14 +1978,14 @@ fn addZIRInstSpecial(
positionals: std.meta.fieldInfo(T, "positionals").field_type,
kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
) !*T {
- const gen_zir = scope.cast(Scope.GenZIR).?;
+ const gen_zir = scope.getGenZIR();
try gen_zir.instructions.ensureCapacity(self.gpa, gen_zir.instructions.items.len + 1);
const inst = try newZIRInst(gen_zir.arena, src, T, positionals, kw_args);
gen_zir.instructions.appendAssumeCapacity(&inst.base);
return inst;
}
-fn addZIRInst(
+pub fn addZIRInst(
self: *Module,
scope: *Scope,
src: usize,
@@ -2396,13 +1998,13 @@ fn addZIRInst(
}
/// TODO The existence of this function is a workaround for a bug in stage1.
-fn addZIRInstConst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
+pub fn addZIRInstConst(self: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type;
return self.addZIRInst(scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
}
/// TODO The existence of this function is a workaround for a bug in stage1.
-fn addZIRInstBlock(self: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Block {
+pub fn addZIRInstBlock(self: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Block {
const P = std.meta.fieldInfo(zir.Inst.Block, "positionals").field_type;
return self.addZIRInstSpecial(scope, src, zir.Inst.Block, P{ .body = body }, .{});
}
@@ -2637,7 +2239,7 @@ fn getNextAnonNameIndex(self: *Module) usize {
return @atomicRmw(usize, &self.next_anon_name_index, .Add, 1, .Monotonic);
}
-fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
+pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl {
const namespace = scope.namespace();
const name_hash = namespace.fullyQualifiedNameHash(ident_name);
return self.decl_table.get(name_hash);
@@ -2658,17 +2260,16 @@ fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.Compile
fn analyzeInstArg(self: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst {
const b = try self.requireRuntimeBlock(scope, inst.base.src);
const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
+ const param_index = b.instructions.items.len;
const param_count = fn_ty.fnParamLen();
- if (inst.positionals.index >= param_count) {
+ if (param_index >= param_count) {
return self.fail(scope, inst.base.src, "parameter index {} outside list of length {}", .{
- inst.positionals.index,
+ param_index,
param_count,
});
}
- const param_type = fn_ty.fnParamType(inst.positionals.index);
- return self.addNewInstArgs(b, inst.base.src, param_type, Inst.Arg, .{
- .index = inst.positionals.index,
- });
+ const param_type = fn_ty.fnParamType(param_index);
+ return self.addNewInstArgs(b, inst.base.src, param_type, Inst.Arg, {});
}
fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst {
@@ -3646,13 +3247,13 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I
return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
}
-fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
+pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
@setCold(true);
const err_msg = try ErrorMsg.create(self.gpa, src, format, args);
return self.failWithOwnedErrorMsg(scope, src, err_msg);
}
-fn failTok(
+pub fn failTok(
self: *Module,
scope: *Scope,
token_index: ast.TokenIndex,
@@ -3664,7 +3265,7 @@ fn failTok(
return self.fail(scope, src, format, args);
}
-fn failNode(
+pub fn failNode(
self: *Module,
scope: *Scope,
ast_node: *ast.Node,
@@ -3705,6 +3306,12 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Err
gen_zir.decl.generation = self.generation;
self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
},
+ .local_var => {
+ const gen_zir = scope.cast(Scope.LocalVar).?.gen_zir;
+ gen_zir.decl.analysis = .sema_failure;
+ gen_zir.decl.generation = self.generation;
+ self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
+ },
.zir_module => {
const zir_module = scope.cast(Scope.ZIRModule).?;
zir_module.status = .loaded_sema_failure;
diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig
new file mode 100644
index 0000000000..be70a724c2
--- /dev/null
+++ b/src-self-hosted/astgen.zig
@@ -0,0 +1,643 @@
+const std = @import("std");
+const mem = std.mem;
+const Value = @import("value.zig").Value;
+const Type = @import("type.zig").Type;
+const TypedValue = @import("TypedValue.zig");
+const assert = std.debug.assert;
+const zir = @import("zir.zig");
+const Module = @import("Module.zig");
+const ast = std.zig.ast;
+const trace = @import("tracy.zig").trace;
+const Scope = Module.Scope;
+const InnerError = Module.InnerError;
+
+/// Turn Zig AST into untyped ZIR istructions.
+pub fn expr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
+ switch (node.tag) {
+ .VarDecl => unreachable, // Handled in `blockExpr`.
+
+ .Identifier => return identifier(mod, scope, node.castTag(.Identifier).?),
+ .Asm => return assembly(mod, scope, node.castTag(.Asm).?),
+ .StringLiteral => return stringLiteral(mod, scope, node.castTag(.StringLiteral).?),
+ .IntegerLiteral => return integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?),
+ .BuiltinCall => return builtinCall(mod, scope, node.castTag(.BuiltinCall).?),
+ .Call => return callExpr(mod, scope, node.castTag(.Call).?),
+ .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?),
+ .ControlFlowExpression => return controlFlowExpr(mod, scope, node.castTag(.ControlFlowExpression).?),
+ .If => return ifExpr(mod, scope, node.castTag(.If).?),
+ .Assign => return assign(mod, scope, node.castTag(.Assign).?),
+ .Add => return add(mod, scope, node.castTag(.Add).?),
+ .BangEqual => return cmp(mod, scope, node.castTag(.BangEqual).?, .neq),
+ .EqualEqual => return cmp(mod, scope, node.castTag(.EqualEqual).?, .eq),
+ .GreaterThan => return cmp(mod, scope, node.castTag(.GreaterThan).?, .gt),
+ .GreaterOrEqual => return cmp(mod, scope, node.castTag(.GreaterOrEqual).?, .gte),
+ .LessThan => return cmp(mod, scope, node.castTag(.LessThan).?, .lt),
+ .LessOrEqual => return cmp(mod, scope, node.castTag(.LessOrEqual).?, .lte),
+ .BoolNot => return boolNot(mod, scope, node.castTag(.BoolNot).?),
+ else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}),
+ }
+}
+
+pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) !void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ if (block_node.label) |label| {
+ return mod.failTok(parent_scope, label, "TODO implement labeled blocks", .{});
+ }
+
+ var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
+ defer block_arena.deinit();
+
+ var scope = parent_scope;
+ for (block_node.statements()) |statement| {
+ switch (statement.tag) {
+ .VarDecl => {
+ const sub_scope = try block_arena.allocator.create(Scope.LocalVar);
+ const var_decl_node = @fieldParentPtr(ast.Node.VarDecl, "base", statement);
+ sub_scope.* = try varDecl(mod, scope, var_decl_node);
+ scope = &sub_scope.base;
+ },
+ else => _ = try expr(mod, scope, statement),
+ }
+ }
+}
+
+fn varDecl(mod: *Module, scope: *Scope, node: *ast.Node.VarDecl) InnerError!Scope.LocalVar {
+ // TODO implement detection of shadowing
+ if (node.getTrailer("comptime_token")) |comptime_token| {
+ return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
+ }
+ if (node.getTrailer("align_node")) |align_node| {
+ return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{});
+ }
+ if (node.getTrailer("type_node")) |type_node| {
+ return mod.failNode(scope, type_node, "TODO implement typed locals", .{});
+ }
+ const tree = scope.tree();
+ switch (tree.token_ids[node.mut_token]) {
+ .Keyword_const => {},
+ .Keyword_var => {
+ return mod.failTok(scope, node.mut_token, "TODO implement mutable locals", .{});
+ },
+ else => unreachable,
+ }
+ // Depending on the type of AST the initialization expression is, we may need an lvalue
+ // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
+ // the variable, no memory location needed.
+ const init_node = node.getTrailer("init_node").?;
+ if (nodeNeedsMemoryLocation(init_node)) {
+ return mod.failNode(scope, init_node, "TODO implement result locations", .{});
+ }
+ const init_inst = try expr(mod, scope, init_node);
+ const ident_name = tree.tokenSlice(node.name_token); // TODO support @"aoeu" identifiers
+ return Scope.LocalVar{
+ .parent = scope,
+ .gen_zir = scope.getGenZIR(),
+ .name = ident_name,
+ .inst = init_inst,
+ };
+}
+
+fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
+ const operand = try expr(mod, scope, node.rhs);
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ return mod.addZIRInst(scope, src, zir.Inst.BoolNot, .{ .operand = operand }, .{});
+}
+
+fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
+ if (infix_node.lhs.tag == .Identifier) {
+ const ident = @fieldParentPtr(ast.Node.Identifier, "base", infix_node.lhs);
+ const tree = scope.tree();
+ const ident_name = tree.tokenSlice(ident.token);
+ if (std.mem.eql(u8, ident_name, "_")) {
+ return expr(mod, scope, infix_node.rhs);
+ } else {
+ return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
+ }
+ } else {
+ return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
+ }
+}
+
+fn add(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
+ const lhs = try expr(mod, scope, infix_node.lhs);
+ const rhs = try expr(mod, scope, infix_node.rhs);
+
+ const tree = scope.tree();
+ const src = tree.token_locs[infix_node.op_token].start;
+
+ return mod.addZIRInst(scope, src, zir.Inst.Add, .{ .lhs = lhs, .rhs = rhs }, .{});
+}
+
+fn cmp(
+ mod: *Module,
+ scope: *Scope,
+ infix_node: *ast.Node.SimpleInfixOp,
+ op: std.math.CompareOperator,
+) InnerError!*zir.Inst {
+ const lhs = try expr(mod, scope, infix_node.lhs);
+ const rhs = try expr(mod, scope, infix_node.rhs);
+
+ const tree = scope.tree();
+ const src = tree.token_locs[infix_node.op_token].start;
+
+ return mod.addZIRInst(scope, src, zir.Inst.Cmp, .{
+ .lhs = lhs,
+ .op = op,
+ .rhs = rhs,
+ }, .{});
+}
+
+fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.Inst {
+ if (if_node.payload) |payload| {
+ return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for optionals", .{});
+ }
+ if (if_node.@"else") |else_node| {
+ if (else_node.payload) |payload| {
+ return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for error unions", .{});
+ }
+ }
+ var block_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = scope.decl().?,
+ .arena = scope.arena(),
+ .instructions = .{},
+ };
+ defer block_scope.instructions.deinit(mod.gpa);
+
+ const cond = try expr(mod, &block_scope.base, if_node.condition);
+
+ const tree = scope.tree();
+ const if_src = tree.token_locs[if_node.if_token].start;
+ const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{
+ .condition = cond,
+ .true_body = undefined, // populated below
+ .false_body = undefined, // populated below
+ }, .{});
+
+ const block = try mod.addZIRInstBlock(scope, if_src, .{
+ .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
+ });
+ var then_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = block_scope.decl,
+ .arena = block_scope.arena,
+ .instructions = .{},
+ };
+ defer then_scope.instructions.deinit(mod.gpa);
+
+ const then_result = try expr(mod, &then_scope.base, if_node.body);
+ if (!then_result.tag.isNoReturn()) {
+ const then_src = tree.token_locs[if_node.body.lastToken()].start;
+ _ = try mod.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{
+ .block = block,
+ .operand = then_result,
+ }, .{});
+ }
+ condbr.positionals.true_body = .{
+ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
+ };
+
+ var else_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = block_scope.decl,
+ .arena = block_scope.arena,
+ .instructions = .{},
+ };
+ defer else_scope.instructions.deinit(mod.gpa);
+
+ if (if_node.@"else") |else_node| {
+ const else_result = try expr(mod, &else_scope.base, else_node.body);
+ if (!else_result.tag.isNoReturn()) {
+ const else_src = tree.token_locs[else_node.body.lastToken()].start;
+ _ = try mod.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{
+ .block = block,
+ .operand = else_result,
+ }, .{});
+ }
+ } else {
+ // TODO Optimization opportunity: we can avoid an allocation and a memcpy here
+ // by directly allocating the body for this one instruction.
+ const else_src = tree.token_locs[if_node.lastToken()].start;
+ _ = try mod.addZIRInst(&else_scope.base, else_src, zir.Inst.BreakVoid, .{
+ .block = block,
+ }, .{});
+ }
+ condbr.positionals.false_body = .{
+ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
+ };
+
+ return &block.base;
+}
+
+fn controlFlowExpr(
+ mod: *Module,
+ scope: *Scope,
+ cfe: *ast.Node.ControlFlowExpression,
+) InnerError!*zir.Inst {
+ switch (cfe.kind) {
+ .Break => return mod.failNode(scope, &cfe.base, "TODO implement astgen.Expr for Break", .{}),
+ .Continue => return mod.failNode(scope, &cfe.base, "TODO implement astgen.Expr for Continue", .{}),
+ .Return => {},
+ }
+ const tree = scope.tree();
+ const src = tree.token_locs[cfe.ltoken].start;
+ if (cfe.rhs) |rhs_node| {
+ const operand = try expr(mod, scope, rhs_node);
+ return mod.addZIRInst(scope, src, zir.Inst.Return, .{ .operand = operand }, .{});
+ } else {
+ return mod.addZIRInst(scope, src, zir.Inst.ReturnVoid, .{}, .{});
+ }
+}
+
+fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerError!*zir.Inst {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const tree = scope.tree();
+ // TODO implement @"aoeu" identifiers
+ const ident_name = tree.tokenSlice(ident.token);
+ const src = tree.token_locs[ident.token].start;
+ if (mem.eql(u8, ident_name, "_")) {
+ return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});
+ }
+
+ if (getSimplePrimitiveValue(ident_name)) |typed_value| {
+ return mod.addZIRInstConst(scope, src, typed_value);
+ }
+
+ if (ident_name.len >= 2) integer: {
+ const first_c = ident_name[0];
+ if (first_c == 'i' or first_c == 'u') {
+ const is_signed = first_c == 'i';
+ const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
+ error.Overflow => return mod.failNode(
+ scope,
+ &ident.base,
+ "primitive integer type '{}' exceeds maximum bit width of 65535",
+ .{ident_name},
+ ),
+ error.InvalidCharacter => break :integer,
+ };
+ const val = switch (bit_count) {
+ 8 => if (is_signed) Value.initTag(.i8_type) else Value.initTag(.u8_type),
+ 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type),
+ 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type),
+ 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type),
+ else => return mod.failNode(scope, &ident.base, "TODO implement arbitrary integer bitwidth types", .{}),
+ };
+ return mod.addZIRInstConst(scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = val,
+ });
+ }
+ }
+
+ // Local variables, including function parameters.
+ {
+ var s = scope;
+ while (true) switch (s.tag) {
+ .local_var => {
+ const local_var = s.cast(Scope.LocalVar).?;
+ if (mem.eql(u8, local_var.name, ident_name)) {
+ return local_var.inst;
+ }
+ s = local_var.parent;
+ },
+ .gen_zir => s = s.cast(Scope.GenZIR).?.parent,
+ else => break,
+ };
+ }
+
+ if (mod.lookupDeclName(scope, ident_name)) |decl| {
+ return try mod.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
+ }
+
+ return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});
+}
+
+fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.StringLiteral) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const unparsed_bytes = tree.tokenSlice(str_lit.token);
+ const arena = scope.arena();
+
+ var bad_index: usize = undefined;
+ const bytes = std.zig.parseStringLiteral(arena, unparsed_bytes, &bad_index) catch |err| switch (err) {
+ error.InvalidCharacter => {
+ const bad_byte = unparsed_bytes[bad_index];
+ const src = tree.token_locs[str_lit.token].start;
+ return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
+ },
+ else => |e| return e,
+ };
+
+ const src = tree.token_locs[str_lit.token].start;
+ return mod.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
+}
+
+fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral) InnerError!*zir.Inst {
+ const arena = scope.arena();
+ const tree = scope.tree();
+ const prefixed_bytes = tree.tokenSlice(int_lit.token);
+ const base = if (mem.startsWith(u8, prefixed_bytes, "0x"))
+ 16
+ else if (mem.startsWith(u8, prefixed_bytes, "0o"))
+ 8
+ else if (mem.startsWith(u8, prefixed_bytes, "0b"))
+ 2
+ else
+ @as(u8, 10);
+
+ const bytes = if (base == 10)
+ prefixed_bytes
+ else
+ prefixed_bytes[2..];
+
+ if (std.fmt.parseInt(u64, bytes, base)) |small_int| {
+ const int_payload = try arena.create(Value.Payload.Int_u64);
+ int_payload.* = .{ .int = small_int };
+ const src = tree.token_locs[int_lit.token].start;
+ return mod.addZIRInstConst(scope, src, .{
+ .ty = Type.initTag(.comptime_int),
+ .val = Value.initPayload(&int_payload.base),
+ });
+ } else |err| {
+ return mod.failTok(scope, int_lit.token, "TODO implement int literals that don't fit in a u64", .{});
+ }
+}
+
+fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
+ if (asm_node.outputs.len != 0) {
+ return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{});
+ }
+ const arena = scope.arena();
+ const tree = scope.tree();
+
+ const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len);
+ const args = try arena.alloc(*zir.Inst, asm_node.inputs.len);
+
+ for (asm_node.inputs) |input, i| {
+ // TODO semantically analyze constraints
+ inputs[i] = try expr(mod, scope, input.constraint);
+ args[i] = try expr(mod, scope, input.expr);
+ }
+
+ const src = tree.token_locs[asm_node.asm_token].start;
+ const return_type = try mod.addZIRInstConst(scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.void_type),
+ });
+ const asm_inst = try mod.addZIRInst(scope, src, zir.Inst.Asm, .{
+ .asm_source = try expr(mod, scope, asm_node.template),
+ .return_type = return_type,
+ }, .{
+ .@"volatile" = asm_node.volatile_token != null,
+ //.clobbers = TODO handle clobbers
+ .inputs = inputs,
+ .args = args,
+ });
+ return asm_inst;
+}
+
+fn builtinCall(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const builtin_name = tree.tokenSlice(call.builtin_token);
+ const src = tree.token_locs[call.builtin_token].start;
+
+ inline for (std.meta.declarations(zir.Inst)) |inst| {
+ if (inst.data != .Type) continue;
+ const T = inst.data.Type;
+ if (!@hasDecl(T, "builtin_name")) continue;
+ if (std.mem.eql(u8, builtin_name, T.builtin_name)) {
+ var value: T = undefined;
+ const positionals = @typeInfo(std.meta.fieldInfo(T, "positionals").field_type).Struct;
+ if (positionals.fields.len == 0) {
+ return mod.addZIRInst(scope, src, T, value.positionals, value.kw_args);
+ }
+ const arg_count: ?usize = if (positionals.fields[0].field_type == []*zir.Inst) null else positionals.fields.len;
+ if (arg_count) |some| {
+ if (call.params_len != some) {
+ return mod.failTok(
+ scope,
+ call.builtin_token,
+ "expected {} parameter{}, found {}",
+ .{ some, if (some == 1) "" else "s", call.params_len },
+ );
+ }
+ const params = call.params();
+ inline for (positionals.fields) |p, i| {
+ @field(value.positionals, p.name) = try expr(mod, scope, params[i]);
+ }
+ } else {
+ return mod.failTok(scope, call.builtin_token, "TODO var args builtin '{}'", .{builtin_name});
+ }
+
+ return mod.addZIRInst(scope, src, T, value.positionals, .{});
+ }
+ }
+ return mod.failTok(scope, call.builtin_token, "TODO implement builtin call for '{}'", .{builtin_name});
+}
+
+fn callExpr(mod: *Module, scope: *Scope, node: *ast.Node.Call) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const lhs = try expr(mod, scope, node.lhs);
+
+ const param_nodes = node.params();
+ const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);
+ for (param_nodes) |param_node, i| {
+ args[i] = try expr(mod, scope, param_node);
+ }
+
+ const src = tree.token_locs[node.lhs.firstToken()].start;
+ return mod.addZIRInst(scope, src, zir.Inst.Call, .{
+ .func = lhs,
+ .args = args,
+ }, .{});
+}
+
+fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.Unreachable) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[unreach_node.token].start;
+ return mod.addZIRInst(scope, src, zir.Inst.Unreachable, .{}, .{});
+}
+
+fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
+ const simple_types = std.ComptimeStringMap(Value.Tag, .{
+ .{ "u8", .u8_type },
+ .{ "i8", .i8_type },
+ .{ "isize", .isize_type },
+ .{ "usize", .usize_type },
+ .{ "c_short", .c_short_type },
+ .{ "c_ushort", .c_ushort_type },
+ .{ "c_int", .c_int_type },
+ .{ "c_uint", .c_uint_type },
+ .{ "c_long", .c_long_type },
+ .{ "c_ulong", .c_ulong_type },
+ .{ "c_longlong", .c_longlong_type },
+ .{ "c_ulonglong", .c_ulonglong_type },
+ .{ "c_longdouble", .c_longdouble_type },
+ .{ "f16", .f16_type },
+ .{ "f32", .f32_type },
+ .{ "f64", .f64_type },
+ .{ "f128", .f128_type },
+ .{ "c_void", .c_void_type },
+ .{ "bool", .bool_type },
+ .{ "void", .void_type },
+ .{ "type", .type_type },
+ .{ "anyerror", .anyerror_type },
+ .{ "comptime_int", .comptime_int_type },
+ .{ "comptime_float", .comptime_float_type },
+ .{ "noreturn", .noreturn_type },
+ });
+ if (simple_types.get(name)) |tag| {
+ return TypedValue{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(tag),
+ };
+ }
+ if (mem.eql(u8, name, "null")) {
+ return TypedValue{
+ .ty = Type.initTag(.@"null"),
+ .val = Value.initTag(.null_value),
+ };
+ }
+ if (mem.eql(u8, name, "undefined")) {
+ return TypedValue{
+ .ty = Type.initTag(.@"undefined"),
+ .val = Value.initTag(.undef),
+ };
+ }
+ if (mem.eql(u8, name, "true")) {
+ return TypedValue{
+ .ty = Type.initTag(.bool),
+ .val = Value.initTag(.bool_true),
+ };
+ }
+ if (mem.eql(u8, name, "false")) {
+ return TypedValue{
+ .ty = Type.initTag(.bool),
+ .val = Value.initTag(.bool_false),
+ };
+ }
+ return null;
+}
+
+fn nodeNeedsMemoryLocation(node: *ast.Node) bool {
+ return switch (node.tag) {
+ .Root,
+ .Use,
+ .TestDecl,
+ .DocComment,
+ .SwitchCase,
+ .SwitchElse,
+ .Else,
+ .Payload,
+ .PointerPayload,
+ .PointerIndexPayload,
+ .ContainerField,
+ .ErrorTag,
+ .FieldInitializer,
+ => unreachable,
+
+ .ControlFlowExpression,
+ .BitNot,
+ .BoolNot,
+ .VarDecl,
+ .Defer,
+ .AddressOf,
+ .OptionalType,
+ .Negation,
+ .NegationWrap,
+ .Resume,
+ .ArrayType,
+ .ArrayTypeSentinel,
+ .PtrType,
+ .SliceType,
+ .Suspend,
+ .AnyType,
+ .ErrorType,
+ .FnProto,
+ .AnyFrameType,
+ .IntegerLiteral,
+ .FloatLiteral,
+ .EnumLiteral,
+ .StringLiteral,
+ .MultilineStringLiteral,
+ .CharLiteral,
+ .BoolLiteral,
+ .NullLiteral,
+ .UndefinedLiteral,
+ .Unreachable,
+ .Identifier,
+ .ErrorSetDecl,
+ .ContainerDecl,
+ .Asm,
+ .Add,
+ .AddWrap,
+ .ArrayCat,
+ .ArrayMult,
+ .Assign,
+ .AssignBitAnd,
+ .AssignBitOr,
+ .AssignBitShiftLeft,
+ .AssignBitShiftRight,
+ .AssignBitXor,
+ .AssignDiv,
+ .AssignSub,
+ .AssignSubWrap,
+ .AssignMod,
+ .AssignAdd,
+ .AssignAddWrap,
+ .AssignMul,
+ .AssignMulWrap,
+ .BangEqual,
+ .BitAnd,
+ .BitOr,
+ .BitShiftLeft,
+ .BitShiftRight,
+ .BitXor,
+ .BoolAnd,
+ .BoolOr,
+ .Div,
+ .EqualEqual,
+ .ErrorUnion,
+ .GreaterOrEqual,
+ .GreaterThan,
+ .LessOrEqual,
+ .LessThan,
+ .MergeErrorSets,
+ .Mod,
+ .Mul,
+ .MulWrap,
+ .Range,
+ .Period,
+ .Sub,
+ .SubWrap,
+ => false,
+
+ .ArrayInitializer,
+ .ArrayInitializerDot,
+ .StructInitializer,
+ .StructInitializerDot,
+ => true,
+
+ .GroupedExpression => nodeNeedsMemoryLocation(node.castTag(.GroupedExpression).?.expr),
+
+ .UnwrapOptional => @panic("TODO nodeNeedsMemoryLocation for UnwrapOptional"),
+ .Catch => @panic("TODO nodeNeedsMemoryLocation for Catch"),
+ .Await => @panic("TODO nodeNeedsMemoryLocation for Await"),
+ .Try => @panic("TODO nodeNeedsMemoryLocation for Try"),
+ .If => @panic("TODO nodeNeedsMemoryLocation for If"),
+ .SuffixOp => @panic("TODO nodeNeedsMemoryLocation for SuffixOp"),
+ .Call => @panic("TODO nodeNeedsMemoryLocation for Call"),
+ .Switch => @panic("TODO nodeNeedsMemoryLocation for Switch"),
+ .While => @panic("TODO nodeNeedsMemoryLocation for While"),
+ .For => @panic("TODO nodeNeedsMemoryLocation for For"),
+ .BuiltinCall => @panic("TODO nodeNeedsMemoryLocation for BuiltinCall"),
+ .Comptime => @panic("TODO nodeNeedsMemoryLocation for Comptime"),
+ .Nosuspend => @panic("TODO nodeNeedsMemoryLocation for Nosuspend"),
+ .Block => @panic("TODO nodeNeedsMemoryLocation for Block"),
+ };
+}
diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig
index 314d497808..e78ee28b5d 100644
--- a/src-self-hosted/codegen.zig
+++ b/src-self-hosted/codegen.zig
@@ -73,6 +73,7 @@ pub fn generateSymbol(
.code = code,
.err_msg = null,
.args = mc_args,
+ .arg_index = 0,
.branch_stack = &branch_stack,
.src = src,
};
@@ -255,6 +256,7 @@ const Function = struct {
code: *std.ArrayList(u8),
err_msg: ?*ErrorMsg,
args: []MCValue,
+ arg_index: usize,
src: usize,
/// Whenever there is a runtime branch, we push a Branch onto this stack,
@@ -603,7 +605,9 @@ const Function = struct {
}
fn genArg(self: *Function, inst: *ir.Inst.Arg) !MCValue {
- return self.args[inst.args.index];
+ const i = self.arg_index;
+ self.arg_index += 1;
+ return self.args[i];
}
fn genBreakpoint(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch) !MCValue {
diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig
index c654bef611..a150957de0 100644
--- a/src-self-hosted/ir.zig
+++ b/src-self-hosted/ir.zig
@@ -101,10 +101,7 @@ pub const Inst = struct {
pub const Arg = struct {
pub const base_tag = Tag.arg;
base: Inst,
-
- args: struct {
- index: usize,
- },
+ args: void,
};
pub const Assembly = struct {
diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig
index b9ab28cc17..1da52cda96 100644
--- a/src-self-hosted/translate_c.zig
+++ b/src-self-hosted/translate_c.zig
@@ -1103,11 +1103,11 @@ fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.No
const enum_ident = try transCreateNodeIdentifier(c, name);
const period_tok = try appendToken(c, .Period, ".");
const field_ident = try transCreateNodeIdentifier(c, field_name);
- const field_access_node = try c.arena.create(ast.Node.InfixOp);
+ const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp);
field_access_node.* = .{
+ .base = .{ .tag = .Period },
.op_token = period_tok,
.lhs = enum_ident,
- .op = .Period,
.rhs = field_ident,
};
cast_node.params()[0] = &field_access_node.base;
@@ -1219,7 +1219,7 @@ fn transStmt(
.StringLiteralClass => return transStringLiteral(rp, scope, @ptrCast(*const ZigClangStringLiteral, stmt), result_used),
.ParenExprClass => {
const expr = try transExpr(rp, scope, ZigClangParenExpr_getSubExpr(@ptrCast(*const ZigClangParenExpr, stmt)), .used, lrvalue);
- if (expr.id == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
+ if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
const node = try rp.c.arena.create(ast.Node.GroupedExpression);
node.* = .{
.lparen = try appendToken(rp.c, .LParen, "("),
@@ -1264,7 +1264,7 @@ fn transStmt(
.OpaqueValueExprClass => {
const source_expr = ZigClangOpaqueValueExpr_getSourceExpr(@ptrCast(*const ZigClangOpaqueValueExpr, stmt)).?;
const expr = try transExpr(rp, scope, source_expr, .used, lrvalue);
- if (expr.id == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
+ if (expr.tag == .GroupedExpression) return maybeSuppressResult(rp, scope, result_used, expr);
const node = try rp.c.arena.create(ast.Node.GroupedExpression);
node.* = .{
.lparen = try appendToken(rp.c, .LParen, "("),
@@ -1294,7 +1294,7 @@ fn transBinaryOperator(
const op = ZigClangBinaryOperator_getOpcode(stmt);
const qt = ZigClangBinaryOperator_getType(stmt);
var op_token: ast.TokenIndex = undefined;
- var op_id: ast.Node.InfixOp.Op = undefined;
+ var op_id: ast.Node.Tag = undefined;
switch (op) {
.Assign => return try transCreateNodeAssign(rp, scope, result_used, ZigClangBinaryOperator_getLHS(stmt), ZigClangBinaryOperator_getRHS(stmt)),
.Comma => {
@@ -1693,7 +1693,7 @@ fn transBoolExpr(
var res = try transExpr(rp, scope, expr, used, lrvalue);
if (isBoolRes(res)) {
- if (!grouped and res.id == .GroupedExpression) {
+ if (!grouped and res.tag == .GroupedExpression) {
const group = @fieldParentPtr(ast.Node.GroupedExpression, "base", res);
res = group.expr;
// get zig fmt to work properly
@@ -1736,26 +1736,23 @@ fn exprIsStringLiteral(expr: *const ZigClangExpr) bool {
}
fn isBoolRes(res: *ast.Node) bool {
- switch (res.id) {
- .InfixOp => switch (@fieldParentPtr(ast.Node.InfixOp, "base", res).op) {
- .BoolOr,
- .BoolAnd,
- .EqualEqual,
- .BangEqual,
- .LessThan,
- .GreaterThan,
- .LessOrEqual,
- .GreaterOrEqual,
- => return true,
+ switch (res.tag) {
+ .BoolOr,
+ .BoolAnd,
+ .EqualEqual,
+ .BangEqual,
+ .LessThan,
+ .GreaterThan,
+ .LessOrEqual,
+ .GreaterOrEqual,
+ .BoolNot,
+ .BoolLiteral,
+ => return true,
- else => {},
- },
- .BoolNot => return true,
- .BoolLiteral => return true,
.GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr),
- else => {},
+
+ else => return false,
}
- return false;
}
fn finishBoolExpr(
@@ -2312,11 +2309,11 @@ fn transInitListExprArray(
&filler_init_node.base
else blk: {
const mul_tok = try appendToken(rp.c, .AsteriskAsterisk, "**");
- const mul_node = try rp.c.arena.create(ast.Node.InfixOp);
+ const mul_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
mul_node.* = .{
+ .base = .{ .tag = .ArrayMult },
.op_token = mul_tok,
.lhs = &filler_init_node.base,
- .op = .ArrayMult,
.rhs = try transCreateNodeInt(rp.c, leftover_count),
};
break :blk &mul_node.base;
@@ -2326,11 +2323,11 @@ fn transInitListExprArray(
return rhs_node;
}
- const cat_node = try rp.c.arena.create(ast.Node.InfixOp);
+ const cat_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
cat_node.* = .{
+ .base = .{ .tag = .ArrayCat },
.op_token = cat_tok,
.lhs = &init_node.base,
- .op = .ArrayCat,
.rhs = rhs_node,
};
return &cat_node.base;
@@ -2723,11 +2720,11 @@ fn transCase(
const ellips = try appendToken(rp.c, .Ellipsis3, "...");
const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value);
- const node = try rp.c.arena.create(ast.Node.InfixOp);
+ const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
node.* = .{
+ .base = .{ .tag = .Range },
.op_token = ellips,
.lhs = lhs_node,
- .op = .Range,
.rhs = rhs_node,
};
break :blk &node.base;
@@ -3153,7 +3150,7 @@ fn transCreatePreCrement(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangUnaryOperator,
- op: ast.Node.InfixOp.Op,
+ op: ast.Node.Tag,
op_tok_id: std.zig.Token.Id,
bytes: []const u8,
used: ResultUsed,
@@ -3227,7 +3224,7 @@ fn transCreatePostCrement(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangUnaryOperator,
- op: ast.Node.InfixOp.Op,
+ op: ast.Node.Tag,
op_tok_id: std.zig.Token.Id,
bytes: []const u8,
used: ResultUsed,
@@ -3349,10 +3346,10 @@ fn transCreateCompoundAssign(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangCompoundAssignOperator,
- assign_op: ast.Node.InfixOp.Op,
+ assign_op: ast.Node.Tag,
assign_tok_id: std.zig.Token.Id,
assign_bytes: []const u8,
- bin_op: ast.Node.InfixOp.Op,
+ bin_op: ast.Node.Tag,
bin_tok_id: std.zig.Token.Id,
bin_bytes: []const u8,
used: ResultUsed,
@@ -3377,7 +3374,7 @@ fn transCreateCompoundAssign(
// zig: lhs += rhs
if ((is_mod or is_div) and is_signed) {
const op_token = try appendToken(rp.c, .Equal, "=");
- const op_node = try rp.c.arena.create(ast.Node.InfixOp);
+ const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
const builtin = if (is_mod) "@rem" else "@divTrunc";
const builtin_node = try rp.c.createBuiltinCall(builtin, 2);
const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value);
@@ -3386,9 +3383,9 @@ fn transCreateCompoundAssign(
builtin_node.params()[1] = try transExpr(rp, scope, rhs, .used, .r_value);
builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
op_node.* = .{
+ .base = .{ .tag = .Assign },
.op_token = op_token,
.lhs = lhs_node,
- .op = .Assign,
.rhs = &builtin_node.base,
};
_ = try appendToken(rp.c, .Semicolon, ";");
@@ -3452,7 +3449,7 @@ fn transCreateCompoundAssign(
if ((is_mod or is_div) and is_signed) {
const op_token = try appendToken(rp.c, .Equal, "=");
- const op_node = try rp.c.arena.create(ast.Node.InfixOp);
+ const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
const builtin = if (is_mod) "@rem" else "@divTrunc";
const builtin_node = try rp.c.createBuiltinCall(builtin, 2);
builtin_node.params()[0] = try transCreateNodePtrDeref(rp.c, lhs_node);
@@ -3461,9 +3458,9 @@ fn transCreateCompoundAssign(
builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
_ = try appendToken(rp.c, .Semicolon, ";");
op_node.* = .{
+ .base = .{ .tag = .Assign },
.op_token = op_token,
.lhs = ref_node,
- .op = .Assign,
.rhs = &builtin_node.base,
};
_ = try appendToken(rp.c, .Semicolon, ";");
@@ -3716,11 +3713,11 @@ fn maybeSuppressResult(
}
const lhs = try transCreateNodeIdentifier(rp.c, "_");
const op_token = try appendToken(rp.c, .Equal, "=");
- const op_node = try rp.c.arena.create(ast.Node.InfixOp);
+ const op_node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
op_node.* = .{
+ .base = .{ .tag = .Assign },
.op_token = op_token,
.lhs = lhs,
- .op = .Assign,
.rhs = result,
};
return &op_node.base;
@@ -4095,11 +4092,11 @@ fn transCreateNodeAssign(
}
fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node {
- const field_access_node = try c.arena.create(ast.Node.InfixOp);
+ const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp);
field_access_node.* = .{
+ .base = .{ .tag = .Period },
.op_token = try appendToken(c, .Period, "."),
.lhs = container,
- .op = .Period,
.rhs = try transCreateNodeIdentifier(c, field_name),
};
return &field_access_node.base;
@@ -4107,12 +4104,13 @@ fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []c
fn transCreateNodeSimplePrefixOp(
c: *Context,
- comptime tag: ast.Node.Id,
+ comptime tag: ast.Node.Tag,
op_tok_id: std.zig.Token.Id,
bytes: []const u8,
-) !*ast.Node.SimplePrefixOp(tag) {
- const node = try c.arena.create(ast.Node.SimplePrefixOp(tag));
+) !*ast.Node.SimplePrefixOp {
+ const node = try c.arena.create(ast.Node.SimplePrefixOp);
node.* = .{
+ .base = .{ .tag = tag },
.op_token = try appendToken(c, op_tok_id, bytes),
.rhs = undefined, // translate and set afterward
};
@@ -4123,7 +4121,7 @@ fn transCreateNodeInfixOp(
rp: RestorePoint,
scope: *Scope,
lhs_node: *ast.Node,
- op: ast.Node.InfixOp.Op,
+ op: ast.Node.Tag,
op_token: ast.TokenIndex,
rhs_node: *ast.Node,
used: ResultUsed,
@@ -4133,11 +4131,11 @@ fn transCreateNodeInfixOp(
try appendToken(rp.c, .LParen, "(")
else
null;
- const node = try rp.c.arena.create(ast.Node.InfixOp);
+ const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
node.* = .{
+ .base = .{ .tag = op },
.op_token = op_token,
.lhs = lhs_node,
- .op = op,
.rhs = rhs_node,
};
if (!grouped) return maybeSuppressResult(rp, scope, used, &node.base);
@@ -4155,7 +4153,7 @@ fn transCreateNodeBoolInfixOp(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangBinaryOperator,
- op: ast.Node.InfixOp.Op,
+ op: ast.Node.Tag,
used: ResultUsed,
grouped: bool,
) !*ast.Node {
@@ -4535,7 +4533,7 @@ fn transCreateNodeShiftOp(
rp: RestorePoint,
scope: *Scope,
stmt: *const ZigClangBinaryOperator,
- op: ast.Node.InfixOp.Op,
+ op: ast.Node.Tag,
op_tok_id: std.zig.Token.Id,
bytes: []const u8,
) !*ast.Node {
@@ -4557,11 +4555,11 @@ fn transCreateNodeShiftOp(
cast_node.params()[1] = rhs;
cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
- const node = try rp.c.arena.create(ast.Node.InfixOp);
+ const node = try rp.c.arena.create(ast.Node.SimpleInfixOp);
node.* = .{
+ .base = .{ .tag = op },
.op_token = op_token,
.lhs = lhs,
- .op = op,
.rhs = &cast_node.base,
};
@@ -5338,10 +5336,10 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
.{@tagName(last.id)},
);
_ = try appendToken(c, .Semicolon, ";");
- const type_of_arg = if (expr.id != .Block) expr else blk: {
+ const type_of_arg = if (expr.tag != .Block) expr else blk: {
const blk = @fieldParentPtr(ast.Node.Block, "base", expr);
const blk_last = blk.statements()[blk.statements_len - 1];
- std.debug.assert(blk_last.id == .ControlFlowExpression);
+ std.debug.assert(blk_last.tag == .ControlFlowExpression);
const br = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", blk_last);
break :blk br.rhs.?;
};
@@ -5403,11 +5401,11 @@ fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_
// suppress result
const lhs = try transCreateNodeIdentifier(c, "_");
const op_token = try appendToken(c, .Equal, "=");
- const op_node = try c.arena.create(ast.Node.InfixOp);
+ const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
op_node.* = .{
+ .base = .{ .tag = .Assign },
.op_token = op_token,
.lhs = lhs,
- .op = .Assign,
.rhs = last,
};
try block_scope.statements.append(&op_node.base);
@@ -5786,9 +5784,60 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
}
}
+fn nodeIsInfixOp(tag: ast.Node.Tag) bool {
+ return switch (tag) {
+ .Add,
+ .AddWrap,
+ .ArrayCat,
+ .ArrayMult,
+ .Assign,
+ .AssignBitAnd,
+ .AssignBitOr,
+ .AssignBitShiftLeft,
+ .AssignBitShiftRight,
+ .AssignBitXor,
+ .AssignDiv,
+ .AssignSub,
+ .AssignSubWrap,
+ .AssignMod,
+ .AssignAdd,
+ .AssignAddWrap,
+ .AssignMul,
+ .AssignMulWrap,
+ .BangEqual,
+ .BitAnd,
+ .BitOr,
+ .BitShiftLeft,
+ .BitShiftRight,
+ .BitXor,
+ .BoolAnd,
+ .BoolOr,
+ .Div,
+ .EqualEqual,
+ .ErrorUnion,
+ .GreaterOrEqual,
+ .GreaterThan,
+ .LessOrEqual,
+ .LessThan,
+ .MergeErrorSets,
+ .Mod,
+ .Mul,
+ .MulWrap,
+ .Period,
+ .Range,
+ .Sub,
+ .SubWrap,
+ .UnwrapOptional,
+ .Catch,
+ => true,
+
+ else => false,
+ };
+}
+
fn macroBoolToInt(c: *Context, node: *ast.Node) !*ast.Node {
if (!isBoolRes(node)) {
- if (node.id != .InfixOp) return node;
+ if (!nodeIsInfixOp(node.tag)) return node;
const group_node = try c.arena.create(ast.Node.GroupedExpression);
group_node.* = .{
@@ -5807,7 +5856,7 @@ fn macroBoolToInt(c: *Context, node: *ast.Node) !*ast.Node {
fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
if (isBoolRes(node)) {
- if (node.id != .InfixOp) return node;
+ if (!nodeIsInfixOp(node.tag)) return node;
const group_node = try c.arena.create(ast.Node.GroupedExpression);
group_node.* = .{
@@ -5820,11 +5869,11 @@ fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
const op_token = try appendToken(c, .BangEqual, "!=");
const zero = try transCreateNodeInt(c, 0);
- const res = try c.arena.create(ast.Node.InfixOp);
+ const res = try c.arena.create(ast.Node.SimpleInfixOp);
res.* = .{
+ .base = .{ .tag = .BangEqual },
.op_token = op_token,
.lhs = node,
- .op = .BangEqual,
.rhs = zero,
};
const group_node = try c.arena.create(ast.Node.GroupedExpression);
@@ -5841,7 +5890,7 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
while (true) {
const tok = it.next().?;
var op_token: ast.TokenIndex = undefined;
- var op_id: ast.Node.InfixOp.Op = undefined;
+ var op_id: ast.Node.Tag = undefined;
var bool_op = false;
switch (tok.id) {
.Period => {
@@ -6048,11 +6097,11 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt;
const lhs_node = try cast_fn(c, node);
const rhs_node = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
- const op_node = try c.arena.create(ast.Node.InfixOp);
+ const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
op_node.* = .{
+ .base = .{ .tag = op_id },
.op_token = op_token,
.lhs = lhs_node,
- .op = op_id,
.rhs = try cast_fn(c, rhs_node),
};
node = &op_node.base;
@@ -6105,7 +6154,7 @@ fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 {
}
fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
- switch (node.id) {
+ switch (node.tag) {
.ContainerDecl,
.AddressOf,
.Await,
@@ -6130,10 +6179,9 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
}
},
- .InfixOp => {
- const infix = node.cast(ast.Node.InfixOp).?;
- if (infix.op != .Period)
- return null;
+ .Period => {
+ const infix = node.castTag(.Period).?;
+
if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
for (container.fieldsAndDecls()) |field_ref| {
@@ -6160,9 +6208,7 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
return getContainer(c, ty);
}
}
- } else if (ref.cast(ast.Node.InfixOp)) |infix| {
- if (infix.op != .Period)
- return null;
+ } else if (ref.castTag(.Period)) |infix| {
if (getContainerTypeOf(c, infix.lhs)) |ty_node| {
if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
for (container.fieldsAndDecls()) |field_ref| {
@@ -6182,7 +6228,7 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto {
const init = if (ref.cast(ast.Node.VarDecl)) |v| v.getTrailer("init_node").? else return null;
if (getContainerTypeOf(c, init)) |ty_node| {
- if (ty_node.cast(ast.Node.OptionalType)) |prefix| {
+ if (ty_node.castTag(.OptionalType)) |prefix| {
if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| {
return fn_proto;
}
diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig
index 45ced54255..2f696d1787 100644
--- a/src-self-hosted/zir.zig
+++ b/src-self-hosted/zir.zig
@@ -34,7 +34,8 @@ pub const Inst = struct {
/// These names are used directly as the instruction names in the text format.
pub const Tag = enum {
- /// Function parameter value.
+ /// Function parameter value. These must be first in a function's main block,
+ /// in respective order with the parameters.
arg,
/// A labeled block of code, which can return a value.
block,
@@ -184,9 +185,7 @@ pub const Inst = struct {
pub const base_tag = Tag.arg;
base: Inst,
- positionals: struct {
- index: usize,
- },
+ positionals: struct {},
kw_args: struct {},
};
@@ -1384,15 +1383,17 @@ const EmitZIR = struct {
for (src_decls.items) |ir_decl| {
switch (ir_decl.analysis) {
.unreferenced => continue,
+
.complete => {},
+ .codegen_failure => {}, // We still can emit the ZIR.
+ .codegen_failure_retryable => {}, // We still can emit the ZIR.
+
.in_progress => unreachable,
.outdated => unreachable,
.sema_failure,
.sema_failure_retryable,
- .codegen_failure,
.dependency_failure,
- .codegen_failure_retryable,
=> if (self.old_module.failed_decls.get(ir_decl)) |err_msg| {
const fail_inst = try self.arena.allocator.create(Inst.CompileError);
fail_inst.* = .{
@@ -1728,7 +1729,7 @@ const EmitZIR = struct {
.src = inst.src,
.tag = Inst.Arg.base_tag,
},
- .positionals = .{ .index = old_inst.args.index },
+ .positionals = .{},
.kw_args = .{},
};
break :blk &new_inst.base;