Commit aac6e8c418
Changed files (12)
lib
src-self-hosted
test
stage2
lib/std/zig/ast.zig
@@ -495,8 +495,10 @@ pub const Node = struct {
While,
For,
If,
- ControlFlowExpression,
Suspend,
+ Continue,
+ Break,
+ Return,
// Type expressions
AnyType,
@@ -601,6 +603,24 @@ pub const Node = struct {
.Try,
=> SimplePrefixOp,
+ .Identifier,
+ .BoolLiteral,
+ .NullLiteral,
+ .UndefinedLiteral,
+ .Unreachable,
+ .AnyType,
+ .ErrorType,
+ .IntegerLiteral,
+ .FloatLiteral,
+ .StringLiteral,
+ .CharLiteral,
+ => OneToken,
+
+ .Continue,
+ .Break,
+ .Return,
+ => ControlFlowExpression,
+
.ArrayType => ArrayType,
.ArrayTypeSentinel => ArrayTypeSentinel,
@@ -621,23 +641,11 @@ pub const Node = struct {
.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,
@@ -1182,19 +1190,19 @@ pub const Node = struct {
}
};
- pub const Identifier = struct {
- base: Node = Node{ .tag = .Identifier },
+ pub const OneToken = struct {
+ base: Node,
token: TokenIndex,
- pub fn iterate(self: *const Identifier, index: usize) ?*Node {
+ pub fn iterate(self: *const OneToken, index: usize) ?*Node {
return null;
}
- pub fn firstToken(self: *const Identifier) TokenIndex {
+ pub fn firstToken(self: *const OneToken) TokenIndex {
return self.token;
}
- pub fn lastToken(self: *const Identifier) TokenIndex {
+ pub fn lastToken(self: *const OneToken) TokenIndex {
return self.token;
}
};
@@ -2569,34 +2577,65 @@ 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.
+ /// Trailed in memory by possibly many things, with each optional thing
+ /// determined by a bit in `trailer_flags`.
+ /// Can be: return, break, continue
pub const ControlFlowExpression = struct {
- base: Node = Node{ .tag = .ControlFlowExpression },
+ base: Node,
+ trailer_flags: TrailerFlags,
ltoken: TokenIndex,
- kind: Kind,
- rhs: ?*Node,
- pub const Kind = union(enum) {
- Break: ?*Node,
- Continue: ?*Node,
- Return,
+ pub const TrailerFlags = std.meta.TrailerFlags(struct {
+ rhs: *Node,
+ label: TokenIndex,
+ });
+
+ pub const RequiredFields = struct {
+ tag: Tag,
+ ltoken: TokenIndex,
};
+ pub fn getRHS(self: *const ControlFlowExpression) ?*Node {
+ return self.getTrailer("rhs");
+ }
+
+ pub fn getLabel(self: *const ControlFlowExpression) ?TokenIndex {
+ return self.getTrailer("label");
+ }
+
+ pub fn getTrailer(self: *const ControlFlowExpression, comptime name: []const u8) ?TrailerFlags.Field(name) {
+ const trailers_start = @ptrCast([*]const u8, self) + @sizeOf(ControlFlowExpression);
+ return self.trailer_flags.get(trailers_start, name);
+ }
+
+ pub fn setTrailer(self: *ControlFlowExpression, comptime name: []const u8, value: TrailerFlags.Field(name)) void {
+ const trailers_start = @ptrCast([*]u8, self) + @sizeOf(ControlFlowExpression);
+ self.trailer_flags.set(trailers_start, name, value);
+ }
+
+ pub fn create(allocator: *mem.Allocator, required: RequiredFields, trailers: anytype) !*ControlFlowExpression {
+ const trailer_flags = TrailerFlags.init(trailers);
+ const bytes = try allocator.alignedAlloc(u8, @alignOf(ControlFlowExpression), sizeInBytes(trailer_flags));
+ const ctrl_flow_expr = @ptrCast(*ControlFlowExpression, bytes.ptr);
+ ctrl_flow_expr.* = .{
+ .base = .{ .tag = required.tag },
+ .trailer_flags = trailer_flags,
+ .ltoken = required.ltoken,
+ };
+ const trailers_start = bytes.ptr + @sizeOf(ControlFlowExpression);
+ trailer_flags.setMany(trailers_start, trailers);
+ return ctrl_flow_expr;
+ }
+
+ pub fn destroy(self: *ControlFlowExpression, allocator: *mem.Allocator) void {
+ const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.trailer_flags)];
+ allocator.free(bytes);
+ }
+
pub fn iterate(self: *const ControlFlowExpression, index: usize) ?*Node {
var i = index;
- switch (self.kind) {
- .Break, .Continue => |maybe_label| {
- if (maybe_label) |label| {
- if (i < 1) return label;
- i -= 1;
- }
- },
- .Return => {},
- }
-
- if (self.rhs) |rhs| {
+ if (self.getRHS()) |rhs| {
if (i < 1) return rhs;
i -= 1;
}
@@ -2609,21 +2648,20 @@ pub const Node = struct {
}
pub fn lastToken(self: *const ControlFlowExpression) TokenIndex {
- if (self.rhs) |rhs| {
+ if (self.getRHS()) |rhs| {
return rhs.lastToken();
}
- switch (self.kind) {
- .Break, .Continue => |maybe_label| {
- if (maybe_label) |label| {
- return label.lastToken();
- }
- },
- .Return => return self.ltoken,
+ if (self.getLabel()) |label| {
+ return label;
}
return self.ltoken;
}
+
+ fn sizeInBytes(trailer_flags: TrailerFlags) usize {
+ return @sizeOf(ControlFlowExpression) + trailer_flags.sizeInBytes();
+ }
};
pub const Suspend = struct {
@@ -2655,23 +2693,6 @@ pub const Node = struct {
}
};
- pub const IntegerLiteral = struct {
- base: Node = Node{ .tag = .IntegerLiteral },
- token: TokenIndex,
-
- pub fn iterate(self: *const IntegerLiteral, index: usize) ?*Node {
- return null;
- }
-
- pub fn firstToken(self: *const IntegerLiteral) TokenIndex {
- return self.token;
- }
-
- pub fn lastToken(self: *const IntegerLiteral) TokenIndex {
- return self.token;
- }
- };
-
pub const EnumLiteral = struct {
base: Node = Node{ .tag = .EnumLiteral },
dot: TokenIndex,
@@ -2690,23 +2711,6 @@ pub const Node = struct {
}
};
- pub const FloatLiteral = struct {
- base: Node = Node{ .tag = .FloatLiteral },
- token: TokenIndex,
-
- pub fn iterate(self: *const FloatLiteral, index: usize) ?*Node {
- return null;
- }
-
- pub fn firstToken(self: *const FloatLiteral) TokenIndex {
- return self.token;
- }
-
- pub fn lastToken(self: *const FloatLiteral) TokenIndex {
- return self.token;
- }
- };
-
/// Parameters are in memory following BuiltinCall.
pub const BuiltinCall = struct {
base: Node = Node{ .tag = .BuiltinCall },
@@ -2757,23 +2761,6 @@ pub const Node = struct {
}
};
- pub const StringLiteral = struct {
- base: Node = Node{ .tag = .StringLiteral },
- token: TokenIndex,
-
- pub fn iterate(self: *const StringLiteral, index: usize) ?*Node {
- return null;
- }
-
- pub fn firstToken(self: *const StringLiteral) TokenIndex {
- return self.token;
- }
-
- pub fn lastToken(self: *const StringLiteral) TokenIndex {
- return self.token;
- }
- };
-
/// The string literal tokens appear directly in memory after MultilineStringLiteral.
pub const MultilineStringLiteral = struct {
base: Node = Node{ .tag = .MultilineStringLiteral },
@@ -2817,74 +2804,6 @@ pub const Node = struct {
}
};
- pub const CharLiteral = struct {
- base: Node = Node{ .tag = .CharLiteral },
- token: TokenIndex,
-
- pub fn iterate(self: *const CharLiteral, index: usize) ?*Node {
- return null;
- }
-
- pub fn firstToken(self: *const CharLiteral) TokenIndex {
- return self.token;
- }
-
- pub fn lastToken(self: *const CharLiteral) TokenIndex {
- return self.token;
- }
- };
-
- pub const BoolLiteral = struct {
- base: Node = Node{ .tag = .BoolLiteral },
- token: TokenIndex,
-
- pub fn iterate(self: *const BoolLiteral, index: usize) ?*Node {
- return null;
- }
-
- pub fn firstToken(self: *const BoolLiteral) TokenIndex {
- return self.token;
- }
-
- pub fn lastToken(self: *const BoolLiteral) TokenIndex {
- return self.token;
- }
- };
-
- pub const NullLiteral = struct {
- base: Node = Node{ .tag = .NullLiteral },
- token: TokenIndex,
-
- pub fn iterate(self: *const NullLiteral, index: usize) ?*Node {
- return null;
- }
-
- pub fn firstToken(self: *const NullLiteral) TokenIndex {
- return self.token;
- }
-
- pub fn lastToken(self: *const NullLiteral) TokenIndex {
- return self.token;
- }
- };
-
- pub const UndefinedLiteral = struct {
- base: Node = Node{ .tag = .UndefinedLiteral },
- token: TokenIndex,
-
- pub fn iterate(self: *const UndefinedLiteral, index: usize) ?*Node {
- return null;
- }
-
- pub fn firstToken(self: *const UndefinedLiteral) TokenIndex {
- return self.token;
- }
-
- pub fn lastToken(self: *const UndefinedLiteral) TokenIndex {
- return self.token;
- }
- };
-
pub const Asm = struct {
base: Node = Node{ .tag = .Asm },
asm_token: TokenIndex,
@@ -2904,7 +2823,7 @@ pub const Node = struct {
rparen: TokenIndex,
pub const Kind = union(enum) {
- Variable: *Identifier,
+ Variable: *OneToken,
Return: *Node,
};
@@ -3005,57 +2924,6 @@ pub const Node = struct {
}
};
- pub const Unreachable = struct {
- base: Node = Node{ .tag = .Unreachable },
- token: TokenIndex,
-
- pub fn iterate(self: *const Unreachable, index: usize) ?*Node {
- return null;
- }
-
- pub fn firstToken(self: *const Unreachable) TokenIndex {
- return self.token;
- }
-
- pub fn lastToken(self: *const Unreachable) TokenIndex {
- return self.token;
- }
- };
-
- pub const ErrorType = struct {
- base: Node = Node{ .tag = .ErrorType },
- token: TokenIndex,
-
- pub fn iterate(self: *const ErrorType, index: usize) ?*Node {
- return null;
- }
-
- pub fn firstToken(self: *const ErrorType) TokenIndex {
- return self.token;
- }
-
- pub fn lastToken(self: *const ErrorType) TokenIndex {
- return self.token;
- }
- };
-
- pub const AnyType = struct {
- base: Node = Node{ .tag = .AnyType },
- token: TokenIndex,
-
- pub fn iterate(self: *const AnyType, index: usize) ?*Node {
- return null;
- }
-
- pub fn firstToken(self: *const AnyType) TokenIndex {
- return self.token;
- }
-
- pub fn lastToken(self: *const AnyType) TokenIndex {
- return self.token;
- }
- };
-
/// TODO remove from the Node base struct
/// TODO actually maybe remove entirely in favor of iterating backward from Node.firstToken()
/// and forwards to find same-line doc comments.
lib/std/zig/parse.zig
@@ -628,8 +628,11 @@ const Parser = struct {
var type_expr: ?*Node = null;
if (p.eatToken(.Colon)) |_| {
if (p.eatToken(.Keyword_anytype) orelse p.eatToken(.Keyword_var)) |anytype_tok| {
- const node = try p.arena.allocator.create(Node.AnyType);
- node.* = .{ .token = anytype_tok };
+ const node = try p.arena.allocator.create(Node.OneToken);
+ node.* = .{
+ .base = .{ .tag = .AnyType },
+ .token = anytype_tok,
+ };
type_expr = &node.base;
} else {
type_expr = try p.expectNode(parseTypeExpr, .{
@@ -1079,12 +1082,13 @@ const Parser = struct {
if (p.eatToken(.Keyword_break)) |token| {
const label = try p.parseBreakLabel();
const expr_node = try p.parseExpr();
- const node = try p.arena.allocator.create(Node.ControlFlowExpression);
- node.* = .{
+ const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{
+ .tag = .Break,
.ltoken = token,
- .kind = .{ .Break = label },
+ }, .{
+ .label = label,
.rhs = expr_node,
- };
+ });
return &node.base;
}
@@ -1115,12 +1119,13 @@ const Parser = struct {
if (p.eatToken(.Keyword_continue)) |token| {
const label = try p.parseBreakLabel();
- const node = try p.arena.allocator.create(Node.ControlFlowExpression);
- node.* = .{
+ const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{
+ .tag = .Continue,
.ltoken = token,
- .kind = .{ .Continue = label },
+ }, .{
+ .label = label,
.rhs = null,
- };
+ });
return &node.base;
}
@@ -1139,12 +1144,12 @@ const Parser = struct {
if (p.eatToken(.Keyword_return)) |token| {
const expr_node = try p.parseExpr();
- const node = try p.arena.allocator.create(Node.ControlFlowExpression);
- node.* = .{
+ const node = try Node.ControlFlowExpression.create(&p.arena.allocator, .{
+ .tag = .Return,
.ltoken = token,
- .kind = .Return,
+ }, .{
.rhs = expr_node,
- };
+ });
return &node.base;
}
@@ -1516,8 +1521,9 @@ const Parser = struct {
fn parsePrimaryTypeExpr(p: *Parser) !?*Node {
if (try p.parseBuiltinCall()) |node| return node;
if (p.eatToken(.CharLiteral)) |token| {
- const node = try p.arena.allocator.create(Node.CharLiteral);
+ const node = try p.arena.allocator.create(Node.OneToken);
node.* = .{
+ .base = .{ .tag = .CharLiteral },
.token = token,
};
return &node.base;
@@ -1547,7 +1553,7 @@ const Parser = struct {
const identifier = try p.expectNodeRecoverable(parseIdentifier, .{
.ExpectedIdentifier = .{ .token = p.tok_i },
});
- const global_error_set = try p.createLiteral(Node.ErrorType, token);
+ const global_error_set = try p.createLiteral(.ErrorType, token);
if (period == null or identifier == null) return global_error_set;
const node = try p.arena.allocator.create(Node.SimpleInfixOp);
@@ -1559,8 +1565,8 @@ const Parser = struct {
};
return &node.base;
}
- if (p.eatToken(.Keyword_false)) |token| return p.createLiteral(Node.BoolLiteral, token);
- if (p.eatToken(.Keyword_null)) |token| return p.createLiteral(Node.NullLiteral, token);
+ if (p.eatToken(.Keyword_false)) |token| return p.createLiteral(.BoolLiteral, token);
+ if (p.eatToken(.Keyword_null)) |token| return p.createLiteral(.NullLiteral, token);
if (p.eatToken(.Keyword_anyframe)) |token| {
const node = try p.arena.allocator.create(Node.AnyFrameType);
node.* = .{
@@ -1569,9 +1575,9 @@ const Parser = struct {
};
return &node.base;
}
- if (p.eatToken(.Keyword_true)) |token| return p.createLiteral(Node.BoolLiteral, token);
- if (p.eatToken(.Keyword_undefined)) |token| return p.createLiteral(Node.UndefinedLiteral, token);
- if (p.eatToken(.Keyword_unreachable)) |token| return p.createLiteral(Node.Unreachable, token);
+ if (p.eatToken(.Keyword_true)) |token| return p.createLiteral(.BoolLiteral, token);
+ if (p.eatToken(.Keyword_undefined)) |token| return p.createLiteral(.UndefinedLiteral, token);
+ if (p.eatToken(.Keyword_unreachable)) |token| return p.createLiteral(.Unreachable, token);
if (try p.parseStringLiteral()) |node| return node;
if (try p.parseSwitchExpr()) |node| return node;
@@ -1865,7 +1871,7 @@ const Parser = struct {
const variable = try p.expectNode(parseIdentifier, .{
.ExpectedIdentifier = .{ .token = p.tok_i },
});
- break :blk .{ .Variable = variable.cast(Node.Identifier).? };
+ break :blk .{ .Variable = variable.castTag(.Identifier).? };
};
const rparen = try p.expectToken(.RParen);
@@ -1906,11 +1912,10 @@ const Parser = struct {
}
/// BreakLabel <- COLON IDENTIFIER
- fn parseBreakLabel(p: *Parser) !?*Node {
+ fn parseBreakLabel(p: *Parser) !?TokenIndex {
_ = p.eatToken(.Colon) orelse return null;
- return try p.expectNode(parseIdentifier, .{
- .ExpectedIdentifier = .{ .token = p.tok_i },
- });
+ const ident = try p.expectToken(.Identifier);
+ return ident;
}
/// BlockLabel <- IDENTIFIER COLON
@@ -3022,8 +3027,9 @@ const Parser = struct {
});
// lets pretend this was an identifier so we can continue parsing
- const node = try p.arena.allocator.create(Node.Identifier);
+ const node = try p.arena.allocator.create(Node.OneToken);
node.* = .{
+ .base = .{ .tag = .Identifier },
.token = token,
};
return &node.base;
@@ -3054,8 +3060,9 @@ const Parser = struct {
fn parseIdentifier(p: *Parser) !?*Node {
const token = p.eatToken(.Identifier) orelse return null;
- const node = try p.arena.allocator.create(Node.Identifier);
+ const node = try p.arena.allocator.create(Node.OneToken);
node.* = .{
+ .base = .{ .tag = .Identifier },
.token = token,
};
return &node.base;
@@ -3064,16 +3071,18 @@ const Parser = struct {
fn parseAnyType(p: *Parser) !?*Node {
const token = p.eatToken(.Keyword_anytype) orelse
p.eatToken(.Keyword_var) orelse return null; // TODO remove in next release cycle
- const node = try p.arena.allocator.create(Node.AnyType);
+ const node = try p.arena.allocator.create(Node.OneToken);
node.* = .{
+ .base = .{ .tag = .AnyType },
.token = token,
};
return &node.base;
}
- fn createLiteral(p: *Parser, comptime T: type, token: TokenIndex) !*Node {
- const result = try p.arena.allocator.create(T);
- result.* = T{
+ fn createLiteral(p: *Parser, tag: ast.Node.Tag, token: TokenIndex) !*Node {
+ const result = try p.arena.allocator.create(Node.OneToken);
+ result.* = .{
+ .base = .{ .tag = tag },
.token = token,
};
return &result.base;
@@ -3081,8 +3090,9 @@ const Parser = struct {
fn parseStringLiteralSingle(p: *Parser) !?*Node {
if (p.eatToken(.StringLiteral)) |token| {
- const node = try p.arena.allocator.create(Node.StringLiteral);
+ const node = try p.arena.allocator.create(Node.OneToken);
node.* = .{
+ .base = .{ .tag = .StringLiteral },
.token = token,
};
return &node.base;
@@ -3131,8 +3141,9 @@ const Parser = struct {
fn parseIntegerLiteral(p: *Parser) !?*Node {
const token = p.eatToken(.IntegerLiteral) orelse return null;
- const node = try p.arena.allocator.create(Node.IntegerLiteral);
+ const node = try p.arena.allocator.create(Node.OneToken);
node.* = .{
+ .base = .{ .tag = .IntegerLiteral },
.token = token,
};
return &node.base;
@@ -3140,8 +3151,9 @@ const Parser = struct {
fn parseFloatLiteral(p: *Parser) !?*Node {
const token = p.eatToken(.FloatLiteral) orelse return null;
- const node = try p.arena.allocator.create(Node.FloatLiteral);
+ const node = try p.arena.allocator.create(Node.OneToken);
node.* = .{
+ .base = .{ .tag = .FloatLiteral },
.token = token,
};
return &node.base;
lib/std/zig/render.zig
@@ -366,10 +366,32 @@ fn renderExpression(
space: Space,
) (@TypeOf(stream).Error || Error)!void {
switch (base.tag) {
- .Identifier => {
- const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);
- return renderToken(tree, stream, identifier.token, indent, start_col, space);
+ .Identifier,
+ .IntegerLiteral,
+ .FloatLiteral,
+ .StringLiteral,
+ .CharLiteral,
+ .BoolLiteral,
+ .NullLiteral,
+ .Unreachable,
+ .ErrorType,
+ .UndefinedLiteral,
+ => {
+ const casted_node = base.cast(ast.Node.OneToken).?;
+ return renderToken(tree, stream, casted_node.token, indent, start_col, space);
},
+
+ .AnyType => {
+ const any_type = base.castTag(.AnyType).?;
+ if (mem.eql(u8, tree.tokenSlice(any_type.token), "var")) {
+ // TODO remove in next release cycle
+ try stream.writeAll("anytype");
+ if (space == .Comma) try stream.writeAll(",\n");
+ return;
+ }
+ return renderToken(tree, stream, any_type.token, indent, start_col, space);
+ },
+
.Block => {
const block = @fieldParentPtr(ast.Node.Block, "base", base);
@@ -399,6 +421,7 @@ fn renderExpression(
return renderToken(tree, stream, block.rbrace, indent, start_col, space);
}
},
+
.Defer => {
const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);
@@ -1107,50 +1130,48 @@ fn renderExpression(
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ?
},
- .ControlFlowExpression => {
- const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base);
+ .Break => {
+ const flow_expr = base.castTag(.Break).?;
+ const maybe_rhs = flow_expr.getRHS();
+ const maybe_label = flow_expr.getLabel();
- switch (flow_expr.kind) {
- .Break => |maybe_label| {
- if (maybe_label == null and flow_expr.rhs == null) {
- return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); // break
- }
-
- try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); // break
- if (maybe_label) |label| {
- const colon = tree.nextToken(flow_expr.ltoken);
- try renderToken(tree, stream, colon, indent, start_col, Space.None); // :
-
- if (flow_expr.rhs == null) {
- return renderExpression(allocator, stream, tree, indent, start_col, label, space); // label
- }
- try renderExpression(allocator, stream, tree, indent, start_col, label, Space.Space); // label
- }
- },
- .Continue => |maybe_label| {
- assert(flow_expr.rhs == null);
+ if (maybe_label == null and maybe_rhs == null) {
+ return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); // break
+ }
- if (maybe_label == null and flow_expr.rhs == null) {
- return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); // continue
- }
+ try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); // break
+ if (maybe_label) |label| {
+ const colon = tree.nextToken(flow_expr.ltoken);
+ try renderToken(tree, stream, colon, indent, start_col, Space.None); // :
- try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); // continue
- if (maybe_label) |label| {
- const colon = tree.nextToken(flow_expr.ltoken);
- try renderToken(tree, stream, colon, indent, start_col, Space.None); // :
+ if (maybe_rhs == null) {
+ return renderToken(tree, stream, label, indent, start_col, space); // label
+ }
+ try renderToken(tree, stream, label, indent, start_col, Space.Space); // label
+ }
+ return renderExpression(allocator, stream, tree, indent, start_col, maybe_rhs.?, space);
+ },
- return renderExpression(allocator, stream, tree, indent, start_col, label, space);
- }
- },
- .Return => {
- if (flow_expr.rhs == null) {
- return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space);
- }
- try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space);
- },
+ .Continue => {
+ const flow_expr = base.castTag(.Continue).?;
+ if (flow_expr.getLabel()) |label| {
+ try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space); // continue
+ const colon = tree.nextToken(flow_expr.ltoken);
+ try renderToken(tree, stream, colon, indent, start_col, Space.None); // :
+ return renderToken(tree, stream, label, indent, start_col, space); // label
+ } else {
+ return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space); // continue
}
+ },
- return renderExpression(allocator, stream, tree, indent, start_col, flow_expr.rhs.?, space);
+ .Return => {
+ const flow_expr = base.castTag(.Return).?;
+ if (flow_expr.getRHS()) |rhs| {
+ try renderToken(tree, stream, flow_expr.ltoken, indent, start_col, Space.Space);
+ return renderExpression(allocator, stream, tree, indent, start_col, rhs, space);
+ } else {
+ return renderToken(tree, stream, flow_expr.ltoken, indent, start_col, space);
+ }
},
.Payload => {
@@ -1208,48 +1229,6 @@ fn renderExpression(
return renderExpression(allocator, stream, tree, indent, start_col, field_init.expr, space);
},
- .IntegerLiteral => {
- const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base);
- return renderToken(tree, stream, integer_literal.token, indent, start_col, space);
- },
- .FloatLiteral => {
- const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base);
- return renderToken(tree, stream, float_literal.token, indent, start_col, space);
- },
- .StringLiteral => {
- const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base);
- return renderToken(tree, stream, string_literal.token, indent, start_col, space);
- },
- .CharLiteral => {
- const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
- return renderToken(tree, stream, char_literal.token, indent, start_col, space);
- },
- .BoolLiteral => {
- const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
- return renderToken(tree, stream, bool_literal.token, indent, start_col, space);
- },
- .NullLiteral => {
- const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base);
- return renderToken(tree, stream, null_literal.token, indent, start_col, space);
- },
- .Unreachable => {
- const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base);
- return renderToken(tree, stream, unreachable_node.token, indent, start_col, space);
- },
- .ErrorType => {
- const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base);
- return renderToken(tree, stream, error_type.token, indent, start_col, space);
- },
- .AnyType => {
- const any_type = @fieldParentPtr(ast.Node.AnyType, "base", base);
- if (mem.eql(u8, tree.tokenSlice(any_type.token), "var")) {
- // TODO remove in next release cycle
- try stream.writeAll("anytype");
- if (space == .Comma) try stream.writeAll(",\n");
- return;
- }
- return renderToken(tree, stream, any_type.token, indent, start_col, space);
- },
.ContainerDecl => {
const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
@@ -1468,10 +1447,6 @@ fn renderExpression(
}
try stream.writeByteNTimes(' ', indent);
},
- .UndefinedLiteral => {
- const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base);
- return renderToken(tree, stream, undefined_literal.token, indent, start_col, space);
- },
.BuiltinCall => {
const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base);
lib/std/target.zig
@@ -436,6 +436,8 @@ pub const Target = struct {
macho,
wasm,
c,
+ hex,
+ raw,
};
pub const SubSystem = enum {
src-self-hosted/codegen/c.zig
@@ -90,7 +90,7 @@ fn genFn(file: *C, decl: *Decl) !void {
const instructions = func.analysis.success.instructions;
if (instructions.len > 0) {
for (instructions) |inst| {
- try writer.writeAll("\n\t");
+ try writer.writeAll("\n ");
switch (inst.tag) {
.assembly => try genAsm(file, inst.castTag(.assembly).?, decl),
.call => try genCall(file, inst.castTag(.call).?, decl),
@@ -106,21 +106,7 @@ fn genFn(file: *C, decl: *Decl) !void {
}
fn genRet(file: *C, inst: *Inst.UnOp, decl: *Decl, expected_return_type: Type) !void {
- const writer = file.main.writer();
- const ret_value = inst.operand;
- const value = ret_value.value().?;
- if (expected_return_type.eql(ret_value.ty))
- return file.fail(decl.src(), "TODO return {}", .{expected_return_type})
- else if (expected_return_type.isInt() and ret_value.ty.tag() == .comptime_int)
- if (value.intFitsInType(expected_return_type, file.options.target))
- if (expected_return_type.intInfo(file.options.target).bits <= 64)
- try writer.print("return {};", .{value.toUnsignedInt()})
- else
- return file.fail(decl.src(), "TODO return ints > 64 bits", .{})
- else
- return file.fail(decl.src(), "comptime int {} does not fit in {}", .{ value.toUnsignedInt(), expected_return_type })
- else
- return file.fail(decl.src(), "return type mismatch: expected {}, found {}", .{ expected_return_type, ret_value.ty });
+ return file.fail(decl.src(), "TODO return {}", .{expected_return_type});
}
fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
@@ -162,7 +148,7 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
if (c.val.tag() == .int_u64) {
try writer.writeAll("register ");
try renderType(file, writer, arg.ty, decl.src());
- try writer.print(" {}_constant __asm__(\"{}\") = {};\n\t", .{ reg, reg, c.val.toUnsignedInt() });
+ try writer.print(" {}_constant __asm__(\"{}\") = {};\n ", .{ reg, reg, c.val.toUnsignedInt() });
} else {
return file.fail(decl.src(), "TODO inline asm {} args", .{c.val.tag()});
}
src-self-hosted/astgen.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const mem = std.mem;
+const Allocator = std.mem.Allocator;
const Value = @import("value.zig").Value;
const Type = @import("type.zig").Type;
const TypedValue = @import("TypedValue.zig");
@@ -11,35 +12,67 @@ const trace = @import("tracy.zig").trace;
const Scope = Module.Scope;
const InnerError = Module.InnerError;
+pub const ResultLoc = union(enum) {
+ /// The expression is the right-hand side of assignment to `_`.
+ discard,
+ /// The expression has an inferred type, and it will be evaluated as an rvalue.
+ none,
+ /// The expression will be type coerced into this type, but it will be evaluated as an rvalue.
+ ty: *zir.Inst,
+ /// The expression must store its result into this typed pointer.
+ ptr: *zir.Inst,
+ /// The expression must store its result into this allocation, which has an inferred type.
+ inferred_ptr: *zir.Inst.Tag.alloc_inferred.Type(),
+ /// The expression must store its result into this pointer, which is a typed pointer that
+ /// has been bitcasted to whatever the expression's type is.
+ bitcasted_ptr: *zir.Inst.UnOp,
+ /// There is a pointer for the expression to store its result into, however, its type
+ /// is inferred based on peer type resolution for a `zir.Inst.Block`.
+ block_ptr: *zir.Inst.Block,
+};
+
+pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst {
+ const type_src = scope.tree().token_locs[type_node.firstToken()].start;
+ const type_type = try mod.addZIRInstConst(scope, type_src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.type_type),
+ });
+ const type_rl: ResultLoc = .{ .ty = type_type };
+ return expr(mod, scope, type_rl, type_node);
+}
+
/// Turn Zig AST into untyped ZIR istructions.
-pub fn expr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
+pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
switch (node.tag) {
.VarDecl => unreachable, // Handled in `blockExpr`.
-
- .Add => return simpleInfixOp(mod, scope, node.castTag(.Add).?, .add),
- .Sub => return simpleInfixOp(mod, scope, node.castTag(.Sub).?, .sub),
- .BangEqual => return simpleInfixOp(mod, scope, node.castTag(.BangEqual).?, .cmp_neq),
- .EqualEqual => return simpleInfixOp(mod, scope, node.castTag(.EqualEqual).?, .cmp_eq),
- .GreaterThan => return simpleInfixOp(mod, scope, node.castTag(.GreaterThan).?, .cmp_gt),
- .GreaterOrEqual => return simpleInfixOp(mod, scope, node.castTag(.GreaterOrEqual).?, .cmp_gte),
- .LessThan => return simpleInfixOp(mod, scope, node.castTag(.LessThan).?, .cmp_lt),
- .LessOrEqual => return simpleInfixOp(mod, scope, node.castTag(.LessOrEqual).?, .cmp_lte),
-
- .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).?),
+ .Assign => unreachable, // Handled in `blockExpr`.
+
+ .Add => return arithmetic(mod, scope, rl, node.castTag(.Add).?, .add),
+ .Sub => return arithmetic(mod, scope, rl, node.castTag(.Sub).?, .sub),
+
+ .BangEqual => return cmp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq),
+ .EqualEqual => return cmp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq),
+ .GreaterThan => return cmp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt),
+ .GreaterOrEqual => return cmp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte),
+ .LessThan => return cmp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt),
+ .LessOrEqual => return cmp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte),
+
+ .Identifier => return rlWrap(mod, scope, rl, try identifier(mod, scope, node.castTag(.Identifier).?)),
+ .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),
+ .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),
+ .IntegerLiteral => return rlWrap(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)),
+ .BuiltinCall => return builtinCall(mod, scope, rl, node.castTag(.BuiltinCall).?),
+ .Call => return callExpr(mod, scope, rl, 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).?),
- .Period => return field(mod, scope, node.castTag(.Period).?),
- .Deref => return deref(mod, scope, node.castTag(.Deref).?),
- .BoolNot => return boolNot(mod, scope, node.castTag(.BoolNot).?),
- .FloatLiteral => return floatLiteral(mod, scope, node.castTag(.FloatLiteral).?),
- .UndefinedLiteral, .BoolLiteral, .NullLiteral => return primitiveLiteral(mod, scope, node),
+ .Return => return ret(mod, scope, node.castTag(.Return).?),
+ .If => return ifExpr(mod, scope, rl, node.castTag(.If).?),
+ .Period => return rlWrap(mod, scope, rl, try field(mod, scope, node.castTag(.Period).?)),
+ .Deref => return rlWrap(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)),
+ .BoolNot => return rlWrap(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)),
+ .FloatLiteral => return rlWrap(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)),
+ .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),
+ .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)),
+ .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)),
else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}),
}
}
@@ -59,17 +92,28 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
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;
+ const var_decl_node = statement.castTag(.VarDecl).?;
+ scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator);
+ },
+ .Assign => {
+ const ass = statement.castTag(.Assign).?;
+ try assign(mod, scope, ass);
+ },
+ else => {
+ const possibly_unused_result = try expr(mod, scope, .none, statement);
+ const src = scope.tree().token_locs[statement.firstToken()].start;
+ _ = try mod.addZIRUnOp(scope, src, .ensure_result_used, possibly_unused_result);
},
- else => _ = try expr(mod, scope, statement),
}
}
}
-fn varDecl(mod: *Module, scope: *Scope, node: *ast.Node.VarDecl) InnerError!Scope.LocalVar {
+fn varDecl(
+ mod: *Module,
+ scope: *Scope,
+ node: *ast.Node.VarDecl,
+ block_arena: *Allocator,
+) InnerError!*Scope {
// TODO implement detection of shadowing
if (node.getTrailer("comptime_token")) |comptime_token| {
return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
@@ -78,48 +122,96 @@ fn varDecl(mod: *Module, scope: *Scope, node: *ast.Node.VarDecl) InnerError!Scop
return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{});
}
const tree = scope.tree();
+ const name_src = tree.token_locs[node.name_token].start;
+ const ident_name = try identifierTokenString(mod, scope, node.name_token);
+ const init_node = node.getTrailer("init_node").?;
switch (tree.token_ids[node.mut_token]) {
.Keyword_const => {
- if (node.getTrailer("type_node")) |type_node| {
- return mod.failNode(scope, type_node, "TODO implement typed const locals", .{});
- }
// 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 (nodeMayNeedMemoryLocation(init_node)) {
- return mod.failNode(scope, init_node, "TODO implement result locations", .{});
+ if (node.getTrailer("type_node")) |type_node| {
+ const type_inst = try typeExpr(mod, scope, type_node);
+ const alloc = try mod.addZIRUnOp(scope, name_src, .alloc, type_inst);
+ const result_loc: ResultLoc = .{ .ptr = alloc };
+ const init_inst = try expr(mod, scope, result_loc, init_node);
+ const sub_scope = try block_arena.create(Scope.LocalVal);
+ sub_scope.* = .{
+ .parent = scope,
+ .gen_zir = scope.getGenZIR(),
+ .name = ident_name,
+ .inst = init_inst,
+ };
+ return &sub_scope.base;
+ } else {
+ const alloc = try mod.addZIRNoOpT(scope, name_src, .alloc_inferred);
+ const result_loc: ResultLoc = .{ .inferred_ptr = alloc };
+ const init_inst = try expr(mod, scope, result_loc, init_node);
+ const sub_scope = try block_arena.create(Scope.LocalVal);
+ sub_scope.* = .{
+ .parent = scope,
+ .gen_zir = scope.getGenZIR(),
+ .name = ident_name,
+ .inst = init_inst,
+ };
+ return &sub_scope.base;
+ }
+ } else {
+ const result_loc: ResultLoc = if (node.getTrailer("type_node")) |type_node|
+ .{ .ty = try typeExpr(mod, scope, type_node) }
+ else
+ .none;
+ const init_inst = try expr(mod, scope, result_loc, init_node);
+ const sub_scope = try block_arena.create(Scope.LocalVal);
+ sub_scope.* = .{
+ .parent = scope,
+ .gen_zir = scope.getGenZIR(),
+ .name = ident_name,
+ .inst = init_inst,
+ };
+ return &sub_scope.base;
}
- const init_inst = try expr(mod, scope, init_node);
- const ident_name = try identifierTokenString(mod, scope, node.name_token);
- return Scope.LocalVar{
- .parent = scope,
- .gen_zir = scope.getGenZIR(),
- .name = ident_name,
- .inst = init_inst,
- };
},
.Keyword_var => {
- return mod.failNode(scope, &node.base, "TODO implement local vars", .{});
+ if (node.getTrailer("type_node")) |type_node| {
+ const type_inst = try typeExpr(mod, scope, type_node);
+ const alloc = try mod.addZIRUnOp(scope, name_src, .alloc, type_inst);
+ const result_loc: ResultLoc = .{ .ptr = alloc };
+ const init_inst = try expr(mod, scope, result_loc, init_node);
+ const sub_scope = try block_arena.create(Scope.LocalPtr);
+ sub_scope.* = .{
+ .parent = scope,
+ .gen_zir = scope.getGenZIR(),
+ .name = ident_name,
+ .ptr = alloc,
+ };
+ return &sub_scope.base;
+ } else {
+ const alloc = try mod.addZIRNoOp(scope, name_src, .alloc_inferred);
+ const result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? };
+ const init_inst = try expr(mod, scope, result_loc, init_node);
+ const sub_scope = try block_arena.create(Scope.LocalPtr);
+ sub_scope.* = .{
+ .parent = scope,
+ .gen_zir = scope.getGenZIR(),
+ .name = ident_name,
+ .ptr = alloc,
+ };
+ return &sub_scope.base;
+ }
},
else => unreachable,
}
}
-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.addZIRUnOp(scope, src, .boolnot, 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);
+fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!void {
+ if (infix_node.lhs.castTag(.Identifier)) |ident| {
const tree = scope.tree();
const ident_name = try identifierTokenString(mod, scope, ident.token);
if (std.mem.eql(u8, ident_name, "_")) {
- return expr(mod, scope, infix_node.rhs);
+ _ = try expr(mod, scope, .discard, infix_node.rhs);
+ return;
} else {
return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
}
@@ -128,6 +220,17 @@ fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) Inne
}
}
+fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[node.op_token].start;
+ const bool_type = try mod.addZIRInstConst(scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.bool_type),
+ });
+ const operand = try expr(mod, scope, .{ .ty = bool_type }, node.rhs);
+ return mod.addZIRUnOp(scope, src, .boolnot, operand);
+}
+
/// Identifier token -> String (allocated in scope.arena())
pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
const tree = scope.tree();
@@ -148,7 +251,7 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex)
return ident_name;
}
-pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.Identifier) InnerError!*zir.Inst {
+pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
const tree = scope.tree();
const src = tree.token_locs[node.token].start;
@@ -158,10 +261,11 @@ pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.Identif
}
fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
+ // TODO introduce lvalues
const tree = scope.tree();
const src = tree.token_locs[node.op_token].start;
- const lhs = try expr(mod, scope, node.lhs);
+ const lhs = try expr(mod, scope, .none, node.lhs);
const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);
const pointer = try mod.addZIRInst(scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{});
@@ -171,26 +275,44 @@ fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!
fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
const tree = scope.tree();
const src = tree.token_locs[node.rtoken].start;
- const lhs = try expr(mod, scope, node.lhs);
+ const lhs = try expr(mod, scope, .none, node.lhs);
return mod.addZIRUnOp(scope, src, .deref, lhs);
}
-fn simpleInfixOp(
+fn cmp(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ infix_node: *ast.Node.SimpleInfixOp,
+ cmp_inst_tag: zir.Inst.Tag,
+) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const src = tree.token_locs[infix_node.op_token].start;
+
+ const lhs = try expr(mod, scope, .none, infix_node.lhs);
+ const rhs = try expr(mod, scope, .none, infix_node.rhs);
+ const result = try mod.addZIRBinOp(scope, src, cmp_inst_tag, lhs, rhs);
+ return rlWrap(mod, scope, rl, result);
+}
+
+fn arithmetic(
mod: *Module,
scope: *Scope,
+ rl: ResultLoc,
infix_node: *ast.Node.SimpleInfixOp,
op_inst_tag: zir.Inst.Tag,
) InnerError!*zir.Inst {
- const lhs = try expr(mod, scope, infix_node.lhs);
- const rhs = try expr(mod, scope, infix_node.rhs);
+ const lhs = try expr(mod, scope, .none, infix_node.lhs);
+ const rhs = try expr(mod, scope, .none, infix_node.rhs);
const tree = scope.tree();
const src = tree.token_locs[infix_node.op_token].start;
- return mod.addZIRBinOp(scope, src, op_inst_tag, lhs, rhs);
+ const result = try mod.addZIRBinOp(scope, src, op_inst_tag, lhs, rhs);
+ return rlWrap(mod, scope, rl, result);
}
-fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.Inst {
+fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst {
if (if_node.payload) |payload| {
return mod.failNode(scope, payload, "TODO implement astgen.IfExpr for optionals", .{});
}
@@ -207,10 +329,14 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
};
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 bool_type = try mod.addZIRInstConst(scope, if_src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.bool_type),
+ });
+ const cond = try expr(mod, &block_scope.base, .{ .ty = bool_type }, if_node.condition);
+
const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{
.condition = cond,
.then_body = undefined, // populated below
@@ -228,7 +354,16 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
};
defer then_scope.instructions.deinit(mod.gpa);
- const then_result = try expr(mod, &then_scope.base, if_node.body);
+ // Most result location types can be forwarded directly; however
+ // if we need to write to a pointer which has an inferred type,
+ // proper type inference requires peer type resolution on the if's
+ // branches.
+ const branch_rl: ResultLoc = switch (rl) {
+ .discard, .none, .ty, .ptr => rl,
+ .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
+ };
+
+ const then_result = try expr(mod, &then_scope.base, branch_rl, 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, .{
@@ -249,7 +384,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
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);
+ const else_result = try expr(mod, &else_scope.base, branch_rl, 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, .{
@@ -272,27 +407,25 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In
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 => {},
- }
+fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
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.addZIRUnOp(scope, src, .@"return", operand);
+ if (cfe.getRHS()) |rhs_node| {
+ if (nodeMayNeedMemoryLocation(rhs_node)) {
+ const ret_ptr = try mod.addZIRNoOp(scope, src, .ret_ptr);
+ const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node);
+ return mod.addZIRUnOp(scope, src, .@"return", operand);
+ } else {
+ const fn_ret_ty = try mod.addZIRNoOp(scope, src, .ret_type);
+ const operand = try expr(mod, scope, .{ .ty = fn_ret_ty }, rhs_node);
+ return mod.addZIRUnOp(scope, src, .@"return", operand);
+ }
} else {
return mod.addZIRNoOp(scope, src, .returnvoid);
}
}
-fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerError!*zir.Inst {
+fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError!*zir.Inst {
const tracy = trace(@src());
defer tracy.end();
@@ -345,12 +478,19 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerErr
{
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;
+ .local_val => {
+ const local_val = s.cast(Scope.LocalVal).?;
+ if (mem.eql(u8, local_val.name, ident_name)) {
+ return local_val.inst;
+ }
+ s = local_val.parent;
+ },
+ .local_ptr => {
+ const local_ptr = s.cast(Scope.LocalPtr).?;
+ if (mem.eql(u8, local_ptr.name, ident_name)) {
+ return try mod.addZIRUnOp(scope, src, .deref, local_ptr.ptr);
}
- s = local_var.parent;
+ s = local_ptr.parent;
},
.gen_zir => s = s.cast(Scope.GenZIR).?.parent,
else => break,
@@ -364,7 +504,7 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerErr
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 {
+fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
const tree = scope.tree();
const unparsed_bytes = tree.tokenSlice(str_lit.token);
const arena = scope.arena();
@@ -383,7 +523,7 @@ fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.StringLiteral)
return mod.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
}
-fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral) InnerError!*zir.Inst {
+fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
const arena = scope.arena();
const tree = scope.tree();
const prefixed_bytes = tree.tokenSlice(int_lit.token);
@@ -414,7 +554,7 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.IntegerLiteral
}
}
-fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.FloatLiteral) InnerError!*zir.Inst {
+fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
const arena = scope.arena();
const tree = scope.tree();
const bytes = tree.tokenSlice(float_lit.token);
@@ -434,30 +574,38 @@ fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.FloatLiteral)
});
}
-fn primitiveLiteral(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
+fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
const arena = scope.arena();
const tree = scope.tree();
- const src = tree.token_locs[node.firstToken()].start;
+ const src = tree.token_locs[node.token].start;
+ return mod.addZIRInstConst(scope, src, .{
+ .ty = Type.initTag(.@"undefined"),
+ .val = Value.initTag(.undef),
+ });
+}
- if (node.cast(ast.Node.BoolLiteral)) |bool_node| {
- return mod.addZIRInstConst(scope, src, .{
- .ty = Type.initTag(.bool),
- .val = if (tree.token_ids[bool_node.token] == .Keyword_true)
- Value.initTag(.bool_true)
- else
- Value.initTag(.bool_false),
- });
- } else if (node.tag == .UndefinedLiteral) {
- return mod.addZIRInstConst(scope, src, .{
- .ty = Type.initTag(.@"undefined"),
- .val = Value.initTag(.undef),
- });
- } else if (node.tag == .NullLiteral) {
- return mod.addZIRInstConst(scope, src, .{
- .ty = Type.initTag(.@"null"),
- .val = Value.initTag(.null_value),
- });
- } else unreachable;
+fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const arena = scope.arena();
+ const tree = scope.tree();
+ const src = tree.token_locs[node.token].start;
+ return mod.addZIRInstConst(scope, src, .{
+ .ty = Type.initTag(.bool),
+ .val = switch (tree.token_ids[node.token]) {
+ .Keyword_true => Value.initTag(.bool_true),
+ .Keyword_false => Value.initTag(.bool_false),
+ else => unreachable,
+ },
+ });
+}
+
+fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
+ const arena = scope.arena();
+ const tree = scope.tree();
+ const src = tree.token_locs[node.token].start;
+ return mod.addZIRInstConst(scope, src, .{
+ .ty = Type.initTag(.@"null"),
+ .val = Value.initTag(.null_value),
+ });
}
fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
@@ -470,19 +618,26 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi
const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len);
const args = try arena.alloc(*zir.Inst, asm_node.inputs.len);
+ const src = tree.token_locs[asm_node.asm_token].start;
+
+ const str_type = try mod.addZIRInstConst(scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.const_slice_u8_type),
+ });
+ const str_type_rl: ResultLoc = .{ .ty = str_type };
+
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);
+ inputs[i] = try expr(mod, scope, str_type_rl, input.constraint);
+ args[i] = try expr(mod, scope, .none, 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),
+ .asm_source = try expr(mod, scope, str_type_rl, asm_node.template),
.return_type = return_type,
}, .{
.@"volatile" = asm_node.volatile_token != null,
@@ -493,63 +648,174 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi
return asm_inst;
}
-fn builtinCall(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
+fn ensureBuiltinParamCount(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall, count: u32) !void {
+ if (call.params_len == count)
+ return;
+
+ const s = if (count == 1) "" else "s";
+ return mod.failTok(scope, call.builtin_token, "expected {} parameter{}, found {}", .{ count, s, call.params_len });
+}
+
+fn simpleCast(
+ mod: *Module,
+ scope: *Scope,
+ rl: ResultLoc,
+ call: *ast.Node.BuiltinCall,
+ inst_tag: zir.Inst.Tag,
+) InnerError!*zir.Inst {
+ try ensureBuiltinParamCount(mod, scope, call, 2);
const tree = scope.tree();
- const builtin_name = tree.tokenSlice(call.builtin_token);
const src = tree.token_locs[call.builtin_token].start;
+ const type_type = try mod.addZIRInstConst(scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.type_type),
+ });
+ const params = call.params();
+ const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]);
+ const rhs = try expr(mod, scope, .none, params[1]);
+ const result = try mod.addZIRBinOp(scope, src, inst_tag, dest_type, rhs);
+ return rlWrap(mod, scope, rl, result);
+}
- 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});
- }
+fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
+ try ensureBuiltinParamCount(mod, scope, call, 1);
+ const operand = try expr(mod, scope, .none, call.params()[0]);
+ const tree = scope.tree();
+ const src = tree.token_locs[call.builtin_token].start;
+ return mod.addZIRUnOp(scope, src, .ptrtoint, operand);
+}
- return mod.addZIRInst(scope, src, T, value.positionals, .{});
- }
+fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
+ try ensureBuiltinParamCount(mod, scope, call, 2);
+ const tree = scope.tree();
+ const src = tree.token_locs[call.builtin_token].start;
+ const params = call.params();
+ const dest_type = try typeExpr(mod, scope, params[0]);
+ switch (rl) {
+ .none => return try expr(mod, scope, .{ .ty = dest_type }, params[1]),
+ .discard => {
+ const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
+ _ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result);
+ return result;
+ },
+ .ty => |result_ty| {
+ const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
+ return mod.addZIRBinOp(scope, src, .as, result_ty, result);
+ },
+ .ptr => |result_ptr| {
+ const casted_result_ptr = try mod.addZIRBinOp(scope, src, .coerce_result_ptr, dest_type, result_ptr);
+ return expr(mod, scope, .{ .ptr = casted_result_ptr }, params[1]);
+ },
+ .bitcasted_ptr => |bitcasted_ptr| {
+ // TODO here we should be able to resolve the inference; we now have a type for the result.
+ return mod.failTok(scope, call.builtin_token, "TODO implement @as with result location @bitCast", .{});
+ },
+ .inferred_ptr => |result_alloc| {
+ // TODO here we should be able to resolve the inference; we now have a type for the result.
+ return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
+ },
+ .block_ptr => |block_ptr| {
+ const casted_block_ptr = try mod.addZIRInst(scope, src, zir.Inst.CoerceResultBlockPtr, .{
+ .dest_type = dest_type,
+ .block = block_ptr,
+ }, .{});
+ return expr(mod, scope, .{ .ptr = casted_block_ptr }, params[1]);
+ },
+ }
+}
+
+fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
+ try ensureBuiltinParamCount(mod, scope, call, 2);
+ const tree = scope.tree();
+ const src = tree.token_locs[call.builtin_token].start;
+ const type_type = try mod.addZIRInstConst(scope, src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.type_type),
+ });
+ const params = call.params();
+ const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]);
+ switch (rl) {
+ .none => {
+ const operand = try expr(mod, scope, .none, params[1]);
+ return mod.addZIRBinOp(scope, src, .bitcast, dest_type, operand);
+ },
+ .discard => {
+ const operand = try expr(mod, scope, .none, params[1]);
+ const result = try mod.addZIRBinOp(scope, src, .bitcast, dest_type, operand);
+ _ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result);
+ return result;
+ },
+ .ty => |result_ty| {
+ const result = try expr(mod, scope, .none, params[1]);
+ const bitcasted = try mod.addZIRBinOp(scope, src, .bitcast, dest_type, result);
+ return mod.addZIRBinOp(scope, src, .as, result_ty, bitcasted);
+ },
+ .ptr => |result_ptr| {
+ const casted_result_ptr = try mod.addZIRUnOp(scope, src, .bitcast_result_ptr, result_ptr);
+ return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]);
+ },
+ .bitcasted_ptr => |bitcasted_ptr| {
+ return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location another @bitCast", .{});
+ },
+ .block_ptr => |block_ptr| {
+ return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location inferred peer types", .{});
+ },
+ .inferred_ptr => |result_alloc| {
+ // TODO here we should be able to resolve the inference; we now have a type for the result.
+ return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{});
+ },
}
- 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 {
+fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
const tree = scope.tree();
- const lhs = try expr(mod, scope, node.lhs);
+ const builtin_name = tree.tokenSlice(call.builtin_token);
+
+ // We handle the different builtins manually because they have different semantics depending
+ // on the function. For example, `@as` and others participate in result location semantics,
+ // and `@cImport` creates a special scope that collects a .c source code text buffer.
+ // Also, some builtins have a variable number of parameters.
+
+ if (mem.eql(u8, builtin_name, "@ptrToInt")) {
+ return rlWrap(mod, scope, rl, try ptrToInt(mod, scope, call));
+ } else if (mem.eql(u8, builtin_name, "@as")) {
+ return as(mod, scope, rl, call);
+ } else if (mem.eql(u8, builtin_name, "@floatCast")) {
+ return simpleCast(mod, scope, rl, call, .floatcast);
+ } else if (mem.eql(u8, builtin_name, "@intCast")) {
+ return simpleCast(mod, scope, rl, call, .intcast);
+ } else if (mem.eql(u8, builtin_name, "@bitCast")) {
+ return bitCast(mod, scope, rl, call);
+ } else {
+ return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name});
+ }
+}
+
+fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) InnerError!*zir.Inst {
+ const tree = scope.tree();
+ const lhs = try expr(mod, scope, .none, 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 param_src = tree.token_locs[param_node.firstToken()].start;
+ const param_type = try mod.addZIRInst(scope, param_src, zir.Inst.ParamType, .{
+ .func = lhs,
+ .arg_index = i,
+ }, .{});
+ args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node);
}
const src = tree.token_locs[node.lhs.firstToken()].start;
- return mod.addZIRInst(scope, src, zir.Inst.Call, .{
+ const result = try mod.addZIRInst(scope, src, zir.Inst.Call, .{
.func = lhs,
.args = args,
}, .{});
+ // TODO function call with result location
+ return rlWrap(mod, scope, rl, result);
}
-fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.Unreachable) InnerError!*zir.Inst {
+fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst {
const tree = scope.tree();
const src = tree.token_locs[unreach_node.token].start;
return mod.addZIRNoOp(scope, src, .@"unreachable");
@@ -611,7 +877,9 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool {
.FieldInitializer,
=> unreachable,
- .ControlFlowExpression,
+ .Return,
+ .Break,
+ .Continue,
.BitNot,
.BoolNot,
.VarDecl,
@@ -722,3 +990,39 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool {
}
}
}
+
+/// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of
+/// result locations must call this function on their result.
+/// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.
+/// If the `ResultLoc` is `ty`, it will coerce the result to the type.
+fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerError!*zir.Inst {
+ switch (rl) {
+ .none => return result,
+ .discard => {
+ // Emit a compile error for discarding error values.
+ _ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result);
+ return result;
+ },
+ .ty => |ty_inst| return mod.addZIRBinOp(scope, result.src, .as, ty_inst, result),
+ .ptr => |ptr_inst| {
+ const casted_result = try mod.addZIRInst(scope, result.src, zir.Inst.CoerceToPtrElem, .{
+ .ptr = ptr_inst,
+ .value = result,
+ }, .{});
+ _ = try mod.addZIRInst(scope, result.src, zir.Inst.Store, .{
+ .ptr = ptr_inst,
+ .value = casted_result,
+ }, .{});
+ return casted_result;
+ },
+ .bitcasted_ptr => |bitcasted_ptr| {
+ return mod.fail(scope, result.src, "TODO implement rlWrap .bitcasted_ptr", .{});
+ },
+ .inferred_ptr => |alloc| {
+ return mod.fail(scope, result.src, "TODO implement rlWrap .inferred_ptr", .{});
+ },
+ .block_ptr => |block_ptr| {
+ return mod.fail(scope, result.src, "TODO implement rlWrap .block_ptr", .{});
+ },
+ }
+}
src-self-hosted/link.zig
@@ -1579,6 +1579,8 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !Fi
.elf => {},
.macho => return error.TODOImplementWritingMachO,
.wasm => return error.TODOImplementWritingWasmObjects,
+ .hex => return error.TODOImplementWritingHex,
+ .raw => return error.TODOImplementWritingRaw,
}
var self: File.Elf = .{
@@ -1638,6 +1640,8 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Fil
.elf => {},
.macho => return error.IncrFailed,
.wasm => return error.IncrFailed,
+ .hex => return error.IncrFailed,
+ .raw => return error.IncrFailed,
}
var self: File.Elf = .{
.allocator = allocator,
src-self-hosted/main.zig
@@ -141,11 +141,19 @@ const usage_build_generic =
\\ --name [name] Override output name
\\ --mode [mode] Set the build mode
\\ Debug (default) optimizations off, safety on
- \\ ReleaseFast optimizations on, safety off
- \\ ReleaseSafe optimizations on, safety on
- \\ ReleaseSmall optimize for small binary, safety off
+ \\ ReleaseFast Optimizations on, safety off
+ \\ ReleaseSafe Optimizations on, safety on
+ \\ ReleaseSmall Optimize for small binary, safety off
\\ --dynamic Force output to be dynamically linked
\\ --strip Exclude debug symbols
+ \\ -ofmt=[mode] Override target object format
+ \\ elf Executable and Linking Format
+ \\ c Compile to C source code
+ \\ coff (planned) Common Object File Format (Windows)
+ \\ pe (planned) Portable Executable (Windows)
+ \\ macho (planned) macOS relocatables
+ \\ hex (planned) Intel IHEX
+ \\ raw (planned) Dump machine code directly
\\
\\Link Options:
\\ -l[lib], --library [lib] Link against system library
@@ -195,7 +203,7 @@ fn buildOutputType(
var target_arch_os_abi: []const u8 = "native";
var target_mcpu: ?[]const u8 = null;
var target_dynamic_linker: ?[]const u8 = null;
- var object_format: ?std.builtin.ObjectFormat = null;
+ var target_ofmt: ?[]const u8 = null;
var system_libs = std.ArrayList([]const u8).init(gpa);
defer system_libs.deinit();
@@ -282,12 +290,8 @@ fn buildOutputType(
}
i += 1;
target_mcpu = args[i];
- } else if (mem.eql(u8, arg, "--c")) {
- if (object_format) |old| {
- std.debug.print("attempted to override object format {} with C\n", .{old});
- process.exit(1);
- }
- object_format = .c;
+ } else if (mem.startsWith(u8, arg, "-ofmt=")) {
+ target_ofmt = arg["-ofmt=".len..];
} else if (mem.startsWith(u8, arg, "-mcpu=")) {
target_mcpu = arg["-mcpu=".len..];
} else if (mem.eql(u8, arg, "--dynamic-linker")) {
@@ -434,6 +438,30 @@ fn buildOutputType(
process.exit(1);
};
+ const object_format: ?std.Target.ObjectFormat = blk: {
+ const ofmt = target_ofmt orelse break :blk null;
+ if (mem.eql(u8, ofmt, "elf")) {
+ break :blk .elf;
+ } else if (mem.eql(u8, ofmt, "c")) {
+ break :blk .c;
+ } else if (mem.eql(u8, ofmt, "coff")) {
+ break :blk .coff;
+ } else if (mem.eql(u8, ofmt, "pe")) {
+ break :blk .coff;
+ } else if (mem.eql(u8, ofmt, "macho")) {
+ break :blk .macho;
+ } else if (mem.eql(u8, ofmt, "wasm")) {
+ break :blk .wasm;
+ } else if (mem.eql(u8, ofmt, "hex")) {
+ break :blk .hex;
+ } else if (mem.eql(u8, ofmt, "raw")) {
+ break :blk .raw;
+ } else {
+ std.debug.print("unsupported object format: {}", .{ofmt});
+ process.exit(1);
+ }
+ };
+
const bin_path = switch (emit_bin) {
.no => {
std.debug.print("-fno-emit-bin not supported yet", .{});
src-self-hosted/Module.zig
@@ -212,7 +212,8 @@ pub const Decl = struct {
},
.block => unreachable,
.gen_zir => unreachable,
- .local_var => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
.decl => unreachable,
}
}
@@ -308,7 +309,8 @@ 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,
+ .local_val => return self.cast(LocalVal).?.gen_zir.arena,
+ .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena,
.zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
.file => unreachable,
}
@@ -320,7 +322,8 @@ 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,
+ .local_val => return self.cast(LocalVal).?.gen_zir.decl,
+ .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl,
.decl => self.cast(DeclAnalysis).?.decl,
.zir_module => null,
.file => null,
@@ -333,7 +336,8 @@ 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,
+ .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope,
+ .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope,
.decl => return self.cast(DeclAnalysis).?.decl.scope,
.zir_module, .file => return self,
}
@@ -346,7 +350,8 @@ pub const Scope = struct {
switch (self.tag) {
.block => unreachable,
.gen_zir => unreachable,
- .local_var => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
.decl => unreachable,
.zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
.file => return self.cast(File).?.fullyQualifiedNameHash(name),
@@ -361,7 +366,8 @@ 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,
+ .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(File).?.contents.tree,
+ .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(File).?.contents.tree,
}
}
@@ -370,7 +376,8 @@ pub const Scope = struct {
return switch (self.tag) {
.block => unreachable,
.gen_zir => self.cast(GenZIR).?,
- .local_var => return self.cast(LocalVar).?.gen_zir,
+ .local_val => return self.cast(LocalVal).?.gen_zir,
+ .local_ptr => return self.cast(LocalPtr).?.gen_zir,
.decl => unreachable,
.zir_module => unreachable,
.file => unreachable,
@@ -397,7 +404,8 @@ pub const Scope = struct {
.zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
.block => unreachable,
.gen_zir => unreachable,
- .local_var => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
.decl => unreachable,
}
}
@@ -408,7 +416,8 @@ pub const Scope = struct {
.zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa),
.block => unreachable,
.gen_zir => unreachable,
- .local_var => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
.decl => unreachable,
}
}
@@ -418,7 +427,8 @@ 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,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
.block => unreachable,
.decl => unreachable,
}
@@ -431,7 +441,8 @@ pub const Scope = struct {
.zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
.block => unreachable,
.gen_zir => unreachable,
- .local_var => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
.decl => unreachable,
}
}
@@ -451,7 +462,8 @@ pub const Scope = struct {
},
.block => unreachable,
.gen_zir => unreachable,
- .local_var => unreachable,
+ .local_val => unreachable,
+ .local_ptr => unreachable,
.decl => unreachable,
}
}
@@ -472,7 +484,8 @@ pub const Scope = struct {
block,
decl,
gen_zir,
- local_var,
+ local_val,
+ local_ptr,
};
pub const File = struct {
@@ -708,17 +721,31 @@ pub const Scope = struct {
instructions: std.ArrayListUnmanaged(*zir.Inst) = .{},
};
+ /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
/// 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;
+ pub const LocalVal = struct {
+ pub const base_tag: Tag = .local_val;
base: Scope = Scope{ .tag = base_tag },
- /// Parents can be: `LocalVar`, `GenZIR`.
+ /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
parent: *Scope,
gen_zir: *GenZIR,
name: []const u8,
inst: *zir.Inst,
};
+
+ /// This could be a `const` or `var` local. It has a pointer instead of a value.
+ /// This structure lives as long as the AST generation of the Block
+ /// node that contains the variable.
+ pub const LocalPtr = struct {
+ pub const base_tag: Tag = .local_ptr;
+ base: Scope = Scope{ .tag = base_tag },
+ /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`.
+ parent: *Scope,
+ gen_zir: *GenZIR,
+ name: []const u8,
+ ptr: *zir.Inst,
+ };
};
pub const AllErrors = struct {
@@ -1176,12 +1203,19 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
const param_decls = fn_proto.params();
const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
+
+ const fn_src = tree.token_locs[fn_proto.fn_token].start;
+ const type_type = try self.addZIRInstConst(&fn_type_scope.base, fn_src, .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.type_type),
+ });
+ const type_type_rl: astgen.ResultLoc = .{ .ty = type_type };
for (param_decls) |param_decl, i| {
const param_type_node = switch (param_decl.param_type) {
.any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}),
.type_expr => |node| node,
};
- param_types[i] = try astgen.expr(self, &fn_type_scope.base, param_type_node);
+ param_types[i] = try astgen.expr(self, &fn_type_scope.base, type_type_rl, 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", .{});
@@ -1209,8 +1243,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 astgen.expr(self, &fn_type_scope.base, return_type_expr);
- const fn_src = tree.token_locs[fn_proto.fn_token].start;
+ const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr);
const fn_type_inst = try self.addZIRInst(&fn_type_scope.base, fn_src, zir.Inst.FnType, .{
.return_type = return_type_inst,
.param_types = param_types,
@@ -1266,7 +1299,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
.kw_args = .{},
};
gen_scope.instructions.items[i] = &arg.base;
- const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVar);
+ const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVal);
sub_scope.* = .{
.parent = params_scope,
.gen_zir = &gen_scope,
@@ -1829,6 +1862,7 @@ fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
return self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
}
+/// TODO split this into `requireRuntimeBlock` and `requireFunctionBlock` and audit callsites.
fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {
return scope.cast(Scope.Block) orelse
return self.fail(scope, src, "instruction illegal outside function body", .{});
@@ -2098,12 +2132,7 @@ pub fn addZIRInstSpecial(
return inst;
}
-pub fn addZIRNoOp(
- self: *Module,
- scope: *Scope,
- src: usize,
- tag: zir.Inst.Tag,
-) !*zir.Inst {
+pub fn addZIRNoOpT(self: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst.NoOp {
const gen_zir = scope.getGenZIR();
try gen_zir.instructions.ensureCapacity(self.gpa, gen_zir.instructions.items.len + 1);
const inst = try gen_zir.arena.create(zir.Inst.NoOp);
@@ -2116,6 +2145,11 @@ pub fn addZIRNoOp(
.kw_args = .{},
};
gen_zir.instructions.appendAssumeCapacity(&inst.base);
+ return inst;
+}
+
+pub fn addZIRNoOp(self: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst {
+ const inst = try self.addZIRNoOpT(scope, src, tag);
return &inst.base;
}
@@ -2320,24 +2354,36 @@ fn analyzeInstConst(self: *Module, scope: *Scope, const_inst: *zir.Inst.Const) I
fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst {
switch (old_inst.tag) {
+ .alloc => return self.analyzeInstAlloc(scope, old_inst.castTag(.alloc).?),
+ .alloc_inferred => return self.analyzeInstAllocInferred(scope, old_inst.castTag(.alloc_inferred).?),
.arg => return self.analyzeInstArg(scope, old_inst.castTag(.arg).?),
+ .bitcast_result_ptr => return self.analyzeInstBitCastResultPtr(scope, old_inst.castTag(.bitcast_result_ptr).?),
.block => return self.analyzeInstBlock(scope, old_inst.castTag(.block).?),
.@"break" => return self.analyzeInstBreak(scope, old_inst.castTag(.@"break").?),
.breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.castTag(.breakpoint).?),
.breakvoid => return self.analyzeInstBreakVoid(scope, old_inst.castTag(.breakvoid).?),
.call => return self.analyzeInstCall(scope, old_inst.castTag(.call).?),
+ .coerce_result_block_ptr => return self.analyzeInstCoerceResultBlockPtr(scope, old_inst.castTag(.coerce_result_block_ptr).?),
+ .coerce_result_ptr => return self.analyzeInstCoerceResultPtr(scope, old_inst.castTag(.coerce_result_ptr).?),
+ .coerce_to_ptr_elem => return self.analyzeInstCoerceToPtrElem(scope, old_inst.castTag(.coerce_to_ptr_elem).?),
.compileerror => return self.analyzeInstCompileError(scope, old_inst.castTag(.compileerror).?),
.@"const" => return self.analyzeInstConst(scope, old_inst.castTag(.@"const").?),
.declref => return self.analyzeInstDeclRef(scope, old_inst.castTag(.declref).?),
.declref_str => return self.analyzeInstDeclRefStr(scope, old_inst.castTag(.declref_str).?),
.declval => return self.analyzeInstDeclVal(scope, old_inst.castTag(.declval).?),
.declval_in_module => return self.analyzeInstDeclValInModule(scope, old_inst.castTag(.declval_in_module).?),
+ .ensure_result_used => return self.analyzeInstEnsureResultUsed(scope, old_inst.castTag(.ensure_result_used).?),
+ .ensure_result_non_error => return self.analyzeInstEnsureResultNonError(scope, old_inst.castTag(.ensure_result_non_error).?),
+ .ret_ptr => return self.analyzeInstRetPtr(scope, old_inst.castTag(.ret_ptr).?),
+ .ret_type => return self.analyzeInstRetType(scope, old_inst.castTag(.ret_type).?),
+ .store => return self.analyzeInstStore(scope, old_inst.castTag(.store).?),
.str => return self.analyzeInstStr(scope, old_inst.castTag(.str).?),
.int => {
const big_int = old_inst.castTag(.int).?.positionals.int;
return self.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int);
},
.inttype => return self.analyzeInstIntType(scope, old_inst.castTag(.inttype).?),
+ .param_type => return self.analyzeInstParamType(scope, old_inst.castTag(.param_type).?),
.ptrtoint => return self.analyzeInstPtrToInt(scope, old_inst.castTag(.ptrtoint).?),
.fieldptr => return self.analyzeInstFieldPtr(scope, old_inst.castTag(.fieldptr).?),
.deref => return self.analyzeInstDeref(scope, old_inst.castTag(.deref).?),
@@ -2369,6 +2415,94 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
}
}
+fn analyzeInstCoerceResultBlockPtr(
+ self: *Module,
+ scope: *Scope,
+ inst: *zir.Inst.CoerceResultBlockPtr,
+) InnerError!*Inst {
+ return self.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultBlockPtr", .{});
+}
+
+fn analyzeInstBitCastResultPtr(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
+ return self.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastResultPtr", .{});
+}
+
+fn analyzeInstCoerceResultPtr(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
+ return self.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{});
+}
+
+fn analyzeInstCoerceToPtrElem(self: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst {
+ return self.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceToPtrElem", .{});
+}
+
+fn analyzeInstRetPtr(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
+ return self.fail(scope, inst.base.src, "TODO implement analyzeInstRetPtr", .{});
+}
+
+fn analyzeInstRetType(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) 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 ret_type = fn_ty.fnReturnType();
+ return self.constType(scope, inst.base.src, ret_type);
+}
+
+fn analyzeInstEnsureResultUsed(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
+ const operand = try self.resolveInst(scope, inst.positionals.operand);
+ switch (operand.ty.zigTypeTag()) {
+ .Void, .NoReturn => return self.constVoid(scope, operand.src),
+ else => return self.fail(scope, operand.src, "expression value is ignored", .{}),
+ }
+}
+
+fn analyzeInstEnsureResultNonError(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
+ const operand = try self.resolveInst(scope, inst.positionals.operand);
+ switch (operand.ty.zigTypeTag()) {
+ .ErrorSet, .ErrorUnion => return self.fail(scope, operand.src, "error is discarded", .{}),
+ else => return self.constVoid(scope, operand.src),
+ }
+}
+
+fn analyzeInstAlloc(self: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
+ return self.fail(scope, inst.base.src, "TODO implement analyzeInstAlloc", .{});
+}
+
+fn analyzeInstAllocInferred(self: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
+ return self.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});
+}
+
+fn analyzeInstStore(self: *Module, scope: *Scope, inst: *zir.Inst.Store) InnerError!*Inst {
+ return self.fail(scope, inst.base.src, "TODO implement analyzeInstStore", .{});
+}
+
+fn analyzeInstParamType(self: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst {
+ const fn_inst = try self.resolveInst(scope, inst.positionals.func);
+ const arg_index = inst.positionals.arg_index;
+
+ const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) {
+ .Fn => fn_inst.ty,
+ .BoundFn => {
+ return self.fail(scope, fn_inst.src, "TODO implement analyzeInstParamType for method call syntax", .{});
+ },
+ else => {
+ return self.fail(scope, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty});
+ },
+ };
+
+ // TODO support C-style var args
+ const param_count = fn_ty.fnParamLen();
+ if (arg_index >= param_count) {
+ return self.fail(scope, inst.base.src, "arg index {} out of bounds; '{}' has {} arguments", .{
+ arg_index,
+ fn_ty,
+ param_count,
+ });
+ }
+
+ // TODO support generic functions
+ const param_type = fn_ty.fnParamType(arg_index);
+ return self.constType(scope, inst.base.src, param_type);
+}
+
fn analyzeInstStr(self: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst {
// The bytes references memory inside the ZIR module, which can get deallocated
// after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena.
@@ -2746,13 +2880,13 @@ fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primi
return self.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue());
}
-fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.As) InnerError!*Inst {
- const dest_type = try self.resolveType(scope, as.positionals.dest_type);
- const new_inst = try self.resolveInst(scope, as.positionals.value);
+fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.BinOp) InnerError!*Inst {
+ const dest_type = try self.resolveType(scope, as.positionals.lhs);
+ const new_inst = try self.resolveInst(scope, as.positionals.rhs);
return self.coerce(scope, dest_type, new_inst);
}
-fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.PtrToInt) InnerError!*Inst {
+fn analyzeInstPtrToInt(self: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) InnerError!*Inst {
const ptr = try self.resolveInst(scope, ptrtoint.positionals.operand);
if (ptr.ty.zigTypeTag() != .Pointer) {
return self.fail(scope, ptrtoint.positionals.operand.src, "expected pointer, found '{}'", .{ptr.ty});
@@ -2797,16 +2931,16 @@ fn analyzeInstFieldPtr(self: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPt
}
}
-fn analyzeInstIntCast(self: *Module, scope: *Scope, inst: *zir.Inst.IntCast) InnerError!*Inst {
- const dest_type = try self.resolveType(scope, inst.positionals.dest_type);
- const operand = try self.resolveInst(scope, inst.positionals.operand);
+fn analyzeInstIntCast(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
+ const dest_type = try self.resolveType(scope, inst.positionals.lhs);
+ const operand = try self.resolveInst(scope, inst.positionals.rhs);
const dest_is_comptime_int = switch (dest_type.zigTypeTag()) {
.ComptimeInt => true,
.Int => false,
else => return self.fail(
scope,
- inst.positionals.dest_type.src,
+ inst.positionals.lhs.src,
"expected integer type, found '{}'",
.{
dest_type,
@@ -2818,7 +2952,7 @@ fn analyzeInstIntCast(self: *Module, scope: *Scope, inst: *zir.Inst.IntCast) Inn
.ComptimeInt, .Int => {},
else => return self.fail(
scope,
- inst.positionals.operand.src,
+ inst.positionals.rhs.src,
"expected integer type, found '{}'",
.{operand.ty},
),
@@ -2833,22 +2967,22 @@ fn analyzeInstIntCast(self: *Module, scope: *Scope, inst: *zir.Inst.IntCast) Inn
return self.fail(scope, inst.base.src, "TODO implement analyze widen or shorten int", .{});
}
-fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *zir.Inst.BitCast) InnerError!*Inst {
- const dest_type = try self.resolveType(scope, inst.positionals.dest_type);
- const operand = try self.resolveInst(scope, inst.positionals.operand);
+fn analyzeInstBitCast(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
+ const dest_type = try self.resolveType(scope, inst.positionals.lhs);
+ const operand = try self.resolveInst(scope, inst.positionals.rhs);
return self.bitcast(scope, dest_type, operand);
}
-fn analyzeInstFloatCast(self: *Module, scope: *Scope, inst: *zir.Inst.FloatCast) InnerError!*Inst {
- const dest_type = try self.resolveType(scope, inst.positionals.dest_type);
- const operand = try self.resolveInst(scope, inst.positionals.operand);
+fn analyzeInstFloatCast(self: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
+ const dest_type = try self.resolveType(scope, inst.positionals.lhs);
+ const operand = try self.resolveInst(scope, inst.positionals.rhs);
const dest_is_comptime_float = switch (dest_type.zigTypeTag()) {
.ComptimeFloat => true,
.Float => false,
else => return self.fail(
scope,
- inst.positionals.dest_type.src,
+ inst.positionals.lhs.src,
"expected float type, found '{}'",
.{
dest_type,
@@ -2860,7 +2994,7 @@ fn analyzeInstFloatCast(self: *Module, scope: *Scope, inst: *zir.Inst.FloatCast)
.ComptimeFloat, .Float, .ComptimeInt => {},
else => return self.fail(
scope,
- inst.positionals.operand.src,
+ inst.positionals.rhs.src,
"expected float type, found '{}'",
.{operand.ty},
),
@@ -3560,8 +3694,14 @@ 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;
+ .local_val => {
+ const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir;
+ gen_zir.decl.analysis = .sema_failure;
+ gen_zir.decl.generation = self.generation;
+ self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
+ },
+ .local_ptr => {
+ const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir;
gen_zir.decl.analysis = .sema_failure;
gen_zir.decl.generation = self.generation;
self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
src-self-hosted/translate_c.zig
@@ -1308,8 +1308,7 @@ fn transBinaryOperator(
const rhs = try transExpr(rp, &block_scope.base, ZigClangBinaryOperator_getRHS(stmt), .used, .r_value);
if (expr) {
_ = try appendToken(rp.c, .Semicolon, ";");
- const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label);
- break_node.rhs = rhs;
+ const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label, rhs);
try block_scope.statements.append(&break_node.base);
const block_node = try block_scope.complete(rp.c);
const rparen = try appendToken(rp.c, .RParen, ")");
@@ -1881,12 +1880,19 @@ fn transReturnStmt(
scope: *Scope,
expr: *const ZigClangReturnStmt,
) TransError!*ast.Node {
- const node = try transCreateNodeReturnExpr(rp.c);
- if (ZigClangReturnStmt_getRetValue(expr)) |val_expr| {
- node.rhs = try transExprCoercing(rp, scope, val_expr, .used, .r_value);
- }
+ const return_kw = try appendToken(rp.c, .Keyword_return, "return");
+ const rhs: ?*ast.Node = if (ZigClangReturnStmt_getRetValue(expr)) |val_expr|
+ try transExprCoercing(rp, scope, val_expr, .used, .r_value)
+ else
+ null;
+ const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{
+ .ltoken = return_kw,
+ .tag = .Return,
+ }, .{
+ .rhs = rhs,
+ });
_ = try appendToken(rp.c, .Semicolon, ";");
- return &node.base;
+ return &return_expr.base;
}
fn transStringLiteral(
@@ -1912,8 +1918,9 @@ fn transStringLiteral(
buf[buf.len - 1] = '"';
const token = try appendToken(rp.c, .StringLiteral, buf);
- const node = try rp.c.arena.create(ast.Node.StringLiteral);
+ const node = try rp.c.arena.create(ast.Node.OneToken);
node.* = .{
+ .base = .{ .tag = .StringLiteral },
.token = token,
};
return maybeSuppressResult(rp, scope, result_used, &node.base);
@@ -2518,7 +2525,7 @@ fn transDoWhileLoop(
prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true);
_ = try appendToken(rp.c, .RParen, ")");
if_node.condition = &prefix_op.base;
- if_node.body = &(try transCreateNodeBreak(rp.c, null)).base;
+ if_node.body = &(try transCreateNodeBreak(rp.c, null, null)).base;
_ = try appendToken(rp.c, .Semicolon, ";");
const body_node = if (ZigClangStmt_getStmtClass(ZigClangDoStmt_getBody(stmt)) == .CompoundStmtClass) blk: {
@@ -2688,7 +2695,7 @@ fn transSwitch(
_ = try appendToken(rp.c, .Colon, ":");
if (!switch_scope.has_default) {
const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
- else_prong.expr = &(try transCreateNodeBreak(rp.c, "__switch")).base;
+ else_prong.expr = &(try transCreateNodeBreak(rp.c, "__switch", null)).base;
_ = try appendToken(rp.c, .Comma, ",");
if (switch_scope.case_index >= switch_scope.cases.len)
@@ -2732,7 +2739,7 @@ fn transCase(
try transExpr(rp, scope, ZigClangCaseStmt_getLHS(stmt), .used, .r_value);
const switch_prong = try transCreateNodeSwitchCase(rp.c, expr);
- switch_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base;
+ switch_prong.expr = &(try transCreateNodeBreak(rp.c, label, null)).base;
_ = try appendToken(rp.c, .Comma, ",");
if (switch_scope.case_index >= switch_scope.cases.len)
@@ -2768,7 +2775,7 @@ fn transDefault(
_ = try appendToken(rp.c, .Semicolon, ";");
const else_prong = try transCreateNodeSwitchCase(rp.c, try transCreateNodeSwitchElse(rp.c));
- else_prong.expr = &(try transCreateNodeBreak(rp.c, label)).base;
+ else_prong.expr = &(try transCreateNodeBreak(rp.c, label, null)).base;
_ = try appendToken(rp.c, .Comma, ",");
if (switch_scope.case_index >= switch_scope.cases.len)
@@ -2843,8 +2850,9 @@ fn transCharLiteral(
}
var char_buf: [4]u8 = undefined;
const token = try appendTokenFmt(rp.c, .CharLiteral, "'{}'", .{escapeChar(@intCast(u8, val), &char_buf)});
- const node = try rp.c.arena.create(ast.Node.CharLiteral);
+ const node = try rp.c.arena.create(ast.Node.OneToken);
node.* = .{
+ .base = .{ .tag = .CharLiteral },
.token = token,
};
break :blk &node.base;
@@ -2889,8 +2897,11 @@ fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr,
const result = try transStmt(rp, &block_scope.base, it[0], .unused, .r_value);
try block_scope.statements.append(result);
}
- const break_node = try transCreateNodeBreak(rp.c, "blk");
- break_node.rhs = try transStmt(rp, &block_scope.base, it[0], .used, .r_value);
+ const break_node = blk: {
+ var tmp = try CtrlFlow.init(rp.c, .Break, "blk");
+ const rhs = try transStmt(rp, &block_scope.base, it[0], .used, .r_value);
+ break :blk try tmp.finish(rhs);
+ };
_ = try appendToken(rp.c, .Semicolon, ";");
try block_scope.statements.append(&break_node.base);
const block_node = try block_scope.complete(rp.c);
@@ -3205,8 +3216,7 @@ fn transCreatePreCrement(
const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
try block_scope.statements.append(assign);
- const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label);
- break_node.rhs = ref_node;
+ const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label, ref_node);
try block_scope.statements.append(&break_node.base);
const block_node = try block_scope.complete(rp.c);
// semicolon must immediately follow rbrace because it is the last token in a block
@@ -3297,8 +3307,11 @@ fn transCreatePostCrement(
const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
try block_scope.statements.append(assign);
- const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label);
- break_node.rhs = try transCreateNodeIdentifier(rp.c, tmp);
+ const break_node = blk: {
+ var tmp_ctrl_flow = try CtrlFlow.initToken(rp.c, .Break, block_scope.label);
+ const rhs = try transCreateNodeIdentifier(rp.c, tmp);
+ break :blk try tmp_ctrl_flow.finish(rhs);
+ };
try block_scope.statements.append(&break_node.base);
_ = try appendToken(rp.c, .Semicolon, ";");
const block_node = try block_scope.complete(rp.c);
@@ -3490,8 +3503,7 @@ fn transCreateCompoundAssign(
try block_scope.statements.append(assign);
}
- const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label);
- break_node.rhs = ref_node;
+ const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label, ref_node);
try block_scope.statements.append(&break_node.base);
const block_node = try block_scope.complete(rp.c);
const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
@@ -3567,10 +3579,8 @@ fn transCPtrCast(
fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node {
const break_scope = scope.getBreakableScope();
- const br = try transCreateNodeBreak(rp.c, if (break_scope.id == .Switch)
- "__switch"
- else
- null);
+ const label_text: ?[]const u8 = if (break_scope.id == .Switch) "__switch" else null;
+ const br = try transCreateNodeBreak(rp.c, label_text, null);
_ = try appendToken(rp.c, .Semicolon, ";");
return &br.base;
}
@@ -3578,8 +3588,9 @@ fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node {
fn transFloatingLiteral(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangFloatingLiteral, used: ResultUsed) TransError!*ast.Node {
// TODO use something more accurate
const dbl = ZigClangAPFloat_getValueAsApproximateDouble(stmt);
- const node = try rp.c.arena.create(ast.Node.FloatLiteral);
+ const node = try rp.c.arena.create(ast.Node.OneToken);
node.* = .{
+ .base = .{ .tag = .FloatLiteral },
.token = try appendTokenFmt(rp.c, .FloatLiteral, "{d}", .{dbl}),
};
return maybeSuppressResult(rp, scope, used, &node.base);
@@ -3619,7 +3630,7 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const
});
try block_scope.statements.append(&tmp_var.base);
- const break_node = try transCreateNodeBreakToken(rp.c, block_scope.label);
+ var break_node_tmp = try CtrlFlow.initToken(rp.c, .Break, block_scope.label);
const if_node = try transCreateNodeIf(rp.c);
var cond_scope = Scope.Condition{
@@ -3641,7 +3652,7 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const
if_node.@"else".?.body = try transExpr(rp, &block_scope.base, false_expr, .used, .r_value);
_ = try appendToken(rp.c, .Semicolon, ";");
- break_node.rhs = &if_node.base;
+ const break_node = try break_node_tmp.finish(&if_node.base);
_ = try appendToken(rp.c, .Semicolon, ";");
try block_scope.statements.append(&break_node.base);
const block_node = try block_scope.complete(rp.c);
@@ -3822,8 +3833,9 @@ fn qualTypeToLog2IntRef(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigC
if (int_bit_width != 0) {
// we can perform the log2 now.
const cast_bit_width = math.log2_int(u64, int_bit_width);
- const node = try rp.c.arena.create(ast.Node.IntegerLiteral);
+ const node = try rp.c.arena.create(ast.Node.OneToken);
node.* = .{
+ .base = .{ .tag = .IntegerLiteral },
.token = try appendTokenFmt(rp.c, .Identifier, "u{}", .{cast_bit_width}),
};
return &node.base;
@@ -3845,8 +3857,9 @@ fn qualTypeToLog2IntRef(rp: RestorePoint, qt: ZigClangQualType, source_loc: ZigC
const import_fn_call = try rp.c.createBuiltinCall("@import", 1);
const std_token = try appendToken(rp.c, .StringLiteral, "\"std\"");
- const std_node = try rp.c.arena.create(ast.Node.StringLiteral);
+ const std_node = try rp.c.arena.create(ast.Node.OneToken);
std_node.* = .{
+ .base = .{ .tag = .StringLiteral },
.token = std_token,
};
import_fn_call.params()[0] = &std_node.base;
@@ -4081,8 +4094,11 @@ fn transCreateNodeAssign(
const assign = try transCreateNodeInfixOp(rp, &block_scope.base, lhs_node, .Assign, lhs_eq_token, ident, .used, false);
try block_scope.statements.append(assign);
- const break_node = try transCreateNodeBreak(rp.c, label_name);
- break_node.rhs = try transCreateNodeIdentifier(rp.c, tmp);
+ const break_node = blk: {
+ var tmp_ctrl_flow = try CtrlFlow.init(rp.c, .Break, label_name);
+ const rhs_expr = try transCreateNodeIdentifier(rp.c, tmp);
+ break :blk try tmp_ctrl_flow.finish(rhs_expr);
+ };
_ = try appendToken(rp.c, .Semicolon, ";");
try block_scope.statements.append(&break_node.base);
const block_node = try block_scope.complete(rp.c);
@@ -4255,28 +4271,19 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node {
};
defer c.arena.free(str);
const token = try appendToken(c, .IntegerLiteral, str);
- const node = try c.arena.create(ast.Node.IntegerLiteral);
+ const node = try c.arena.create(ast.Node.OneToken);
node.* = .{
+ .base = .{ .tag = .IntegerLiteral },
.token = token,
};
return &node.base;
}
-fn transCreateNodeReturnExpr(c: *Context) !*ast.Node.ControlFlowExpression {
- const ltoken = try appendToken(c, .Keyword_return, "return");
- const node = try c.arena.create(ast.Node.ControlFlowExpression);
- node.* = .{
- .ltoken = ltoken,
- .kind = .Return,
- .rhs = null,
- };
- return node;
-}
-
fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node {
const token = try appendToken(c, .Keyword_undefined, "undefined");
- const node = try c.arena.create(ast.Node.UndefinedLiteral);
+ const node = try c.arena.create(ast.Node.OneToken);
node.* = .{
+ .base = .{ .tag = .UndefinedLiteral },
.token = token,
};
return &node.base;
@@ -4284,8 +4291,9 @@ fn transCreateNodeUndefinedLiteral(c: *Context) !*ast.Node {
fn transCreateNodeNullLiteral(c: *Context) !*ast.Node {
const token = try appendToken(c, .Keyword_null, "null");
- const node = try c.arena.create(ast.Node.NullLiteral);
+ const node = try c.arena.create(ast.Node.OneToken);
node.* = .{
+ .base = .{ .tag = .NullLiteral },
.token = token,
};
return &node.base;
@@ -4296,8 +4304,9 @@ fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node {
try appendToken(c, .Keyword_true, "true")
else
try appendToken(c, .Keyword_false, "false");
- const node = try c.arena.create(ast.Node.BoolLiteral);
+ const node = try c.arena.create(ast.Node.OneToken);
node.* = .{
+ .base = .{ .tag = .BoolLiteral },
.token = token,
};
return &node.base;
@@ -4305,8 +4314,9 @@ fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node {
fn transCreateNodeInt(c: *Context, int: anytype) !*ast.Node {
const token = try appendTokenFmt(c, .IntegerLiteral, "{}", .{int});
- const node = try c.arena.create(ast.Node.IntegerLiteral);
+ const node = try c.arena.create(ast.Node.OneToken);
node.* = .{
+ .base = .{ .tag = .IntegerLiteral },
.token = token,
};
return &node.base;
@@ -4314,8 +4324,9 @@ fn transCreateNodeInt(c: *Context, int: anytype) !*ast.Node {
fn transCreateNodeFloat(c: *Context, int: anytype) !*ast.Node {
const token = try appendTokenFmt(c, .FloatLiteral, "{}", .{int});
- const node = try c.arena.create(ast.Node.FloatLiteral);
+ const node = try c.arena.create(ast.Node.OneToken);
node.* = .{
+ .base = .{ .tag = .FloatLiteral },
.token = token,
};
return &node.base;
@@ -4362,7 +4373,7 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a
const block_lbrace = try appendToken(c, .LBrace, "{");
- const return_expr = try transCreateNodeReturnExpr(c);
+ const return_kw = try appendToken(c, .Keyword_return, "return");
const unwrap_expr = try transCreateNodeUnwrapNull(c, ref.cast(ast.Node.VarDecl).?.getTrailer("init_node").?);
const call_expr = try c.createCall(unwrap_expr, fn_params.items.len);
@@ -4376,7 +4387,12 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a
}
call_expr.rtoken = try appendToken(c, .RParen, ")");
- return_expr.rhs = &call_expr.base;
+ const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{
+ .ltoken = return_kw,
+ .tag = .Return,
+ }, .{
+ .rhs = &call_expr.base,
+ });
_ = try appendToken(c, .Semicolon, ";");
const block = try ast.Node.Block.alloc(c.arena, 1);
@@ -4424,8 +4440,9 @@ fn transCreateNodeEnumLiteral(c: *Context, name: []const u8) !*ast.Node {
}
fn transCreateNodeStringLiteral(c: *Context, str: []const u8) !*ast.Node {
- const node = try c.arena.create(ast.Node.StringLiteral);
+ const node = try c.arena.create(ast.Node.OneToken);
node.* = .{
+ .base = .{ .tag = .StringLiteral },
.token = try appendToken(c, .StringLiteral, str),
};
return &node.base;
@@ -4455,28 +4472,77 @@ fn transCreateNodeElse(c: *Context) !*ast.Node.Else {
return node;
}
-fn transCreateNodeBreakToken(c: *Context, label: ?ast.TokenIndex) !*ast.Node.ControlFlowExpression {
- const other_token = label orelse return transCreateNodeBreak(c, null);
+fn transCreateNodeBreakToken(
+ c: *Context,
+ label: ?ast.TokenIndex,
+ rhs: ?*ast.Node,
+) !*ast.Node.ControlFlowExpression {
+ const other_token = label orelse return transCreateNodeBreak(c, null, rhs);
const loc = c.token_locs.items[other_token];
const label_name = c.source_buffer.items[loc.start..loc.end];
- return transCreateNodeBreak(c, label_name);
+ return transCreateNodeBreak(c, label_name, rhs);
}
-fn transCreateNodeBreak(c: *Context, label: ?[]const u8) !*ast.Node.ControlFlowExpression {
- const ltoken = try appendToken(c, .Keyword_break, "break");
- const label_node = if (label) |l| blk: {
- _ = try appendToken(c, .Colon, ":");
- break :blk try transCreateNodeIdentifier(c, l);
- } else null;
- const node = try c.arena.create(ast.Node.ControlFlowExpression);
- node.* = .{
- .ltoken = ltoken,
- .kind = .{ .Break = label_node },
- .rhs = null,
- };
- return node;
+fn transCreateNodeBreak(
+ c: *Context,
+ label: ?[]const u8,
+ rhs: ?*ast.Node,
+) !*ast.Node.ControlFlowExpression {
+ var ctrl_flow = try CtrlFlow.init(c, .Break, label);
+ return ctrl_flow.finish(rhs);
}
+const CtrlFlow = struct {
+ c: *Context,
+ ltoken: ast.TokenIndex,
+ label_token: ?ast.TokenIndex,
+ tag: ast.Node.Tag,
+
+ /// Does everything except the RHS.
+ fn init(c: *Context, tag: ast.Node.Tag, label: ?[]const u8) !CtrlFlow {
+ const kw: Token.Id = switch (tag) {
+ .Break => .Keyword_break,
+ .Continue => .Keyword_continue,
+ .Return => .Keyword_return,
+ else => unreachable,
+ };
+ const kw_text = switch (tag) {
+ .Break => "break",
+ .Continue => "continue",
+ .Return => "return",
+ else => unreachable,
+ };
+ const ltoken = try appendToken(c, kw, kw_text);
+ const label_token = if (label) |l| blk: {
+ _ = try appendToken(c, .Colon, ":");
+ break :blk try appendToken(c, .Identifier, l);
+ } else null;
+ return CtrlFlow{
+ .c = c,
+ .ltoken = ltoken,
+ .label_token = label_token,
+ .tag = tag,
+ };
+ }
+
+ fn initToken(c: *Context, tag: ast.Node.Tag, label: ?ast.TokenIndex) !CtrlFlow {
+ const other_token = label orelse return init(c, tag, null);
+ const loc = c.token_locs.items[other_token];
+ const label_name = c.source_buffer.items[loc.start..loc.end];
+ return init(c, tag, label_name);
+ }
+
+ fn finish(self: *CtrlFlow, rhs: ?*ast.Node) !*ast.Node.ControlFlowExpression {
+ return ast.Node.ControlFlowExpression.create(self.c.arena, .{
+ .ltoken = self.ltoken,
+ .tag = self.tag,
+ }, .{
+ .label = self.label_token,
+ .rhs = rhs,
+ });
+ }
+};
+
fn transCreateNodeWhile(c: *Context) !*ast.Node.While {
const while_tok = try appendToken(c, .Keyword_while, "while");
_ = try appendToken(c, .LParen, "(");
@@ -4497,12 +4563,10 @@ fn transCreateNodeWhile(c: *Context) !*ast.Node.While {
fn transCreateNodeContinue(c: *Context) !*ast.Node {
const ltoken = try appendToken(c, .Keyword_continue, "continue");
- const node = try c.arena.create(ast.Node.ControlFlowExpression);
- node.* = .{
+ const node = try ast.Node.ControlFlowExpression.create(c.arena, .{
.ltoken = ltoken,
- .kind = .{ .Continue = null },
- .rhs = null,
- };
+ .tag = .Continue,
+ }, .{});
_ = try appendToken(c, .Semicolon, ";");
return &node.base;
}
@@ -5006,8 +5070,9 @@ pub fn failDecl(c: *Context, loc: ZigClangSourceLocation, name: []const u8, comp
const semi_tok = try appendToken(c, .Semicolon, ";");
_ = try appendTokenFmt(c, .LineComment, "// {}", .{c.locStr(loc)});
- const msg_node = try c.arena.create(ast.Node.StringLiteral);
+ const msg_node = try c.arena.create(ast.Node.OneToken);
msg_node.* = .{
+ .base = .{ .tag = .StringLiteral },
.token = msg_tok,
};
@@ -5110,8 +5175,9 @@ fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex {
fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node {
const token_index = try appendIdentifier(c, name);
- const identifier = try c.arena.create(ast.Node.Identifier);
+ const identifier = try c.arena.create(ast.Node.OneToken);
identifier.* = .{
+ .base = .{ .tag = .Identifier },
.token = token_index,
};
return &identifier.base;
@@ -5119,8 +5185,9 @@ fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node {
fn transCreateNodeIdentifierUnchecked(c: *Context, name: []const u8) !*ast.Node {
const token_index = try appendTokenFmt(c, .Identifier, "{}", .{name});
- const identifier = try c.arena.create(ast.Node.Identifier);
+ const identifier = try c.arena.create(ast.Node.OneToken);
identifier.* = .{
+ .base = .{ .tag = .Identifier },
.token = token_index,
};
return &identifier.base;
@@ -5289,8 +5356,9 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
const param_name_tok = try appendIdentifier(c, mangled_name);
_ = try appendToken(c, .Colon, ":");
- const any_type = try c.arena.create(ast.Node.AnyType);
+ const any_type = try c.arena.create(ast.Node.OneToken);
any_type.* = .{
+ .base = .{ .tag = .AnyType },
.token = try appendToken(c, .Keyword_anytype, "anytype"),
};
@@ -5322,7 +5390,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
const type_of = try c.createBuiltinCall("@TypeOf", 1);
- const return_expr = try transCreateNodeReturnExpr(c);
+ const return_kw = try appendToken(c, .Keyword_return, "return");
const expr = try parseCExpr(c, it, source, source_loc, scope);
const last = it.next().?;
if (last.id != .Eof and last.id != .Nl)
@@ -5337,13 +5405,17 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
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.tag == .ControlFlowExpression);
- const br = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", blk_last);
- break :blk br.rhs.?;
+ const br = blk_last.cast(ast.Node.ControlFlowExpression).?;
+ break :blk br.getRHS().?;
};
type_of.params()[0] = type_of_arg;
type_of.rparen_token = try appendToken(c, .RParen, ")");
- return_expr.rhs = expr;
+ const return_expr = try ast.Node.ControlFlowExpression.create(c.arena, .{
+ .ltoken = return_kw,
+ .tag = .Return,
+ }, .{
+ .rhs = expr,
+ });
try block_scope.statements.append(&return_expr.base);
const block_node = try block_scope.complete(c);
@@ -5416,8 +5488,7 @@ fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_
}
}
- const break_node = try transCreateNodeBreak(c, label_name);
- break_node.rhs = last;
+ const break_node = try transCreateNodeBreak(c, label_name, last);
try block_scope.statements.append(&break_node.base);
const block_node = try block_scope.complete(c);
return &block_node.base;
@@ -5656,15 +5727,17 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
const first_tok = it.list.at(0);
if (source[tok.start] != '\'' or source[tok.start + 1] == '\\' or tok.end - tok.start == 3) {
const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, source[tok.start..tok.end], source[first_tok.start..first_tok.end], source_loc));
- const node = try c.arena.create(ast.Node.CharLiteral);
+ const node = try c.arena.create(ast.Node.OneToken);
node.* = .{
+ .base = .{ .tag = .CharLiteral },
.token = token,
};
return &node.base;
} else {
const token = try appendTokenFmt(c, .IntegerLiteral, "0x{x}", .{source[tok.start + 1 .. tok.end - 1]});
- const node = try c.arena.create(ast.Node.IntegerLiteral);
+ const node = try c.arena.create(ast.Node.OneToken);
node.* = .{
+ .base = .{ .tag = .IntegerLiteral },
.token = token,
};
return &node.base;
@@ -5673,8 +5746,9 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
.StringLiteral => {
const first_tok = it.list.at(0);
const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, source[tok.start..tok.end], source[first_tok.start..first_tok.end], source_loc));
- const node = try c.arena.create(ast.Node.StringLiteral);
+ const node = try c.arena.create(ast.Node.OneToken);
node.* = .{
+ .base = .{ .tag = .StringLiteral },
.token = token,
};
return &node.base;
@@ -6224,7 +6298,7 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
=> return node,
.Identifier => {
- const ident = node.cast(ast.Node.Identifier).?;
+ const ident = node.castTag(.Identifier).?;
if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
if (value.cast(ast.Node.VarDecl)) |var_decl|
return getContainer(c, var_decl.getTrailer("init_node").?);
@@ -6238,7 +6312,7 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
for (container.fieldsAndDecls()) |field_ref| {
const field = field_ref.cast(ast.Node.ContainerField).?;
- const ident = infix.rhs.cast(ast.Node.Identifier).?;
+ const ident = infix.rhs.castTag(.Identifier).?;
if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
return getContainer(c, field.type_expr.?);
}
@@ -6253,7 +6327,7 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node {
}
fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
- if (ref.cast(ast.Node.Identifier)) |ident| {
+ if (ref.castTag(.Identifier)) |ident| {
if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| {
if (value.cast(ast.Node.VarDecl)) |var_decl| {
if (var_decl.getTrailer("type_node")) |ty|
@@ -6265,7 +6339,7 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node {
if (ty_node.cast(ast.Node.ContainerDecl)) |container| {
for (container.fieldsAndDecls()) |field_ref| {
const field = field_ref.cast(ast.Node.ContainerField).?;
- const ident = infix.rhs.cast(ast.Node.Identifier).?;
+ const ident = infix.rhs.castTag(.Identifier).?;
if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) {
return getContainer(c, field.type_expr.?);
}
src-self-hosted/zir.zig
@@ -34,9 +34,17 @@ pub const Inst = struct {
/// These names are used directly as the instruction names in the text format.
pub const Tag = enum {
+ /// Allocates stack local memory. Its lifetime ends when the block ends that contains
+ /// this instruction.
+ alloc,
+ /// Same as `alloc` except the type is inferred.
+ alloc_inferred,
/// Function parameter value. These must be first in a function's main block,
/// in respective order with the parameters.
arg,
+ /// A typed result location pointer is bitcasted to a new result location pointer.
+ /// The new result location pointer has an inferred type.
+ bitcast_result_ptr,
/// A labeled block of code, which can return a value.
block,
/// Return a value from a `Block`.
@@ -45,6 +53,17 @@ pub const Inst = struct {
/// Same as `break` but without an operand; the operand is assumed to be the void value.
breakvoid,
call,
+ /// Coerces a result location pointer to a new element type. It is evaluated "backwards"-
+ /// as type coercion from the new element type to the old element type.
+ /// LHS is destination element type, RHS is result pointer.
+ coerce_result_ptr,
+ /// This instruction does a `coerce_result_ptr` operation on a `Block`'s
+ /// result location pointer, whose type is inferred by peer type resolution on the
+ /// `Block`'s corresponding `break` instructions.
+ coerce_result_block_ptr,
+ /// Equivalent to `as(ptr_child_type(typeof(ptr)), value)`.
+ coerce_to_ptr_elem,
+ /// Emit an error message and fail compilation.
compileerror,
/// Special case, has no textual representation.
@"const",
@@ -57,7 +76,17 @@ pub const Inst = struct {
declval,
/// Same as declval but the parameter is a `*Module.Decl` rather than a name.
declval_in_module,
+ /// Emits a compile error if the operand is not `void`.
+ ensure_result_used,
+ /// Emits a compile error if an error is ignored.
+ ensure_result_non_error,
boolnot,
+ /// Obtains a pointer to the return value.
+ ret_ptr,
+ /// Obtains the return type of the in-scope function.
+ ret_type,
+ /// Write a value to a pointer.
+ store,
/// String Literal. Makes an anonymous Decl and then takes a pointer to it.
str,
int,
@@ -73,6 +102,9 @@ pub const Inst = struct {
@"fn",
fntype,
@"export",
+ /// Given a reference to a function and a parameter index, returns the
+ /// type of the parameter. TODO what happens when the parameter is `anytype`?
+ param_type,
primitive,
intcast,
bitcast,
@@ -96,6 +128,9 @@ pub const Inst = struct {
.breakpoint,
.@"unreachable",
.returnvoid,
+ .alloc_inferred,
+ .ret_ptr,
+ .ret_type,
=> NoOp,
.boolnot,
@@ -103,6 +138,11 @@ pub const Inst = struct {
.@"return",
.isnull,
.isnonnull,
+ .ptrtoint,
+ .alloc,
+ .ensure_result_used,
+ .ensure_result_non_error,
+ .bitcast_result_ptr,
=> UnOp,
.add,
@@ -113,32 +153,36 @@ pub const Inst = struct {
.cmp_gte,
.cmp_gt,
.cmp_neq,
+ .as,
+ .floatcast,
+ .intcast,
+ .bitcast,
+ .coerce_result_ptr,
=> BinOp,
.block => Block,
.@"break" => Break,
.breakvoid => BreakVoid,
.call => Call,
+ .coerce_to_ptr_elem => CoerceToPtrElem,
.declref => DeclRef,
.declref_str => DeclRefStr,
.declval => DeclVal,
.declval_in_module => DeclValInModule,
+ .coerce_result_block_ptr => CoerceResultBlockPtr,
.compileerror => CompileError,
.@"const" => Const,
+ .store => Store,
.str => Str,
.int => Int,
.inttype => IntType,
- .ptrtoint => PtrToInt,
.fieldptr => FieldPtr,
- .as => As,
.@"asm" => Asm,
.@"fn" => Fn,
.@"export" => Export,
+ .param_type => ParamType,
.primitive => Primitive,
.fntype => FnType,
- .intcast => IntCast,
- .bitcast => BitCast,
- .floatcast => FloatCast,
.elemptr => ElemPtr,
.condbr => CondBr,
};
@@ -148,15 +192,26 @@ pub const Inst = struct {
/// Function calls do not count.
pub fn isNoReturn(tag: Tag) bool {
return switch (tag) {
+ .alloc,
+ .alloc_inferred,
.arg,
+ .bitcast_result_ptr,
.block,
.breakpoint,
.call,
+ .coerce_result_ptr,
+ .coerce_result_block_ptr,
+ .coerce_to_ptr_elem,
.@"const",
.declref,
.declref_str,
.declval,
.declval_in_module,
+ .ensure_result_used,
+ .ensure_result_non_error,
+ .ret_ptr,
+ .ret_type,
+ .store,
.str,
.int,
.inttype,
@@ -168,6 +223,7 @@ pub const Inst = struct {
.@"fn",
.fntype,
.@"export",
+ .param_type,
.primitive,
.intcast,
.bitcast,
@@ -292,6 +348,17 @@ pub const Inst = struct {
},
};
+ pub const CoerceToPtrElem = struct {
+ pub const base_tag = Tag.coerce_to_ptr_elem;
+ base: Inst,
+
+ positionals: struct {
+ ptr: *Inst,
+ value: *Inst,
+ },
+ kw_args: struct {},
+ };
+
pub const DeclRef = struct {
pub const base_tag = Tag.declref;
base: Inst,
@@ -332,6 +399,17 @@ pub const Inst = struct {
kw_args: struct {},
};
+ pub const CoerceResultBlockPtr = struct {
+ pub const base_tag = Tag.coerce_result_block_ptr;
+ base: Inst,
+
+ positionals: struct {
+ dest_type: *Inst,
+ block: *Block,
+ },
+ kw_args: struct {},
+ };
+
pub const CompileError = struct {
pub const base_tag = Tag.compileerror;
base: Inst,
@@ -352,33 +430,33 @@ pub const Inst = struct {
kw_args: struct {},
};
- pub const Str = struct {
- pub const base_tag = Tag.str;
+ pub const Store = struct {
+ pub const base_tag = Tag.store;
base: Inst,
positionals: struct {
- bytes: []const u8,
+ ptr: *Inst,
+ value: *Inst,
},
kw_args: struct {},
};
- pub const Int = struct {
- pub const base_tag = Tag.int;
+ pub const Str = struct {
+ pub const base_tag = Tag.str;
base: Inst,
positionals: struct {
- int: BigIntConst,
+ bytes: []const u8,
},
kw_args: struct {},
};
- pub const PtrToInt = struct {
- pub const builtin_name = "@ptrToInt";
- pub const base_tag = Tag.ptrtoint;
+ pub const Int = struct {
+ pub const base_tag = Tag.int;
base: Inst,
positionals: struct {
- operand: *Inst,
+ int: BigIntConst,
},
kw_args: struct {},
};
@@ -394,18 +472,6 @@ pub const Inst = struct {
kw_args: struct {},
};
- pub const As = struct {
- pub const base_tag = Tag.as;
- pub const builtin_name = "@as";
- base: Inst,
-
- positionals: struct {
- dest_type: *Inst,
- value: *Inst,
- },
- kw_args: struct {},
- };
-
pub const Asm = struct {
pub const base_tag = Tag.@"asm";
base: Inst,
@@ -469,6 +535,17 @@ pub const Inst = struct {
kw_args: struct {},
};
+ pub const ParamType = struct {
+ pub const base_tag = Tag.param_type;
+ base: Inst,
+
+ positionals: struct {
+ func: *Inst,
+ arg_index: usize,
+ },
+ kw_args: struct {},
+ };
+
pub const Primitive = struct {
pub const base_tag = Tag.primitive;
base: Inst,
@@ -559,42 +636,6 @@ pub const Inst = struct {
};
};
- pub const FloatCast = struct {
- pub const base_tag = Tag.floatcast;
- pub const builtin_name = "@floatCast";
- base: Inst,
-
- positionals: struct {
- dest_type: *Inst,
- operand: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const IntCast = struct {
- pub const base_tag = Tag.intcast;
- pub const builtin_name = "@intCast";
- base: Inst,
-
- positionals: struct {
- dest_type: *Inst,
- operand: *Inst,
- },
- kw_args: struct {},
- };
-
- pub const BitCast = struct {
- pub const base_tag = Tag.bitcast;
- pub const builtin_name = "@bitCast";
- base: Inst,
-
- positionals: struct {
- dest_type: *Inst,
- operand: *Inst,
- },
- kw_args: struct {},
- };
-
pub const ElemPtr = struct {
pub const base_tag = Tag.elemptr;
base: Inst,
@@ -1467,15 +1508,15 @@ const EmitZIR = struct {
},
.ComptimeInt => return self.emitComptimeIntVal(src, typed_value.val),
.Int => {
- const as_inst = try self.arena.allocator.create(Inst.As);
+ const as_inst = try self.arena.allocator.create(Inst.BinOp);
as_inst.* = .{
.base = .{
+ .tag = .as,
.src = src,
- .tag = Inst.As.base_tag,
},
.positionals = .{
- .dest_type = (try self.emitType(src, typed_value.ty)).inst,
- .value = (try self.emitComptimeIntVal(src, typed_value.val)).inst,
+ .lhs = (try self.emitType(src, typed_value.ty)).inst,
+ .rhs = (try self.emitComptimeIntVal(src, typed_value.val)).inst,
},
.kw_args = .{},
};
@@ -1640,17 +1681,17 @@ const EmitZIR = struct {
src: usize,
new_body: ZirBody,
old_inst: *ir.Inst.UnOp,
- comptime I: type,
+ tag: Inst.Tag,
) Allocator.Error!*Inst {
- const new_inst = try self.arena.allocator.create(I);
+ const new_inst = try self.arena.allocator.create(Inst.BinOp);
new_inst.* = .{
.base = .{
.src = src,
- .tag = I.base_tag,
+ .tag = tag,
},
.positionals = .{
- .dest_type = (try self.emitType(src, old_inst.base.ty)).inst,
- .operand = try self.resolveInst(new_body, old_inst.operand),
+ .lhs = (try self.emitType(old_inst.base.src, old_inst.base.ty)).inst,
+ .rhs = try self.resolveInst(new_body, old_inst.operand),
},
.kw_args = .{},
};
@@ -1691,9 +1732,9 @@ const EmitZIR = struct {
.cmp_gt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_gt).?, .cmp_gt),
.cmp_neq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_neq).?, .cmp_neq),
- .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, Inst.BitCast),
- .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, Inst.IntCast),
- .floatcast => try self.emitCast(inst.src, new_body, inst.castTag(.floatcast).?, Inst.FloatCast),
+ .bitcast => try self.emitCast(inst.src, new_body, inst.castTag(.bitcast).?, .bitcast),
+ .intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast),
+ .floatcast => try self.emitCast(inst.src, new_body, inst.castTag(.floatcast).?, .floatcast),
.block => blk: {
const old_inst = inst.castTag(.block).?;
test/stage2/cbe.zig
@@ -19,13 +19,13 @@ pub fn addCases(ctx: *TestContext) !void {
\\fn main() noreturn {}
\\
\\export fn _start() noreturn {
- \\ main();
+ \\ main();
\\}
,
\\noreturn void main(void);
\\
\\noreturn void _start(void) {
- \\ main();
+ \\ main();
\\}
\\
\\noreturn void main(void) {}
@@ -35,15 +35,15 @@ pub fn addCases(ctx: *TestContext) !void {
// TODO: figure out a way to prevent asm constants from being generated
ctx.c("inline asm", linux_x64,
\\fn exitGood() void {
- \\ asm volatile ("syscall"
- \\ :
- \\ : [number] "{rax}" (231),
- \\ [arg1] "{rdi}" (0)
- \\ );
+ \\ asm volatile ("syscall"
+ \\ :
+ \\ : [number] "{rax}" (231),
+ \\ [arg1] "{rdi}" (0)
+ \\ );
\\}
\\
\\export fn _start() noreturn {
- \\ exitGood();
+ \\ exitGood();
\\}
,
\\#include <stddef.h>
@@ -55,36 +55,14 @@ pub fn addCases(ctx: *TestContext) !void {
\\const char *const exitGood__anon_2 = "syscall";
\\
\\noreturn void _start(void) {
- \\ exitGood();
+ \\ exitGood();
\\}
\\
\\void exitGood(void) {
- \\ register size_t rax_constant __asm__("rax") = 231;
- \\ register size_t rdi_constant __asm__("rdi") = 0;
- \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
- \\ return;
- \\}
- \\
- );
- ctx.c("basic return", linux_x64,
- \\fn main() u8 {
- \\ return 103;
- \\}
- \\
- \\export fn _start() noreturn {
- \\ _ = main();
- \\}
- ,
- \\#include <stdint.h>
- \\
- \\uint8_t main(void);
- \\
- \\noreturn void _start(void) {
- \\ (void)main();
- \\}
- \\
- \\uint8_t main(void) {
- \\ return 103;
+ \\ register size_t rax_constant __asm__("rax") = 231;
+ \\ register size_t rdi_constant __asm__("rdi") = 0;
+ \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
+ \\ return;
\\}
\\
);