Merge pull request #8266 from ziglang/zir-memory-layout

rework ZIR memory layout; overhaul source locations
This commit is contained in:
Andrew Kelley
2021-03-31 23:11:15 -07:00
committed by GitHub
33 changed files with 14773 additions and 11549 deletions

View File

@@ -539,7 +539,7 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/src/ThreadPool.zig"
"${CMAKE_SOURCE_DIR}/src/TypedValue.zig"
"${CMAKE_SOURCE_DIR}/src/WaitGroup.zig"
"${CMAKE_SOURCE_DIR}/src/astgen.zig"
"${CMAKE_SOURCE_DIR}/src/AstGen.zig"
"${CMAKE_SOURCE_DIR}/src/clang.zig"
"${CMAKE_SOURCE_DIR}/src/clang_options.zig"
"${CMAKE_SOURCE_DIR}/src/clang_options_data.zig"
@@ -591,7 +591,7 @@ set(ZIG_STAGE2_SOURCES
"${CMAKE_SOURCE_DIR}/src/value.zig"
"${CMAKE_SOURCE_DIR}/src/windows_sdk.zig"
"${CMAKE_SOURCE_DIR}/src/zir.zig"
"${CMAKE_SOURCE_DIR}/src/zir_sema.zig"
"${CMAKE_SOURCE_DIR}/src/Sema.zig"
)
if(MSVC)

View File

@@ -32,7 +32,7 @@ pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_def
.fields = fields,
.decls = &[_]std.builtin.TypeInfo.Declaration{},
.is_tuple = false,
}});
} });
}
/// Looks up the supplied fields in the given enum type.
@@ -70,7 +70,7 @@ pub fn values(comptime E: type) []const E {
test "std.enum.values" {
const E = extern enum { a, b, c, d = 0 };
testing.expectEqualSlices(E, &.{.a, .b, .c, .d}, values(E));
testing.expectEqualSlices(E, &.{ .a, .b, .c, .d }, values(E));
}
/// Returns the set of all unique named values in the given enum, in
@@ -82,10 +82,10 @@ pub fn uniqueValues(comptime E: type) []const E {
test "std.enum.uniqueValues" {
const E = extern enum { a, b, c, d = 0, e, f = 3 };
testing.expectEqualSlices(E, &.{.a, .b, .c, .f}, uniqueValues(E));
testing.expectEqualSlices(E, &.{ .a, .b, .c, .f }, uniqueValues(E));
const F = enum { a, b, c };
testing.expectEqualSlices(F, &.{.a, .b, .c}, uniqueValues(F));
testing.expectEqualSlices(F, &.{ .a, .b, .c }, uniqueValues(F));
}
/// Returns the set of all unique field values in the given enum, in
@@ -102,8 +102,7 @@ pub fn uniqueFields(comptime E: type) []const EnumField {
}
var unique_fields: []const EnumField = &[_]EnumField{};
outer:
for (raw_fields) |candidate| {
outer: for (raw_fields) |candidate| {
for (unique_fields) |u| {
if (u.value == candidate.value)
continue :outer;
@@ -116,28 +115,25 @@ pub fn uniqueFields(comptime E: type) []const EnumField {
}
/// Determines the length of a direct-mapped enum array, indexed by
/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
/// @intCast(usize, @enumToInt(enum_value)).
/// If the enum is non-exhaustive, the resulting length will only be enough
/// to hold all explicit fields.
/// If the enum contains any fields with values that cannot be represented
/// by usize, a compile error is issued. The max_unused_slots parameter limits
/// the total number of items which have no matching enum key (holes in the enum
/// numbering). So for example, if an enum has values 1, 2, 5, and 6, max_unused_slots
/// must be at least 3, to allow unused slots 0, 3, and 4.
fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int) comptime_int {
const info = @typeInfo(E).Enum;
if (!info.is_exhaustive) {
@compileError("Cannot create direct array of non-exhaustive enum "++@typeName(E));
}
var max_value: comptime_int = -1;
const max_usize: comptime_int = ~@as(usize, 0);
const fields = uniqueFields(E);
for (fields) |f| {
if (f.value < 0) {
@compileError("Cannot create a direct enum array for "++@typeName(E)++", field ."++f.name++" has a negative value.");
@compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ", field ." ++ f.name ++ " has a negative value.");
}
if (f.value > max_value) {
if (f.value > max_usize) {
@compileError("Cannot create a direct enum array for "++@typeName(E)++", field ."++f.name++" is larger than the max value of usize.");
@compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ", field ." ++ f.name ++ " is larger than the max value of usize.");
}
max_value = f.value;
}
@@ -147,14 +143,16 @@ fn directEnumArrayLen(comptime E: type, comptime max_unused_slots: comptime_int)
if (unused_slots > max_unused_slots) {
const unused_str = std.fmt.comptimePrint("{d}", .{unused_slots});
const allowed_str = std.fmt.comptimePrint("{d}", .{max_unused_slots});
@compileError("Cannot create a direct enum array for "++@typeName(E)++". It would have "++unused_str++" unused slots, but only "++allowed_str++" are allowed.");
@compileError("Cannot create a direct enum array for " ++ @typeName(E) ++ ". It would have " ++ unused_str ++ " unused slots, but only " ++ allowed_str ++ " are allowed.");
}
return max_value + 1;
}
/// Initializes an array of Data which can be indexed by
/// @intCast(usize, @enumToInt(enum_value)). The enum must be exhaustive.
/// @intCast(usize, @enumToInt(enum_value)).
/// If the enum is non-exhaustive, the resulting array will only be large enough
/// to hold all explicit fields.
/// If the enum contains any fields with values that cannot be represented
/// by usize, a compile error is issued. The max_unused_slots parameter limits
/// the total number of items which have no matching enum key (holes in the enum
@@ -243,9 +241,9 @@ pub fn nameCast(comptime E: type, comptime value: anytype) E {
if (@hasField(E, n)) {
return @field(E, n);
}
@compileError("Enum "++@typeName(E)++" has no field named "++n);
@compileError("Enum " ++ @typeName(E) ++ " has no field named " ++ n);
}
@compileError("Cannot cast from "++@typeName(@TypeOf(value))++" to "++@typeName(E));
@compileError("Cannot cast from " ++ @typeName(@TypeOf(value)) ++ " to " ++ @typeName(E));
}
}
@@ -256,7 +254,7 @@ test "std.enums.nameCast" {
testing.expectEqual(A.a, nameCast(A, A.a));
testing.expectEqual(A.a, nameCast(A, B.a));
testing.expectEqual(A.a, nameCast(A, "a"));
testing.expectEqual(A.a, nameCast(A, @as(*const[1]u8, "a")));
testing.expectEqual(A.a, nameCast(A, @as(*const [1]u8, "a")));
testing.expectEqual(A.a, nameCast(A, @as([:0]const u8, "a")));
testing.expectEqual(A.a, nameCast(A, @as([]const u8, "a")));
@@ -398,12 +396,12 @@ pub fn EnumArray(comptime E: type, comptime V: type) type {
pub fn NoExtension(comptime Self: type) type {
return NoExt;
}
const NoExt = struct{};
const NoExt = struct {};
/// A set type with an Indexer mapping from keys to indices.
/// Presence or absence is stored as a dense bitfield. This
/// type does no allocation and can be copied by value.
pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type {
pub fn IndexedSet(comptime I: type, comptime Ext: fn (type) type) type {
comptime ensureIndexer(I);
return struct {
const Self = @This();
@@ -422,7 +420,7 @@ pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type {
bits: BitSet = BitSet.initEmpty(),
/// Returns a set containing all possible keys.
/// Returns a set containing all possible keys.
pub fn initFull() Self {
return .{ .bits = BitSet.initFull() };
}
@@ -492,7 +490,8 @@ pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type {
pub fn next(self: *Iterator) ?Key {
return if (self.inner.next()) |index|
Indexer.keyForIndex(index)
else null;
else
null;
}
};
};
@@ -501,7 +500,7 @@ pub fn IndexedSet(comptime I: type, comptime Ext: fn(type)type) type {
/// A map from keys to values, using an index lookup. Uses a
/// bitfield to track presence and a dense array of values.
/// This type does no allocation and can be copied by value.
pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn(type)type) type {
pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn (type) type) type {
comptime ensureIndexer(I);
return struct {
const Self = @This();
@@ -652,7 +651,8 @@ pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn(type)type
.key = Indexer.keyForIndex(index),
.value = &self.values[index],
}
else null;
else
null;
}
};
};
@@ -660,7 +660,7 @@ pub fn IndexedMap(comptime I: type, comptime V: type, comptime Ext: fn(type)type
/// A dense array of values, using an indexed lookup.
/// This type does no allocation and can be copied by value.
pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: fn(type)type) type {
pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: fn (type) type) type {
comptime ensureIndexer(I);
return struct {
const Self = @This();
@@ -769,9 +769,9 @@ pub fn ensureIndexer(comptime T: type) void {
if (!@hasDecl(T, "count")) @compileError("Indexer must have decl count: usize.");
if (@TypeOf(T.count) != usize) @compileError("Indexer.count must be a usize.");
if (!@hasDecl(T, "indexOf")) @compileError("Indexer.indexOf must be a fn(Key)usize.");
if (@TypeOf(T.indexOf) != fn(T.Key)usize) @compileError("Indexer must have decl indexOf: fn(Key)usize.");
if (@TypeOf(T.indexOf) != fn (T.Key) usize) @compileError("Indexer must have decl indexOf: fn(Key)usize.");
if (!@hasDecl(T, "keyForIndex")) @compileError("Indexer must have decl keyForIndex: fn(usize)Key.");
if (@TypeOf(T.keyForIndex) != fn(usize)T.Key) @compileError("Indexer.keyForIndex must be a fn(usize)Key.");
if (@TypeOf(T.keyForIndex) != fn (usize) T.Key) @compileError("Indexer.keyForIndex must be a fn(usize)Key.");
}
}
@@ -802,14 +802,18 @@ pub fn EnumIndexer(comptime E: type) type {
return struct {
pub const Key = E;
pub const count: usize = 0;
pub fn indexOf(e: E) usize { unreachable; }
pub fn keyForIndex(i: usize) E { unreachable; }
pub fn indexOf(e: E) usize {
unreachable;
}
pub fn keyForIndex(i: usize) E {
unreachable;
}
};
}
std.sort.sort(EnumField, &fields, {}, ascByValue);
const min = fields[0].value;
const max = fields[fields.len-1].value;
if (max - min == fields.len-1) {
const max = fields[fields.len - 1].value;
if (max - min == fields.len - 1) {
return struct {
pub const Key = E;
pub const count = fields.len;
@@ -844,7 +848,7 @@ pub fn EnumIndexer(comptime E: type) type {
}
test "std.enums.EnumIndexer dense zeroed" {
const E = enum{ b = 1, a = 0, c = 2 };
const E = enum { b = 1, a = 0, c = 2 };
const Indexer = EnumIndexer(E);
ensureIndexer(Indexer);
testing.expectEqual(E, Indexer.Key);
@@ -908,7 +912,7 @@ test "std.enums.EnumIndexer sparse" {
}
test "std.enums.EnumIndexer repeats" {
const E = extern enum{ a = -2, c = 6, b = 4, b2 = 4 };
const E = extern enum { a = -2, c = 6, b = 4, b2 = 4 };
const Indexer = EnumIndexer(E);
ensureIndexer(Indexer);
testing.expectEqual(E, Indexer.Key);
@@ -957,7 +961,8 @@ test "std.enums.EnumSet" {
}
var mut = Set.init(.{
.a=true, .c=true,
.a = true,
.c = true,
});
testing.expectEqual(@as(usize, 2), mut.count());
testing.expectEqual(true, mut.contains(.a));
@@ -986,7 +991,7 @@ test "std.enums.EnumSet" {
testing.expectEqual(@as(?E, null), it.next());
}
mut.toggleSet(Set.init(.{ .a=true, .b=true }));
mut.toggleSet(Set.init(.{ .a = true, .b = true }));
testing.expectEqual(@as(usize, 2), mut.count());
testing.expectEqual(true, mut.contains(.a));
testing.expectEqual(false, mut.contains(.b));
@@ -994,7 +999,7 @@ test "std.enums.EnumSet" {
testing.expectEqual(true, mut.contains(.d));
testing.expectEqual(true, mut.contains(.e)); // aliases a
mut.setUnion(Set.init(.{ .a=true, .b=true }));
mut.setUnion(Set.init(.{ .a = true, .b = true }));
testing.expectEqual(@as(usize, 3), mut.count());
testing.expectEqual(true, mut.contains(.a));
testing.expectEqual(true, mut.contains(.b));
@@ -1009,7 +1014,7 @@ test "std.enums.EnumSet" {
testing.expectEqual(false, mut.contains(.c));
testing.expectEqual(true, mut.contains(.d));
mut.setIntersection(Set.init(.{ .a=true, .b=true }));
mut.setIntersection(Set.init(.{ .a = true, .b = true }));
testing.expectEqual(@as(usize, 1), mut.count());
testing.expectEqual(true, mut.contains(.a));
testing.expectEqual(false, mut.contains(.b));
@@ -1072,7 +1077,7 @@ test "std.enums.EnumArray sized" {
const undef = Array.initUndefined();
var inst = Array.initFill(5);
const inst2 = Array.init(.{ .a = 1, .b = 2, .c = 3, .d = 4 });
const inst3 = Array.initDefault(6, .{.b = 4, .c = 2});
const inst3 = Array.initDefault(6, .{ .b = 4, .c = 2 });
testing.expectEqual(@as(usize, 5), inst.get(.a));
testing.expectEqual(@as(usize, 5), inst.get(.b));
@@ -1272,10 +1277,12 @@ test "std.enums.EnumMap sized" {
var iter = a.iterator();
const Entry = Map.Entry;
testing.expectEqual(@as(?Entry, Entry{
.key = .b, .value = &a.values[1],
.key = .b,
.value = &a.values[1],
}), iter.next());
testing.expectEqual(@as(?Entry, Entry{
.key = .d, .value = &a.values[3],
.key = .d,
.value = &a.values[3],
}), iter.next());
testing.expectEqual(@as(?Entry, null), iter.next());
}

View File

@@ -11,7 +11,7 @@ pub const Tokenizer = tokenizer.Tokenizer;
pub const fmtId = @import("zig/fmt.zig").fmtId;
pub const fmtEscapes = @import("zig/fmt.zig").fmtEscapes;
pub const parse = @import("zig/parse.zig").parse;
pub const parseStringLiteral = @import("zig/string_literal.zig").parse;
pub const string_literal = @import("zig/string_literal.zig");
pub const ast = @import("zig/ast.zig");
pub const system = @import("zig/system.zig");
pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;

View File

@@ -1252,6 +1252,7 @@ pub const Tree = struct {
buffer[0] = data.lhs;
const params = if (data.lhs == 0) buffer[0..0] else buffer[0..1];
return tree.fullFnProto(.{
.proto_node = node,
.fn_token = tree.nodes.items(.main_token)[node],
.return_type = data.rhs,
.params = params,
@@ -1267,6 +1268,7 @@ pub const Tree = struct {
const params_range = tree.extraData(data.lhs, Node.SubRange);
const params = tree.extra_data[params_range.start..params_range.end];
return tree.fullFnProto(.{
.proto_node = node,
.fn_token = tree.nodes.items(.main_token)[node],
.return_type = data.rhs,
.params = params,
@@ -1283,6 +1285,7 @@ pub const Tree = struct {
buffer[0] = extra.param;
const params = if (extra.param == 0) buffer[0..0] else buffer[0..1];
return tree.fullFnProto(.{
.proto_node = node,
.fn_token = tree.nodes.items(.main_token)[node],
.return_type = data.rhs,
.params = params,
@@ -1298,6 +1301,7 @@ pub const Tree = struct {
const extra = tree.extraData(data.lhs, Node.FnProto);
const params = tree.extra_data[extra.params_start..extra.params_end];
return tree.fullFnProto(.{
.proto_node = node,
.fn_token = tree.nodes.items(.main_token)[node],
.return_type = data.rhs,
.params = params,
@@ -1430,7 +1434,7 @@ pub const Tree = struct {
.ast = .{
.lbracket = tree.nodes.items(.main_token)[node],
.elem_count = data.lhs,
.sentinel = null,
.sentinel = 0,
.elem_type = data.rhs,
},
};
@@ -1440,6 +1444,7 @@ pub const Tree = struct {
assert(tree.nodes.items(.tag)[node] == .array_type_sentinel);
const data = tree.nodes.items(.data)[node];
const extra = tree.extraData(data.rhs, Node.ArrayTypeSentinel);
assert(extra.sentinel != 0);
return .{
.ast = .{
.lbracket = tree.nodes.items(.main_token)[node],
@@ -2119,6 +2124,7 @@ pub const full = struct {
ast: Ast,
pub const Ast = struct {
proto_node: Node.Index,
fn_token: TokenIndex,
return_type: Node.Index,
params: []const Node.Index,
@@ -2262,7 +2268,7 @@ pub const full = struct {
pub const Ast = struct {
lbracket: TokenIndex,
elem_count: Node.Index,
sentinel: ?Node.Index,
sentinel: Node.Index,
elem_type: Node.Index,
};
};
@@ -2549,9 +2555,9 @@ pub const Node = struct {
@"await",
/// `?lhs`. rhs unused. main_token is the `?`.
optional_type,
/// `[lhs]rhs`. lhs can be omitted to make it a slice.
/// `[lhs]rhs`.
array_type,
/// `[lhs:a]b`. `array_type_sentinel[rhs]`.
/// `[lhs:a]b`. `ArrayTypeSentinel[rhs]`.
array_type_sentinel,
/// `[*]align(lhs) rhs`. lhs can be omitted.
/// `*align(lhs) rhs`. lhs can be omitted.

View File

@@ -59,10 +59,7 @@ pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!Tree {
parser.nodes.appendAssumeCapacity(.{
.tag = .root,
.main_token = 0,
.data = .{
.lhs = undefined,
.rhs = undefined,
},
.data = undefined,
});
const root_members = try parser.parseContainerMembers();
const root_decls = try root_members.toSpan(&parser);
@@ -139,6 +136,16 @@ const Parser = struct {
return result;
}
fn setNode(p: *Parser, i: usize, elem: ast.NodeList.Elem) Node.Index {
p.nodes.set(i, elem);
return @intCast(Node.Index, i);
}
fn reserveNode(p: *Parser) !usize {
try p.nodes.resize(p.gpa, p.nodes.len + 1);
return p.nodes.len - 1;
}
fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index {
const fields = std.meta.fields(@TypeOf(extra));
try p.extra_data.ensureCapacity(p.gpa, p.extra_data.items.len + fields.len);
@@ -554,9 +561,10 @@ const Parser = struct {
return fn_proto;
},
.l_brace => {
const fn_decl_index = try p.reserveNode();
const body_block = try p.parseBlock();
assert(body_block != 0);
return p.addNode(.{
return p.setNode(fn_decl_index, .{
.tag = .fn_decl,
.main_token = p.nodes.items(.main_token)[fn_proto],
.data = .{
@@ -634,6 +642,10 @@ const Parser = struct {
/// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr)
fn parseFnProto(p: *Parser) !Node.Index {
const fn_token = p.eatToken(.keyword_fn) orelse return null_node;
// We want the fn proto node to be before its children in the array.
const fn_proto_index = try p.reserveNode();
_ = p.eatToken(.identifier);
const params = try p.parseParamDeclList();
defer params.deinit(p.gpa);
@@ -651,7 +663,7 @@ const Parser = struct {
if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) {
switch (params) {
.zero_or_one => |param| return p.addNode(.{
.zero_or_one => |param| return p.setNode(fn_proto_index, .{
.tag = .fn_proto_simple,
.main_token = fn_token,
.data = .{
@@ -661,7 +673,7 @@ const Parser = struct {
}),
.multi => |list| {
const span = try p.listToSpan(list);
return p.addNode(.{
return p.setNode(fn_proto_index, .{
.tag = .fn_proto_multi,
.main_token = fn_token,
.data = .{
@@ -676,7 +688,7 @@ const Parser = struct {
}
}
switch (params) {
.zero_or_one => |param| return p.addNode(.{
.zero_or_one => |param| return p.setNode(fn_proto_index, .{
.tag = .fn_proto_one,
.main_token = fn_token,
.data = .{
@@ -691,7 +703,7 @@ const Parser = struct {
}),
.multi => |list| {
const span = try p.listToSpan(list);
return p.addNode(.{
return p.setNode(fn_proto_index, .{
.tag = .fn_proto,
.main_token = fn_token,
.data = .{

View File

@@ -717,9 +717,9 @@ fn renderArrayType(
ais.pushIndentNextLine();
try renderToken(ais, tree, array_type.ast.lbracket, inner_space); // lbracket
try renderExpression(gpa, ais, tree, array_type.ast.elem_count, inner_space);
if (array_type.ast.sentinel) |sentinel| {
try renderToken(ais, tree, tree.firstToken(sentinel) - 1, inner_space); // colon
try renderExpression(gpa, ais, tree, sentinel, inner_space);
if (array_type.ast.sentinel != 0) {
try renderToken(ais, tree, tree.firstToken(array_type.ast.sentinel) - 1, inner_space); // colon
try renderExpression(gpa, ais, tree, array_type.ast.sentinel, inner_space);
}
ais.popIndent();
try renderToken(ais, tree, rbracket, .none); // rbracket

View File

@@ -6,112 +6,143 @@
const std = @import("../std.zig");
const assert = std.debug.assert;
const State = enum {
Start,
Backslash,
};
pub const ParseError = error{
OutOfMemory,
/// When this is returned, index will be the position of the character.
InvalidCharacter,
InvalidStringLiteral,
};
/// caller owns returned memory
pub fn parse(
allocator: *std.mem.Allocator,
bytes: []const u8,
bad_index: *usize, // populated if error.InvalidCharacter is returned
) ParseError![]u8 {
pub const Result = union(enum) {
success,
/// Found an invalid character at this index.
invalid_character: usize,
/// Expected hex digits at this index.
expected_hex_digits: usize,
/// Invalid hex digits at this index.
invalid_hex_escape: usize,
/// Invalid unicode escape at this index.
invalid_unicode_escape: usize,
/// The left brace at this index is missing a matching right brace.
missing_matching_rbrace: usize,
/// Expected unicode digits at this index.
expected_unicode_digits: usize,
};
/// Parses `bytes` as a Zig string literal and appends the result to `buf`.
/// Asserts `bytes` has '"' at beginning and end.
pub fn parseAppend(buf: *std.ArrayList(u8), bytes: []const u8) error{OutOfMemory}!Result {
assert(bytes.len >= 2 and bytes[0] == '"' and bytes[bytes.len - 1] == '"');
var list = std.ArrayList(u8).init(allocator);
errdefer list.deinit();
const slice = bytes[1..];
try list.ensureCapacity(slice.len - 1);
const prev_len = buf.items.len;
try buf.ensureCapacity(prev_len + slice.len - 1);
errdefer buf.shrinkRetainingCapacity(prev_len);
const State = enum {
Start,
Backslash,
};
var state = State.Start;
var index: usize = 0;
while (index < slice.len) : (index += 1) {
while (true) : (index += 1) {
const b = slice[index];
switch (state) {
State.Start => switch (b) {
'\\' => state = State.Backslash,
'\n' => {
bad_index.* = index;
return error.InvalidCharacter;
return Result{ .invalid_character = index };
},
'"' => return list.toOwnedSlice(),
else => try list.append(b),
'"' => return Result.success,
else => try buf.append(b),
},
State.Backslash => switch (b) {
'n' => {
try list.append('\n');
try buf.append('\n');
state = State.Start;
},
'r' => {
try list.append('\r');
try buf.append('\r');
state = State.Start;
},
'\\' => {
try list.append('\\');
try buf.append('\\');
state = State.Start;
},
't' => {
try list.append('\t');
try buf.append('\t');
state = State.Start;
},
'\'' => {
try list.append('\'');
try buf.append('\'');
state = State.Start;
},
'"' => {
try list.append('"');
try buf.append('"');
state = State.Start;
},
'x' => {
// TODO: add more/better/broader tests for this.
const index_continue = index + 3;
if (slice.len >= index_continue)
if (std.fmt.parseUnsigned(u8, slice[index + 1 .. index_continue], 16)) |char| {
try list.append(char);
state = State.Start;
index = index_continue - 1; // loop-header increments again
continue;
} else |_| {};
bad_index.* = index;
return error.InvalidCharacter;
if (slice.len < index_continue) {
return Result{ .expected_hex_digits = index };
}
if (std.fmt.parseUnsigned(u8, slice[index + 1 .. index_continue], 16)) |byte| {
try buf.append(byte);
state = State.Start;
index = index_continue - 1; // loop-header increments again
} else |err| switch (err) {
error.Overflow => unreachable, // 2 digits base 16 fits in a u8.
error.InvalidCharacter => {
return Result{ .invalid_hex_escape = index + 1 };
},
}
},
'u' => {
// TODO: add more/better/broader tests for this.
if (slice.len > index + 2 and slice[index + 1] == '{')
// TODO: we are already inside a nice, clean state machine... use it
// instead of this hacky code.
if (slice.len > index + 2 and slice[index + 1] == '{') {
if (std.mem.indexOfScalarPos(u8, slice[0..std.math.min(index + 9, slice.len)], index + 3, '}')) |index_end| {
const hex_str = slice[index + 2 .. index_end];
if (std.fmt.parseUnsigned(u32, hex_str, 16)) |uint| {
if (uint <= 0x10ffff) {
try list.appendSlice(std.mem.toBytes(uint)[0..]);
try buf.appendSlice(std.mem.toBytes(uint)[0..]);
state = State.Start;
index = index_end; // loop-header increments
continue;
}
} else |_| {}
};
bad_index.* = index;
return error.InvalidCharacter;
} else |err| switch (err) {
error.Overflow => unreachable,
error.InvalidCharacter => {
return Result{ .invalid_unicode_escape = index + 1 };
},
}
} else {
return Result{ .missing_matching_rbrace = index + 1 };
}
} else {
return Result{ .expected_unicode_digits = index };
}
},
else => {
bad_index.* = index;
return error.InvalidCharacter;
return Result{ .invalid_character = index };
},
},
}
} else unreachable; // TODO should not need else unreachable on while(true)
}
/// Higher level API. Does not return extra info about parse errors.
/// Caller owns returned memory.
pub fn parseAlloc(allocator: *std.mem.Allocator, bytes: []const u8) ParseError![]u8 {
var buf = std.ArrayList(u8).init(allocator);
defer buf.deinit();
switch (try parseAppend(&buf, bytes)) {
.success => return buf.toOwnedSlice(),
else => return error.InvalidStringLiteral,
}
unreachable;
}
test "parse" {
@@ -121,9 +152,8 @@ test "parse" {
var fixed_buf_mem: [32]u8 = undefined;
var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buf_mem[0..]);
var alloc = &fixed_buf_alloc.allocator;
var bad_index: usize = undefined;
expect(eql(u8, "foo", try parse(alloc, "\"foo\"", &bad_index)));
expect(eql(u8, "foo", try parse(alloc, "\"f\x6f\x6f\"", &bad_index)));
expect(eql(u8, "f💯", try parse(alloc, "\"f\u{1f4af}\"", &bad_index)));
expect(eql(u8, "foo", try parseAlloc(alloc, "\"foo\"")));
expect(eql(u8, "foo", try parseAlloc(alloc, "\"f\x6f\x6f\"")));
expect(eql(u8, "f💯", try parseAlloc(alloc, "\"f\u{1f4af}\"")));
}

4275
src/AstGen.zig Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -259,7 +259,7 @@ pub const CObject = struct {
/// To support incremental compilation, errors are stored in various places
/// so that they can be created and destroyed appropriately. This structure
/// is used to collect all the errors from the various places into one
/// convenient place for API users to consume. It is allocated into 1 heap
/// convenient place for API users to consume. It is allocated into 1 arena
/// and freed all at once.
pub const AllErrors = struct {
arena: std.heap.ArenaAllocator.State,
@@ -267,11 +267,11 @@ pub const AllErrors = struct {
pub const Message = union(enum) {
src: struct {
src_path: []const u8,
line: usize,
column: usize,
byte_offset: usize,
msg: []const u8,
src_path: []const u8,
line: u32,
column: u32,
byte_offset: u32,
notes: []Message = &.{},
},
plain: struct {
@@ -316,29 +316,31 @@ pub const AllErrors = struct {
const notes = try arena.allocator.alloc(Message, module_err_msg.notes.len);
for (notes) |*note, i| {
const module_note = module_err_msg.notes[i];
const source = try module_note.src_loc.file_scope.getSource(module);
const loc = std.zig.findLineColumn(source, module_note.src_loc.byte_offset);
const sub_file_path = module_note.src_loc.file_scope.sub_file_path;
const source = try module_note.src_loc.fileScope().getSource(module);
const byte_offset = try module_note.src_loc.byteOffset();
const loc = std.zig.findLineColumn(source, byte_offset);
const sub_file_path = module_note.src_loc.fileScope().sub_file_path;
note.* = .{
.src = .{
.src_path = try arena.allocator.dupe(u8, sub_file_path),
.msg = try arena.allocator.dupe(u8, module_note.msg),
.byte_offset = module_note.src_loc.byte_offset,
.line = loc.line,
.column = loc.column,
.byte_offset = byte_offset,
.line = @intCast(u32, loc.line),
.column = @intCast(u32, loc.column),
},
};
}
const source = try module_err_msg.src_loc.file_scope.getSource(module);
const loc = std.zig.findLineColumn(source, module_err_msg.src_loc.byte_offset);
const sub_file_path = module_err_msg.src_loc.file_scope.sub_file_path;
const source = try module_err_msg.src_loc.fileScope().getSource(module);
const byte_offset = try module_err_msg.src_loc.byteOffset();
const loc = std.zig.findLineColumn(source, byte_offset);
const sub_file_path = module_err_msg.src_loc.fileScope().sub_file_path;
try errors.append(.{
.src = .{
.src_path = try arena.allocator.dupe(u8, sub_file_path),
.msg = try arena.allocator.dupe(u8, module_err_msg.msg),
.byte_offset = module_err_msg.src_loc.byte_offset,
.line = loc.line,
.column = loc.column,
.byte_offset = byte_offset,
.line = @intCast(u32, loc.line),
.column = @intCast(u32, loc.column),
.notes = notes,
},
});
@@ -939,6 +941,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
};
const module = try arena.create(Module);
errdefer module.deinit();
module.* = .{
.gpa = gpa,
.comp = comp,
@@ -946,7 +949,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
.root_scope = root_scope,
.zig_cache_artifact_directory = zig_cache_artifact_directory,
.emit_h = options.emit_h,
.error_name_list = try std.ArrayListUnmanaged([]const u8).initCapacity(gpa, 1),
};
module.error_name_list.appendAssumeCapacity("(no error)");
break :blk module;
} else blk: {
if (options.emit_h != null) return error.NoZigModuleForCHeader;

File diff suppressed because it is too large Load Diff

View File

@@ -2,13 +2,14 @@ const std = @import("std");
const Order = std.math.Order;
const Value = @import("value.zig").Value;
const RangeSet = @This();
const SwitchProngSrc = @import("AstGen.zig").SwitchProngSrc;
ranges: std.ArrayList(Range),
pub const Range = struct {
start: Value,
end: Value,
src: usize,
first: Value,
last: Value,
src: SwitchProngSrc,
};
pub fn init(allocator: *std.mem.Allocator) RangeSet {
@@ -21,18 +22,15 @@ pub fn deinit(self: *RangeSet) void {
self.ranges.deinit();
}
pub fn add(self: *RangeSet, start: Value, end: Value, src: usize) !?usize {
pub fn add(self: *RangeSet, first: Value, last: Value, src: SwitchProngSrc) !?SwitchProngSrc {
for (self.ranges.items) |range| {
if ((start.compare(.gte, range.start) and start.compare(.lte, range.end)) or
(end.compare(.gte, range.start) and end.compare(.lte, range.end)))
{
// ranges overlap
return range.src;
if (last.compare(.gte, range.first) and first.compare(.lte, range.last)) {
return range.src; // They overlap.
}
}
try self.ranges.append(.{
.start = start,
.end = end,
.first = first,
.last = last,
.src = src,
});
return null;
@@ -40,14 +38,17 @@ pub fn add(self: *RangeSet, start: Value, end: Value, src: usize) !?usize {
/// Assumes a and b do not overlap
fn lessThan(_: void, a: Range, b: Range) bool {
return a.start.compare(.lt, b.start);
return a.first.compare(.lt, b.first);
}
pub fn spans(self: *RangeSet, start: Value, end: Value) !bool {
pub fn spans(self: *RangeSet, first: Value, last: Value) !bool {
if (self.ranges.items.len == 0)
return false;
std.sort.sort(Range, self.ranges.items, {}, lessThan);
if (!self.ranges.items[0].start.eql(start) or
!self.ranges.items[self.ranges.items.len - 1].end.eql(end))
if (!self.ranges.items[0].first.eql(first) or
!self.ranges.items[self.ranges.items.len - 1].last.eql(last))
{
return false;
}
@@ -62,11 +63,11 @@ pub fn spans(self: *RangeSet, start: Value, end: Value) !bool {
// i starts counting from the second item.
const prev = self.ranges.items[i];
// prev.end + 1 == cur.start
try counter.copy(prev.end.toBigInt(&space));
// prev.last + 1 == cur.first
try counter.copy(prev.last.toBigInt(&space));
try counter.addScalar(counter.toConst(), 1);
const cur_start_int = cur.start.toBigInt(&space);
const cur_start_int = cur.first.toBigInt(&space);
if (!cur_start_int.eq(counter.toConst())) {
return false;
}

4897
src/Sema.zig Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,7 @@ const DW = std.dwarf;
const leb128 = std.leb;
const log = std.log.scoped(.codegen);
const build_options = @import("build_options");
const LazySrcLoc = Module.LazySrcLoc;
/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
pub const BlockData = struct {
@@ -498,7 +499,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
defer function.stack.deinit(bin_file.allocator);
defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
var call_info = function.resolveCallingConventionValues(src_loc.byte_offset, fn_type) catch |err| switch (err) {
var call_info = function.resolveCallingConventionValues(src_loc.lazy, fn_type) catch |err| switch (err) {
error.CodegenFail => return Result{ .fail = function.err_msg.? },
else => |e| return e,
};
@@ -791,8 +792,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
}
fn dbgAdvancePCAndLine(self: *Self, src: usize) InnerError!void {
self.prev_di_src = src;
fn dbgAdvancePCAndLine(self: *Self, abs_byte_off: usize) InnerError!void {
self.prev_di_src = abs_byte_off;
self.prev_di_pc = self.code.items.len;
switch (self.debug_output) {
.dwarf => |dbg_out| {
@@ -800,7 +801,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
// lookup table, and changing ir.Inst from storing byte offset to token. Currently
// this involves scanning over the source code for newlines
// (but only from the previous byte offset to the new one).
const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, src);
const delta_line = std.zig.lineDelta(self.source, self.prev_di_src, abs_byte_off);
const delta_pc = self.code.items.len - self.prev_di_pc;
// TODO Look into using the DWARF special opcodes to compress this data. It lets you emit
// single-byte opcodes that add different numbers to both the PC and the line number
@@ -897,6 +898,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?),
.is_err => return self.genIsErr(inst.castTag(.is_err).?),
.is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?),
.error_to_int => return self.genErrorToInt(inst.castTag(.error_to_int).?),
.int_to_error => return self.genIntToError(inst.castTag(.int_to_error).?),
.load => return self.genLoad(inst.castTag(.load).?),
.loop => return self.genLoop(inst.castTag(.loop).?),
.not => return self.genNot(inst.castTag(.not).?),
@@ -978,7 +981,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
/// Copies a value to a register without tracking the register. The register is not considered
/// allocated. A second call to `copyToTmpRegister` may return the same register.
/// This can have a side effect of spilling instructions to the stack to free up a register.
fn copyToTmpRegister(self: *Self, src: usize, ty: Type, mcv: MCValue) !Register {
fn copyToTmpRegister(self: *Self, src: LazySrcLoc, ty: Type, mcv: MCValue) !Register {
const reg = self.findUnusedReg() orelse b: {
// We'll take over the first register. Move the instruction that was previously
// there to a stack allocation.
@@ -1457,7 +1460,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
fn genArmBinOpCode(
self: *Self,
src: usize,
src: LazySrcLoc,
dst_reg: Register,
lhs_mcv: MCValue,
rhs_mcv: MCValue,
@@ -1620,7 +1623,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
fn genX8664BinMathCode(
self: *Self,
src: usize,
src: LazySrcLoc,
dst_ty: Type,
dst_mcv: MCValue,
src_mcv: MCValue,
@@ -1706,7 +1709,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
}
fn genX8664ModRMRegToStack(self: *Self, src: usize, ty: Type, off: u32, reg: Register, opcode: u8) !void {
fn genX8664ModRMRegToStack(self: *Self, src: LazySrcLoc, ty: Type, off: u32, reg: Register, opcode: u8) !void {
const abi_size = ty.abiSize(self.target.*);
const adj_off = off + abi_size;
try self.code.ensureCapacity(self.code.items.len + 7);
@@ -1807,7 +1810,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return result;
}
fn genBreakpoint(self: *Self, src: usize) !MCValue {
fn genBreakpoint(self: *Self, src: LazySrcLoc) !MCValue {
switch (arch) {
.i386, .x86_64 => {
try self.code.append(0xcc); // int3
@@ -2234,7 +2237,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
}
fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {
fn ret(self: *Self, src: LazySrcLoc, mcv: MCValue) !MCValue {
const ret_ty = self.fn_type.fnReturnType();
try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv);
switch (arch) {
@@ -2324,8 +2327,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
}
fn genDbgStmt(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
try self.dbgAdvancePCAndLine(inst.base.src);
fn genDbgStmt(self: *Self, inst: *ir.Inst.DbgStmt) !MCValue {
// TODO when reworking tzir memory layout, rework source locations here as
// well to be more efficient, as well as support inlined function calls correctly.
// For now we convert LazySrcLoc to absolute byte offset, to match what the
// existing codegen code expects.
try self.dbgAdvancePCAndLine(inst.byte_offset);
assert(inst.base.isUnused());
return MCValue.dead;
}
@@ -2562,6 +2569,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{});
}
fn genErrorToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
return self.resolveInst(inst.operand);
}
fn genIntToError(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
return self.resolveInst(inst.operand);
}
fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {
// A loop is a setup to be able to jump back to the beginning.
const start_index = self.code.items.len;
@@ -2571,7 +2586,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
/// Send control flow to the `index` of `self.code`.
fn jump(self: *Self, src: usize, index: usize) !void {
fn jump(self: *Self, src: LazySrcLoc, index: usize) !void {
switch (arch) {
.i386, .x86_64 => {
try self.code.ensureCapacity(self.code.items.len + 5);
@@ -2628,7 +2643,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
}
fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {
fn performReloc(self: *Self, src: LazySrcLoc, reloc: Reloc) !void {
switch (reloc) {
.rel32 => |pos| {
const amt = self.code.items.len - (pos + 4);
@@ -2692,7 +2707,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
}
fn br(self: *Self, src: usize, block: *ir.Inst.Block, operand: *ir.Inst) !MCValue {
fn br(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block, operand: *ir.Inst) !MCValue {
if (operand.ty.hasCodeGenBits()) {
const operand_mcv = try self.resolveInst(operand);
const block_mcv = @bitCast(MCValue, block.codegen.mcv);
@@ -2705,7 +2720,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.brVoid(src, block);
}
fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue {
fn brVoid(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block) !MCValue {
// Emit a jump with a relocation. It will be patched up after the block ends.
try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);
@@ -2767,7 +2782,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{});
}
if (inst.output) |output| {
if (inst.output_name) |output| {
if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
}
@@ -2799,7 +2814,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.fail(inst.base.src, "TODO implement support for more aarch64 assembly instructions", .{});
}
if (inst.output) |output| {
if (inst.output_name) |output| {
if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
}
@@ -2829,7 +2844,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});
}
if (inst.output) |output| {
if (inst.output_name) |output| {
if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
}
@@ -2859,7 +2874,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
}
if (inst.output) |output| {
if (inst.output_name) |output| {
if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});
}
@@ -2909,7 +2924,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
/// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
fn setRegOrMem(self: *Self, src: usize, ty: Type, loc: MCValue, val: MCValue) !void {
fn setRegOrMem(self: *Self, src: LazySrcLoc, ty: Type, loc: MCValue, val: MCValue) !void {
switch (loc) {
.none => return,
.register => |reg| return self.genSetReg(src, ty, reg, val),
@@ -2921,7 +2936,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
}
fn genSetStack(self: *Self, src: usize, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
fn genSetStack(self: *Self, src: LazySrcLoc, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
switch (arch) {
.arm, .armeb => switch (mcv) {
.dead => unreachable,
@@ -3160,7 +3175,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
}
fn genSetReg(self: *Self, src: usize, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
fn genSetReg(self: *Self, src: LazySrcLoc, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
switch (arch) {
.arm, .armeb => switch (mcv) {
.dead => unreachable,
@@ -3703,7 +3718,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return mcv;
}
fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) InnerError!MCValue {
fn genTypedValue(self: *Self, src: LazySrcLoc, typed_value: TypedValue) InnerError!MCValue {
if (typed_value.val.isUndef())
return MCValue{ .undef = {} };
const ptr_bits = self.target.cpu.arch.ptrBitWidth();
@@ -3778,7 +3793,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
};
/// Caller must call `CallMCValues.deinit`.
fn resolveCallingConventionValues(self: *Self, src: usize, fn_ty: Type) !CallMCValues {
fn resolveCallingConventionValues(self: *Self, src: LazySrcLoc, fn_ty: Type) !CallMCValues {
const cc = fn_ty.fnCallingConvention();
const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
defer self.gpa.free(param_types);
@@ -3992,13 +4007,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
};
}
fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) InnerError {
fn fail(self: *Self, src: LazySrcLoc, comptime format: []const u8, args: anytype) InnerError {
@setCold(true);
assert(self.err_msg == null);
self.err_msg = try ErrorMsg.create(self.bin_file.allocator, .{
.file_scope = self.src_loc.file_scope,
.byte_offset = src,
}, format, args);
const src_loc = if (src != .unneeded)
src.toSrcLocWithDecl(self.mod_fn.owner_decl)
else
self.src_loc;
self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src_loc, format, args);
return error.CodegenFail;
}

View File

@@ -14,6 +14,7 @@ const TypedValue = @import("../TypedValue.zig");
const C = link.File.C;
const Decl = Module.Decl;
const trace = @import("../tracy.zig").trace;
const LazySrcLoc = Module.LazySrcLoc;
const Mutability = enum { Const, Mut };
@@ -145,11 +146,10 @@ pub const DeclGen = struct {
error_msg: ?*Module.ErrorMsg,
typedefs: TypedefMap,
fn fail(dg: *DeclGen, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, .{
.file_scope = dg.decl.getFileScope(),
.byte_offset = src,
}, format, args);
fn fail(dg: *DeclGen, src: LazySrcLoc, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
@setCold(true);
const src_loc = src.toSrcLocWithDecl(dg.decl);
dg.error_msg = try Module.ErrorMsg.create(dg.module.gpa, src_loc, format, args);
return error.AnalysisFail;
}
@@ -160,7 +160,7 @@ pub const DeclGen = struct {
val: Value,
) error{ OutOfMemory, AnalysisFail }!void {
if (val.isUndef()) {
return dg.fail(dg.decl.src(), "TODO: C backend: properly handle undefined in all cases (with debug safety?)", .{});
return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: properly handle undefined in all cases (with debug safety?)", .{});
}
switch (t.zigTypeTag()) {
.Int => {
@@ -193,7 +193,7 @@ pub const DeclGen = struct {
try writer.print("{s}", .{decl.name});
},
else => |e| return dg.fail(
dg.decl.src(),
.{ .node_offset = 0 },
"TODO: C backend: implement Pointer value {s}",
.{@tagName(e)},
),
@@ -276,7 +276,7 @@ pub const DeclGen = struct {
try writer.writeAll(", .error = 0 }");
}
},
else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement value {s}", .{
else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{
@tagName(e),
}),
}
@@ -350,7 +350,7 @@ pub const DeclGen = struct {
break;
}
} else {
return dg.fail(dg.decl.src(), "TODO: C backend: implement integer types larger than 128 bits", .{});
return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement integer types larger than 128 bits", .{});
}
},
else => unreachable,
@@ -358,7 +358,7 @@ pub const DeclGen = struct {
},
.Pointer => {
if (t.isSlice()) {
return dg.fail(dg.decl.src(), "TODO: C backend: implement slices", .{});
return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement slices", .{});
} else {
try dg.renderType(w, t.elemType());
try w.writeAll(" *");
@@ -431,7 +431,7 @@ pub const DeclGen = struct {
dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
},
.Null, .Undefined => unreachable, // must be const or comptime
else => |e| return dg.fail(dg.decl.src(), "TODO: C backend: implement type {s}", .{
else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type {s}", .{
@tagName(e),
}),
}
@@ -569,13 +569,15 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
.optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?),
.is_err => try genIsErr(o, inst.castTag(.is_err).?),
.is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?),
.error_to_int => try genErrorToInt(o, inst.castTag(.error_to_int).?),
.int_to_error => try genIntToError(o, inst.castTag(.int_to_error).?),
.unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),
.unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),
.unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?),
.unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?),
.wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?),
.wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?),
else => |e| return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement codegen for {}", .{e}),
else => |e| return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for {}", .{e}),
};
switch (result_value) {
.none => {},
@@ -756,11 +758,11 @@ fn genCall(o: *Object, inst: *Inst.Call) !CValue {
try writer.writeAll(");\n");
return result_local;
} else {
return o.dg.fail(o.dg.decl.src(), "TODO: C backend: implement function pointers", .{});
return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement function pointers", .{});
}
}
fn genDbgStmt(o: *Object, inst: *Inst.NoOp) !CValue {
fn genDbgStmt(o: *Object, inst: *Inst.DbgStmt) !CValue {
// TODO emit #line directive here with line number and filename
return CValue.none;
}
@@ -913,13 +915,13 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
try o.writeCValue(writer, arg_c_value);
try writer.writeAll(";\n");
} else {
return o.dg.fail(o.dg.decl.src(), "TODO non-explicit inline asm regs", .{});
return o.dg.fail(.{ .node_offset = 0 }, "TODO non-explicit inline asm regs", .{});
}
}
const volatile_string: []const u8 = if (as.is_volatile) "volatile " else "";
try writer.print("__asm {s}(\"{s}\"", .{ volatile_string, as.asm_source });
if (as.output) |_| {
return o.dg.fail(o.dg.decl.src(), "TODO inline asm output", .{});
return o.dg.fail(.{ .node_offset = 0 }, "TODO inline asm output", .{});
}
if (as.inputs.len > 0) {
if (as.output == null) {
@@ -945,7 +947,7 @@ fn genAsm(o: *Object, as: *Inst.Assembly) !CValue {
if (as.base.isUnused())
return CValue.none;
return o.dg.fail(o.dg.decl.src(), "TODO: C backend: inline asm expression result used", .{});
return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: inline asm expression result used", .{});
}
fn genIsNull(o: *Object, inst: *Inst.UnOp) !CValue {
@@ -1072,6 +1074,14 @@ fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue {
return local;
}
fn genIntToError(o: *Object, inst: *Inst.UnOp) !CValue {
return o.resolveInst(inst.operand);
}
fn genErrorToInt(o: *Object, inst: *Inst.UnOp) !CValue {
return o.resolveInst(inst.operand);
}
fn IndentWriter(comptime UnderlyingWriter: type) type {
return struct {
const Self = @This();

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,7 @@ const Type = @import("../type.zig").Type;
const Value = @import("../value.zig").Value;
const Compilation = @import("../Compilation.zig");
const AnyMCValue = @import("../codegen.zig").AnyMCValue;
const LazySrcLoc = Module.LazySrcLoc;
/// Wasm Value, created when generating an instruction
const WValue = union(enum) {
@@ -70,11 +71,9 @@ pub const Context = struct {
}
/// Sets `err_msg` on `Context` and returns `error.CodegemFail` which is caught in link/Wasm.zig
fn fail(self: *Context, src: usize, comptime fmt: []const u8, args: anytype) InnerError {
self.err_msg = try Module.ErrorMsg.create(self.gpa, .{
.file_scope = self.decl.getFileScope(),
.byte_offset = src,
}, fmt, args);
fn fail(self: *Context, src: LazySrcLoc, comptime fmt: []const u8, args: anytype) InnerError {
const src_loc = src.toSrcLocWithDecl(self.decl);
self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, fmt, args);
return error.CodegenFail;
}
@@ -91,7 +90,7 @@ pub const Context = struct {
}
/// Using a given `Type`, returns the corresponding wasm value type
fn genValtype(self: *Context, src: usize, ty: Type) InnerError!u8 {
fn genValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 {
return switch (ty.tag()) {
.f32 => wasm.valtype(.f32),
.f64 => wasm.valtype(.f64),
@@ -104,7 +103,7 @@ pub const Context = struct {
/// Using a given `Type`, returns the corresponding wasm value type
/// Differently from `genValtype` this also allows `void` to create a block
/// with no return type
fn genBlockType(self: *Context, src: usize, ty: Type) InnerError!u8 {
fn genBlockType(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 {
return switch (ty.tag()) {
.void, .noreturn => wasm.block_empty,
else => self.genValtype(src, ty),
@@ -139,7 +138,7 @@ pub const Context = struct {
ty.fnParamTypes(params);
for (params) |param_type| {
// Can we maybe get the source index of each param?
const val_type = try self.genValtype(self.decl.src(), param_type);
const val_type = try self.genValtype(.{ .node_offset = 0 }, param_type);
try writer.writeByte(val_type);
}
}
@@ -151,7 +150,7 @@ pub const Context = struct {
else => |ret_type| {
try leb.writeULEB128(writer, @as(u32, 1));
// Can we maybe get the source index of the return type?
const val_type = try self.genValtype(self.decl.src(), return_type);
const val_type = try self.genValtype(.{ .node_offset = 0 }, return_type);
try writer.writeByte(val_type);
},
}
@@ -168,7 +167,7 @@ pub const Context = struct {
const mod_fn = blk: {
if (tv.val.castTag(.function)) |func| break :blk func.data;
if (tv.val.castTag(.extern_fn)) |ext_fn| return; // don't need codegen for extern functions
return self.fail(self.decl.src(), "TODO: Wasm codegen for decl type '{s}'", .{tv.ty.tag()});
return self.fail(.{ .node_offset = 0 }, "TODO: Wasm codegen for decl type '{s}'", .{tv.ty.tag()});
};
// Reserve space to write the size after generating the code as well as space for locals count

View File

@@ -25,8 +25,7 @@ pub const Inst = struct {
/// lifetimes of operands are encoded elsewhere.
deaths: DeathsInt = undefined,
ty: Type,
/// Byte offset into the source.
src: usize,
src: Module.LazySrcLoc,
pub const DeathsInt = u16;
pub const DeathsBitIndex = std.math.Log2Int(DeathsInt);
@@ -81,22 +80,28 @@ pub const Inst = struct {
condbr,
constant,
dbg_stmt,
// ?T => bool
/// ?T => bool
is_null,
// ?T => bool (inverted logic)
/// ?T => bool (inverted logic)
is_non_null,
// *?T => bool
/// *?T => bool
is_null_ptr,
// *?T => bool (inverted logic)
/// *?T => bool (inverted logic)
is_non_null_ptr,
// E!T => bool
/// E!T => bool
is_err,
// *E!T => bool
/// *E!T => bool
is_err_ptr,
/// E => u16
error_to_int,
/// u16 => E
int_to_error,
bool_and,
bool_or,
/// Read a value from a pointer.
load,
/// A labeled block of code that loops forever. At the end of the body it is implied
/// to repeat; no explicit "repeat" instruction terminates loop bodies.
loop,
ptrtoint,
ref,
@@ -113,9 +118,9 @@ pub const Inst = struct {
not,
floatcast,
intcast,
// ?T => T
/// ?T => T
optional_payload,
// *?T => *T
/// *?T => *T
optional_payload_ptr,
wrap_optional,
/// E!T -> T
@@ -139,7 +144,6 @@ pub const Inst = struct {
.retvoid,
.unreach,
.breakpoint,
.dbg_stmt,
=> NoOp,
.ref,
@@ -152,6 +156,8 @@ pub const Inst = struct {
.is_null_ptr,
.is_err,
.is_err_ptr,
.int_to_error,
.error_to_int,
.ptrtoint,
.floatcast,
.intcast,
@@ -199,6 +205,7 @@ pub const Inst = struct {
.loop => Loop,
.varptr => VarPtr,
.switchbr => SwitchBr,
.dbg_stmt => DbgStmt,
};
}
@@ -360,7 +367,8 @@ pub const Inst = struct {
base: Inst,
asm_source: []const u8,
is_volatile: bool,
output: ?[]const u8,
output: ?*Inst,
output_name: ?[]const u8,
inputs: []const []const u8,
clobbers: []const []const u8,
args: []const *Inst,
@@ -584,8 +592,512 @@ pub const Inst = struct {
return (self.deaths + self.else_index)[0..self.else_deaths];
}
};
pub const DbgStmt = struct {
pub const base_tag = Tag.dbg_stmt;
base: Inst,
byte_offset: u32,
pub fn operandCount(self: *const DbgStmt) usize {
return 0;
}
pub fn getOperand(self: *const DbgStmt, index: usize) ?*Inst {
return null;
}
};
};
pub const Body = struct {
instructions: []*Inst,
};
/// For debugging purposes, prints a function representation to stderr.
pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void {
const allocator = old_module.gpa;
var ctx: DumpTzir = .{
.allocator = allocator,
.arena = std.heap.ArenaAllocator.init(allocator),
.old_module = &old_module,
.module_fn = module_fn,
.indent = 2,
.inst_table = DumpTzir.InstTable.init(allocator),
.partial_inst_table = DumpTzir.InstTable.init(allocator),
.const_table = DumpTzir.InstTable.init(allocator),
};
defer ctx.inst_table.deinit();
defer ctx.partial_inst_table.deinit();
defer ctx.const_table.deinit();
defer ctx.arena.deinit();
switch (module_fn.state) {
.queued => std.debug.print("(queued)", .{}),
.inline_only => std.debug.print("(inline_only)", .{}),
.in_progress => std.debug.print("(in_progress)", .{}),
.sema_failure => std.debug.print("(sema_failure)", .{}),
.dependency_failure => std.debug.print("(dependency_failure)", .{}),
.success => {
const writer = std.io.getStdErr().writer();
ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR");
},
}
}
const DumpTzir = struct {
allocator: *std.mem.Allocator,
arena: std.heap.ArenaAllocator,
old_module: *const Module,
module_fn: *Module.Fn,
indent: usize,
inst_table: InstTable,
partial_inst_table: InstTable,
const_table: InstTable,
next_index: usize = 0,
next_partial_index: usize = 0,
next_const_index: usize = 0,
const InstTable = std.AutoArrayHashMap(*Inst, usize);
/// TODO: Improve this code to include a stack of Body and store the instructions
/// in there. Now we are putting all the instructions in a function local table,
/// however instructions that are in a Body can be thown away when the Body ends.
fn dump(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) !void {
// First pass to pre-populate the table so that we can show even invalid references.
// Must iterate the same order we iterate the second time.
// We also look for constants and put them in the const_table.
try dtz.fetchInstsAndResolveConsts(body);
std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name});
for (dtz.const_table.items()) |entry| {
const constant = entry.key.castTag(.constant).?;
try writer.print(" @{d}: {} = {};\n", .{
entry.value, constant.base.ty, constant.val,
});
}
return dtz.dumpBody(body, writer);
}
fn fetchInstsAndResolveConsts(dtz: *DumpTzir, body: Body) error{OutOfMemory}!void {
for (body.instructions) |inst| {
try dtz.inst_table.put(inst, dtz.next_index);
dtz.next_index += 1;
switch (inst.tag) {
.alloc,
.retvoid,
.unreach,
.breakpoint,
.dbg_stmt,
.arg,
=> {},
.ref,
.ret,
.bitcast,
.not,
.is_non_null,
.is_non_null_ptr,
.is_null,
.is_null_ptr,
.is_err,
.is_err_ptr,
.error_to_int,
.int_to_error,
.ptrtoint,
.floatcast,
.intcast,
.load,
.optional_payload,
.optional_payload_ptr,
.wrap_optional,
.wrap_errunion_payload,
.wrap_errunion_err,
.unwrap_errunion_payload,
.unwrap_errunion_err,
.unwrap_errunion_payload_ptr,
.unwrap_errunion_err_ptr,
=> {
const un_op = inst.cast(Inst.UnOp).?;
try dtz.findConst(un_op.operand);
},
.add,
.addwrap,
.sub,
.subwrap,
.mul,
.mulwrap,
.cmp_lt,
.cmp_lte,
.cmp_eq,
.cmp_gte,
.cmp_gt,
.cmp_neq,
.store,
.bool_and,
.bool_or,
.bit_and,
.bit_or,
.xor,
=> {
const bin_op = inst.cast(Inst.BinOp).?;
try dtz.findConst(bin_op.lhs);
try dtz.findConst(bin_op.rhs);
},
.br => {
const br = inst.castTag(.br).?;
try dtz.findConst(&br.block.base);
try dtz.findConst(br.operand);
},
.br_block_flat => {
const br_block_flat = inst.castTag(.br_block_flat).?;
try dtz.findConst(&br_block_flat.block.base);
try dtz.fetchInstsAndResolveConsts(br_block_flat.body);
},
.br_void => {
const br_void = inst.castTag(.br_void).?;
try dtz.findConst(&br_void.block.base);
},
.block => {
const block = inst.castTag(.block).?;
try dtz.fetchInstsAndResolveConsts(block.body);
},
.condbr => {
const condbr = inst.castTag(.condbr).?;
try dtz.findConst(condbr.condition);
try dtz.fetchInstsAndResolveConsts(condbr.then_body);
try dtz.fetchInstsAndResolveConsts(condbr.else_body);
},
.switchbr => {
const switchbr = inst.castTag(.switchbr).?;
try dtz.findConst(switchbr.target);
try dtz.fetchInstsAndResolveConsts(switchbr.else_body);
for (switchbr.cases) |case| {
try dtz.fetchInstsAndResolveConsts(case.body);
}
},
.loop => {
const loop = inst.castTag(.loop).?;
try dtz.fetchInstsAndResolveConsts(loop.body);
},
.call => {
const call = inst.castTag(.call).?;
try dtz.findConst(call.func);
for (call.args) |arg| {
try dtz.findConst(arg);
}
},
// TODO fill out this debug printing
.assembly,
.constant,
.varptr,
=> {},
}
}
}
fn dumpBody(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void {
for (body.instructions) |inst| {
const my_index = dtz.next_partial_index;
try dtz.partial_inst_table.put(inst, my_index);
dtz.next_partial_index += 1;
try writer.writeByteNTimes(' ', dtz.indent);
try writer.print("%{d}: {} = {s}(", .{
my_index, inst.ty, @tagName(inst.tag),
});
switch (inst.tag) {
.alloc,
.retvoid,
.unreach,
.breakpoint,
.dbg_stmt,
=> try writer.writeAll(")\n"),
.ref,
.ret,
.bitcast,
.not,
.is_non_null,
.is_null,
.is_non_null_ptr,
.is_null_ptr,
.is_err,
.is_err_ptr,
.error_to_int,
.int_to_error,
.ptrtoint,
.floatcast,
.intcast,
.load,
.optional_payload,
.optional_payload_ptr,
.wrap_optional,
.wrap_errunion_err,
.wrap_errunion_payload,
.unwrap_errunion_err,
.unwrap_errunion_payload,
.unwrap_errunion_payload_ptr,
.unwrap_errunion_err_ptr,
=> {
const un_op = inst.cast(Inst.UnOp).?;
const kinky = try dtz.writeInst(writer, un_op.operand);
if (kinky != null) {
try writer.writeAll(") // Instruction does not dominate all uses!\n");
} else {
try writer.writeAll(")\n");
}
},
.add,
.addwrap,
.sub,
.subwrap,
.mul,
.mulwrap,
.cmp_lt,
.cmp_lte,
.cmp_eq,
.cmp_gte,
.cmp_gt,
.cmp_neq,
.store,
.bool_and,
.bool_or,
.bit_and,
.bit_or,
.xor,
=> {
const bin_op = inst.cast(Inst.BinOp).?;
const lhs_kinky = try dtz.writeInst(writer, bin_op.lhs);
try writer.writeAll(", ");
const rhs_kinky = try dtz.writeInst(writer, bin_op.rhs);
if (lhs_kinky != null or rhs_kinky != null) {
try writer.writeAll(") // Instruction does not dominate all uses!");
if (lhs_kinky) |lhs| {
try writer.print(" %{d}", .{lhs});
}
if (rhs_kinky) |rhs| {
try writer.print(" %{d}", .{rhs});
}
try writer.writeAll("\n");
} else {
try writer.writeAll(")\n");
}
},
.arg => {
const arg = inst.castTag(.arg).?;
try writer.print("{s})\n", .{arg.name});
},
.br => {
const br = inst.castTag(.br).?;
const lhs_kinky = try dtz.writeInst(writer, &br.block.base);
try writer.writeAll(", ");
const rhs_kinky = try dtz.writeInst(writer, br.operand);
if (lhs_kinky != null or rhs_kinky != null) {
try writer.writeAll(") // Instruction does not dominate all uses!");
if (lhs_kinky) |lhs| {
try writer.print(" %{d}", .{lhs});
}
if (rhs_kinky) |rhs| {
try writer.print(" %{d}", .{rhs});
}
try writer.writeAll("\n");
} else {
try writer.writeAll(")\n");
}
},
.br_block_flat => {
const br_block_flat = inst.castTag(.br_block_flat).?;
const block_kinky = try dtz.writeInst(writer, &br_block_flat.block.base);
if (block_kinky != null) {
try writer.writeAll(", { // Instruction does not dominate all uses!\n");
} else {
try writer.writeAll(", {\n");
}
const old_indent = dtz.indent;
dtz.indent += 2;
try dtz.dumpBody(br_block_flat.body, writer);
dtz.indent = old_indent;
try writer.writeByteNTimes(' ', dtz.indent);
try writer.writeAll("})\n");
},
.br_void => {
const br_void = inst.castTag(.br_void).?;
const kinky = try dtz.writeInst(writer, &br_void.block.base);
if (kinky) |_| {
try writer.writeAll(") // Instruction does not dominate all uses!\n");
} else {
try writer.writeAll(")\n");
}
},
.block => {
const block = inst.castTag(.block).?;
try writer.writeAll("{\n");
const old_indent = dtz.indent;
dtz.indent += 2;
try dtz.dumpBody(block.body, writer);
dtz.indent = old_indent;
try writer.writeByteNTimes(' ', dtz.indent);
try writer.writeAll("})\n");
},
.condbr => {
const condbr = inst.castTag(.condbr).?;
const condition_kinky = try dtz.writeInst(writer, condbr.condition);
if (condition_kinky != null) {
try writer.writeAll(", { // Instruction does not dominate all uses!\n");
} else {
try writer.writeAll(", {\n");
}
const old_indent = dtz.indent;
dtz.indent += 2;
try dtz.dumpBody(condbr.then_body, writer);
try writer.writeByteNTimes(' ', old_indent);
try writer.writeAll("}, {\n");
try dtz.dumpBody(condbr.else_body, writer);
dtz.indent = old_indent;
try writer.writeByteNTimes(' ', old_indent);
try writer.writeAll("})\n");
},
.switchbr => {
const switchbr = inst.castTag(.switchbr).?;
const condition_kinky = try dtz.writeInst(writer, switchbr.target);
if (condition_kinky != null) {
try writer.writeAll(", { // Instruction does not dominate all uses!\n");
} else {
try writer.writeAll(", {\n");
}
const old_indent = dtz.indent;
if (switchbr.else_body.instructions.len != 0) {
dtz.indent += 2;
try dtz.dumpBody(switchbr.else_body, writer);
try writer.writeByteNTimes(' ', old_indent);
try writer.writeAll("}, {\n");
dtz.indent = old_indent;
}
for (switchbr.cases) |case| {
dtz.indent += 2;
try dtz.dumpBody(case.body, writer);
try writer.writeByteNTimes(' ', old_indent);
try writer.writeAll("}, {\n");
dtz.indent = old_indent;
}
try writer.writeByteNTimes(' ', old_indent);
try writer.writeAll("})\n");
},
.loop => {
const loop = inst.castTag(.loop).?;
try writer.writeAll("{\n");
const old_indent = dtz.indent;
dtz.indent += 2;
try dtz.dumpBody(loop.body, writer);
dtz.indent = old_indent;
try writer.writeByteNTimes(' ', dtz.indent);
try writer.writeAll("})\n");
},
.call => {
const call = inst.castTag(.call).?;
const args_kinky = try dtz.allocator.alloc(?usize, call.args.len);
defer dtz.allocator.free(args_kinky);
std.mem.set(?usize, args_kinky, null);
var any_kinky_args = false;
const func_kinky = try dtz.writeInst(writer, call.func);
for (call.args) |arg, i| {
try writer.writeAll(", ");
args_kinky[i] = try dtz.writeInst(writer, arg);
any_kinky_args = any_kinky_args or args_kinky[i] != null;
}
if (func_kinky != null or any_kinky_args) {
try writer.writeAll(") // Instruction does not dominate all uses!");
if (func_kinky) |func_index| {
try writer.print(" %{d}", .{func_index});
}
for (args_kinky) |arg_kinky| {
if (arg_kinky) |arg_index| {
try writer.print(" %{d}", .{arg_index});
}
}
try writer.writeAll("\n");
} else {
try writer.writeAll(")\n");
}
},
// TODO fill out this debug printing
.assembly,
.constant,
.varptr,
=> {
try writer.writeAll("!TODO!)\n");
},
}
}
}
fn writeInst(dtz: *DumpTzir, writer: std.fs.File.Writer, inst: *Inst) !?usize {
if (dtz.partial_inst_table.get(inst)) |operand_index| {
try writer.print("%{d}", .{operand_index});
return null;
} else if (dtz.const_table.get(inst)) |operand_index| {
try writer.print("@{d}", .{operand_index});
return null;
} else if (dtz.inst_table.get(inst)) |operand_index| {
try writer.print("%{d}", .{operand_index});
return operand_index;
} else {
try writer.writeAll("!BADREF!");
return null;
}
}
fn findConst(dtz: *DumpTzir, operand: *Inst) !void {
if (operand.tag == .constant) {
try dtz.const_table.put(operand, dtz.next_const_index);
dtz.next_const_index += 1;
}
}
};

View File

@@ -185,8 +185,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
if (module.global_error_set.size == 0) break :render_errors;
var it = module.global_error_set.iterator();
while (it.next()) |entry| {
// + 1 because 0 represents no error
try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value + 1 });
try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value });
}
try err_typedef_writer.writeByte('\n');
}

View File

@@ -34,7 +34,7 @@ pub const base_tag: link.File.Tag = .coff;
const msdos_stub = @embedFile("msdos-stub.bin");
/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
llvm_ir_module: ?*llvm_backend.LLVMIRModule = null,
llvm_object: ?*llvm_backend.Object = null,
base: link.File,
ptr_width: PtrWidth,
@@ -129,7 +129,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
const self = try createEmpty(allocator, options);
errdefer self.base.destroy();
self.llvm_ir_module = try llvm_backend.LLVMIRModule.create(allocator, sub_path, options);
self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
return self;
}
@@ -413,7 +413,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff {
}
pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void {
if (self.llvm_ir_module) |_| return;
if (self.llvm_object) |_| return;
try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
@@ -660,7 +660,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
defer tracy.end();
if (build_options.have_llvm)
if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.updateDecl(module, decl);
if (self.llvm_object) |llvm_object| return try llvm_object.updateDecl(module, decl);
const typed_value = decl.typed_value.most_recent.typed_value;
if (typed_value.val.tag() == .extern_fn) {
@@ -720,15 +720,15 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void {
}
pub fn freeDecl(self: *Coff, decl: *Module.Decl) void {
if (self.llvm_ir_module) |_| return;
if (self.llvm_object) |_| return;
// Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
self.freeTextBlock(&decl.link.coff);
self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {};
}
pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void {
if (self.llvm_ir_module) |_| return;
pub fn updateDeclExports(self: *Coff, module: *Module, decl: *Module.Decl, exports: []const *Module.Export) !void {
if (self.llvm_object) |_| return;
for (exports) |exp| {
if (exp.options.section) |section_name| {
@@ -771,7 +771,7 @@ pub fn flushModule(self: *Coff, comp: *Compilation) !void {
defer tracy.end();
if (build_options.have_llvm)
if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.flushModule(comp);
if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);
if (self.text_section_size_dirty) {
// Write the new raw size in the .text header
@@ -1308,7 +1308,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
}
pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 {
assert(self.llvm_ir_module == null);
assert(self.llvm_object == null);
return self.text_section_virtual_address + decl.link.coff.text_offset;
}
@@ -1318,7 +1318,7 @@ pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !v
pub fn deinit(self: *Coff) void {
if (build_options.have_llvm)
if (self.llvm_ir_module) |ir_module| ir_module.deinit(self.base.allocator);
if (self.llvm_object) |ir_module| ir_module.deinit(self.base.allocator);
self.text_block_free_list.deinit(self.base.allocator);
self.offset_table.deinit(self.base.allocator);

View File

@@ -35,7 +35,7 @@ base: File,
ptr_width: PtrWidth,
/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
llvm_ir_module: ?*llvm_backend.LLVMIRModule = null,
llvm_object: ?*llvm_backend.Object = null,
/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
/// Same order as in the file.
@@ -232,7 +232,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
const self = try createEmpty(allocator, options);
errdefer self.base.destroy();
self.llvm_ir_module = try llvm_backend.LLVMIRModule.create(allocator, sub_path, options);
self.llvm_object = try llvm_backend.Object.create(allocator, sub_path, options);
return self;
}
@@ -299,7 +299,7 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {
pub fn deinit(self: *Elf) void {
if (build_options.have_llvm)
if (self.llvm_ir_module) |ir_module|
if (self.llvm_object) |ir_module|
ir_module.deinit(self.base.allocator);
self.sections.deinit(self.base.allocator);
@@ -318,7 +318,7 @@ pub fn deinit(self: *Elf) void {
}
pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl) u64 {
assert(self.llvm_ir_module == null);
assert(self.llvm_object == null);
assert(decl.link.elf.local_sym_index != 0);
return self.local_symbols.items[decl.link.elf.local_sym_index].st_value;
}
@@ -438,7 +438,7 @@ fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 {
}
pub fn populateMissingMetadata(self: *Elf) !void {
assert(self.llvm_ir_module == null);
assert(self.llvm_object == null);
const small_ptr = switch (self.ptr_width) {
.p32 => true,
@@ -745,7 +745,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
defer tracy.end();
if (build_options.have_llvm)
if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.flushModule(comp);
if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);
// TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
// Zig source code.
@@ -2111,7 +2111,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
}
pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
if (self.llvm_ir_module) |_| return;
if (self.llvm_object) |_| return;
if (decl.link.elf.local_sym_index != 0) return;
@@ -2149,7 +2149,7 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
}
pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
if (self.llvm_ir_module) |_| return;
if (self.llvm_object) |_| return;
// Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
self.freeTextBlock(&decl.link.elf);
@@ -2189,7 +2189,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
defer tracy.end();
if (build_options.have_llvm)
if (self.llvm_ir_module) |llvm_ir_module| return try llvm_ir_module.updateDecl(module, decl);
if (self.llvm_object) |llvm_object| return try llvm_object.updateDecl(module, decl);
const typed_value = decl.typed_value.most_recent.typed_value;
if (typed_value.val.tag() == .extern_fn) {
@@ -2670,10 +2670,10 @@ fn writeDeclDebugInfo(self: *Elf, text_block: *TextBlock, dbg_info_buf: []const
pub fn updateDeclExports(
self: *Elf,
module: *Module,
decl: *const Module.Decl,
decl: *Module.Decl,
exports: []const *Module.Export,
) !void {
if (self.llvm_ir_module) |_| return;
if (self.llvm_object) |_| return;
const tracy = trace(@src());
defer tracy.end();
@@ -2748,7 +2748,7 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec
const tracy = trace(@src());
defer tracy.end();
if (self.llvm_ir_module) |_| return;
if (self.llvm_object) |_| return;
const tree = decl.container.file_scope.tree;
const node_tags = tree.nodes.items(.tag);
@@ -2773,7 +2773,7 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec
}
pub fn deleteExport(self: *Elf, exp: Export) void {
if (self.llvm_ir_module) |_| return;
if (self.llvm_object) |_| return;
const sym_index = exp.sym_index orelse return;
self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};

View File

@@ -1340,7 +1340,7 @@ pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.D
pub fn updateDeclExports(
self: *MachO,
module: *Module,
decl: *const Module.Decl,
decl: *Module.Decl,
exports: []const *Module.Export,
) !void {
const tracy = trace(@src());

View File

@@ -1487,7 +1487,7 @@ fn buildOutputType(
for (diags.arch.?.allCpuModels()) |cpu| {
help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help;
}
std.log.info("Available CPUs for architecture '{s}': {s}", .{
std.log.info("Available CPUs for architecture '{s}':\n{s}", .{
@tagName(diags.arch.?), help_text.items,
});
}
@@ -1499,7 +1499,7 @@ fn buildOutputType(
for (diags.arch.?.allFeaturesList()) |feature| {
help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help;
}
std.log.info("Available CPU features for architecture '{s}': {s}", .{
std.log.info("Available CPU features for architecture '{s}':\n{s}", .{
@tagName(diags.arch.?), help_text.items,
});
}
@@ -1750,15 +1750,12 @@ fn buildOutputType(
}
const self_exe_path = try fs.selfExePathAlloc(arena);
var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir|
.{
.path = lib_dir,
.handle = try fs.cwd().openDir(lib_dir, .{}),
}
else
introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
};
var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{
.path = lib_dir,
.handle = try fs.cwd().openDir(lib_dir, .{}),
} else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
};
defer zig_lib_directory.handle.close();
var thread_pool: ThreadPool = undefined;
@@ -2461,15 +2458,12 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
}
}
var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir|
.{
.path = lib_dir,
.handle = try fs.cwd().openDir(lib_dir, .{}),
}
else
introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
};
var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{
.path = lib_dir,
.handle = try fs.cwd().openDir(lib_dir, .{}),
} else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
fatal("unable to find zig installation directory: {s}", .{@errorName(err)});
};
defer zig_lib_directory.handle.close();
const std_special = "std" ++ fs.path.sep_str ++ "special";
@@ -3281,8 +3275,7 @@ pub const ClangArgIterator = struct {
self.zig_equivalent = clang_arg.zig_equivalent;
break :find_clang_arg;
},
}
else {
} else {
fatal("Unknown Clang option: '{s}'", .{arg});
}
}

View File

@@ -4343,7 +4343,7 @@ fn isZigPrimitiveType(name: []const u8) bool {
}
return true;
}
return @import("astgen.zig").simple_types.has(name);
return @import("AstGen.zig").simple_types.has(name);
}
const MacroCtx = struct {

View File

@@ -92,11 +92,7 @@ pub const Type = extern union {
.anyerror_void_error_union, .error_union => return .ErrorUnion,
.anyframe_T, .@"anyframe" => return .AnyFrame,
.@"struct", .empty_struct => return .Struct,
.@"enum" => return .Enum,
.@"union" => return .Union,
.empty_struct => return .Struct,
.var_args_param => unreachable, // can be any type
}
@@ -173,6 +169,125 @@ pub const Type = extern union {
};
}
pub fn ptrInfo(self: Type) Payload.Pointer {
switch (self.tag()) {
.single_const_pointer_to_comptime_int => return .{ .data = .{
.pointee_type = Type.initTag(.comptime_int),
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = false,
.@"volatile" = false,
.size = .One,
} },
.const_slice_u8 => return .{ .data = .{
.pointee_type = Type.initTag(.u8),
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = false,
.@"volatile" = false,
.size = .Slice,
} },
.single_const_pointer => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = false,
.@"volatile" = false,
.size = .One,
} },
.single_mut_pointer => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = true,
.@"volatile" = false,
.size = .One,
} },
.many_const_pointer => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = false,
.@"volatile" = false,
.size = .Many,
} },
.many_mut_pointer => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = true,
.@"volatile" = false,
.size = .Many,
} },
.c_const_pointer => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = false,
.@"volatile" = false,
.size = .C,
} },
.c_mut_pointer => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = true,
.@"volatile" = false,
.size = .C,
} },
.const_slice => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = false,
.@"volatile" = false,
.size = .Slice,
} },
.mut_slice => return .{ .data = .{
.pointee_type = self.castPointer().?.data,
.sentinel = null,
.@"align" = 0,
.bit_offset = 0,
.host_size = 0,
.@"allowzero" = false,
.mutable = true,
.@"volatile" = false,
.size = .Slice,
} },
.pointer => return self.castTag(.pointer).?.*,
else => unreachable,
}
}
pub fn eql(a: Type, b: Type) bool {
// As a shortcut, if the small tags / addresses match, we're done.
if (a.tag_if_small_enough == b.tag_if_small_enough)
@@ -195,25 +310,38 @@ pub const Type = extern union {
return a.elemType().eql(b.elemType());
},
.Pointer => {
// Hot path for common case:
if (a.castPointer()) |a_payload| {
if (b.castPointer()) |b_payload| {
return a.tag() == b.tag() and eql(a_payload.data, b_payload.data);
const info_a = a.ptrInfo().data;
const info_b = b.ptrInfo().data;
if (!info_a.pointee_type.eql(info_b.pointee_type))
return false;
if (info_a.size != info_b.size)
return false;
if (info_a.mutable != info_b.mutable)
return false;
if (info_a.@"volatile" != info_b.@"volatile")
return false;
if (info_a.@"allowzero" != info_b.@"allowzero")
return false;
if (info_a.bit_offset != info_b.bit_offset)
return false;
if (info_a.host_size != info_b.host_size)
return false;
const sentinel_a = info_a.sentinel;
const sentinel_b = info_b.sentinel;
if (sentinel_a) |sa| {
if (sentinel_b) |sb| {
if (!sa.eql(sb))
return false;
} else {
return false;
}
} else {
if (sentinel_b != null)
return false;
}
const is_slice_a = isSlice(a);
const is_slice_b = isSlice(b);
if (is_slice_a != is_slice_b)
return false;
const ptr_size_a = ptrSize(a);
const ptr_size_b = ptrSize(b);
if (ptr_size_a != ptr_size_b)
return false;
std.debug.panic("TODO implement more pointer Type equality comparison: {} and {}", .{
a, b,
});
return true;
},
.Int => {
// Detect that e.g. u64 != usize, even if the bits match on a particular target.
@@ -399,7 +527,6 @@ pub const Type = extern union {
.const_slice_u8,
.enum_literal,
.anyerror_void_error_union,
.@"anyframe",
.inferred_alloc_const,
.inferred_alloc_mut,
.var_args_param,
@@ -420,7 +547,6 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.anyframe_T,
=> return self.copyPayloadShallow(allocator, Payload.ElemType),
.int_signed,
@@ -480,13 +606,10 @@ pub const Type = extern union {
.payload = try payload.payload.copy(allocator),
});
},
.error_set => return self.copyPayloadShallow(allocator, Payload.Decl),
.error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
.error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),
.empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope),
.@"enum" => return self.copyPayloadShallow(allocator, Payload.Enum),
.@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),
.@"union" => return self.copyPayloadShallow(allocator, Payload.Union),
.@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque),
}
}
@@ -551,7 +674,6 @@ pub const Type = extern union {
// TODO this should print the structs name
.empty_struct => return out_stream.writeAll("struct {}"),
.@"anyframe" => return out_stream.writeAll("anyframe"),
.anyerror_void_error_union => return out_stream.writeAll("anyerror!void"),
.const_slice_u8 => return out_stream.writeAll("[]const u8"),
.fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
@@ -579,12 +701,6 @@ pub const Type = extern union {
continue;
},
.anyframe_T => {
const return_type = ty.castTag(.anyframe_T).?.data;
try out_stream.print("anyframe->", .{});
ty = return_type;
continue;
},
.array_u8 => {
const len = ty.castTag(.array_u8).?.data;
return out_stream.print("[{d}]u8", .{len});
@@ -715,8 +831,8 @@ pub const Type = extern union {
continue;
},
.error_set => {
const decl = ty.castTag(.error_set).?.data;
return out_stream.writeAll(std.mem.spanZ(decl.name));
const error_set = ty.castTag(.error_set).?.data;
return out_stream.writeAll(std.mem.spanZ(error_set.owner_decl.name));
},
.error_set_single => {
const name = ty.castTag(.error_set_single).?.data;
@@ -725,9 +841,6 @@ pub const Type = extern union {
.inferred_alloc_const => return out_stream.writeAll("(inferred_alloc_const)"),
.inferred_alloc_mut => return out_stream.writeAll("(inferred_alloc_mut)"),
// TODO use declaration name
.@"enum" => return out_stream.writeAll("enum {}"),
.@"struct" => return out_stream.writeAll("struct {}"),
.@"union" => return out_stream.writeAll("union {}"),
.@"opaque" => return out_stream.writeAll("opaque {}"),
}
unreachable;
@@ -822,8 +935,6 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
@@ -839,10 +950,6 @@ pub const Type = extern union {
return payload.error_set.hasCodeGenBits() or payload.payload.hasCodeGenBits();
},
.@"enum" => @panic("TODO"),
.@"struct" => @panic("TODO"),
.@"union" => @panic("TODO"),
.c_void,
.void,
.type,
@@ -863,7 +970,39 @@ pub const Type = extern union {
}
pub fn isNoReturn(self: Type) bool {
return self.zigTypeTag() == .NoReturn;
const definitely_correct_result = self.zigTypeTag() == .NoReturn;
const fast_result = self.tag_if_small_enough == @enumToInt(Tag.noreturn);
assert(fast_result == definitely_correct_result);
return fast_result;
}
pub fn ptrAlignment(self: Type, target: Target) u32 {
switch (self.tag()) {
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
.optional_single_const_pointer,
.optional_single_mut_pointer,
=> return self.cast(Payload.ElemType).?.data.abiAlignment(target),
.const_slice_u8 => return 1,
.pointer => {
const ptr_info = self.castTag(.pointer).?.data;
if (ptr_info.@"align" != 0) {
return ptr_info.@"align";
} else {
return ptr_info.pointee_type.abiAlignment();
}
},
else => unreachable,
}
}
/// Asserts that hasCodeGenBits() is true.
@@ -907,17 +1046,9 @@ pub const Type = extern union {
.mut_slice,
.optional_single_const_pointer,
.optional_single_mut_pointer,
.@"anyframe",
.anyframe_T,
.pointer,
=> return @divExact(target.cpu.arch.ptrBitWidth(), 8),
.pointer => {
const payload = self.castTag(.pointer).?.data;
if (payload.@"align" != 0) return payload.@"align";
return @divExact(target.cpu.arch.ptrBitWidth(), 8);
},
.c_short => return @divExact(CType.short.sizeInBits(target), 8),
.c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
.c_int => return @divExact(CType.int.sizeInBits(target), 8),
@@ -967,10 +1098,6 @@ pub const Type = extern union {
@panic("TODO abiAlignment error union");
},
.@"enum" => self.cast(Payload.Enum).?.abiAlignment(target),
.@"struct" => @panic("TODO"),
.@"union" => @panic("TODO"),
.c_void,
.void,
.type,
@@ -1038,7 +1165,7 @@ pub const Type = extern union {
.i64, .u64 => return 8,
.u128, .i128 => return 16,
.@"anyframe", .anyframe_T, .isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
.isize, .usize => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
.const_slice,
.mut_slice,
@@ -1119,10 +1246,6 @@ pub const Type = extern union {
}
@panic("TODO abiSize error union");
},
.@"enum" => @panic("TODO"),
.@"struct" => @panic("TODO"),
.@"union" => @panic("TODO"),
};
}
@@ -1186,15 +1309,10 @@ pub const Type = extern union {
.const_slice,
.mut_slice,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> false,
@@ -1264,15 +1382,10 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> unreachable,
@@ -1361,17 +1474,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> false,
@@ -1442,17 +1550,12 @@ pub const Type = extern union {
.enum_literal,
.mut_slice,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> false,
@@ -1532,17 +1635,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> false,
@@ -1617,17 +1715,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> false,
@@ -1744,17 +1837,12 @@ pub const Type = extern union {
.optional_single_mut_pointer => unreachable,
.enum_literal => unreachable,
.error_union => unreachable,
.@"anyframe" => unreachable,
.anyframe_T => unreachable,
.anyerror_void_error_union => unreachable,
.error_set => unreachable,
.error_set_single => unreachable,
.empty_struct => unreachable,
.inferred_alloc_const => unreachable,
.inferred_alloc_mut => unreachable,
.@"enum" => unreachable,
.@"struct" => unreachable,
.@"union" => unreachable,
.@"opaque" => unreachable,
.var_args_param => unreachable,
@@ -1897,17 +1985,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> unreachable,
@@ -1972,17 +2055,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> unreachable,
@@ -2062,17 +2140,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> false,
@@ -2148,17 +2221,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> false,
@@ -2220,17 +2288,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> unreachable,
@@ -2320,17 +2383,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> false,
@@ -2441,17 +2499,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> unreachable,
@@ -2528,17 +2581,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> unreachable,
@@ -2614,17 +2662,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> unreachable,
@@ -2700,17 +2743,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> unreachable,
@@ -2783,17 +2821,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> unreachable,
@@ -2866,17 +2899,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> unreachable,
@@ -2949,17 +2977,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> false,
@@ -3016,8 +3039,6 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.anyerror_void_error_union,
.anyframe_T,
.@"anyframe",
.error_union,
.error_set,
.error_set_single,
@@ -3025,10 +3046,6 @@ pub const Type = extern union {
.var_args_param,
=> return null,
.@"enum" => @panic("TODO onePossibleValue enum"),
.@"struct" => @panic("TODO onePossibleValue struct"),
.@"union" => @panic("TODO onePossibleValue union"),
.empty_struct => return Value.initTag(.empty_struct_value),
.void => return Value.initTag(.void_value),
.noreturn => return Value.initTag(.unreachable_value),
@@ -3128,17 +3145,12 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
.empty_struct,
.inferred_alloc_const,
.inferred_alloc_mut,
.@"enum",
.@"struct",
.@"union",
.@"opaque",
.var_args_param,
=> return false,
@@ -3220,8 +3232,6 @@ pub const Type = extern union {
.optional_single_const_pointer,
.enum_literal,
.error_union,
.@"anyframe",
.anyframe_T,
.anyerror_void_error_union,
.error_set,
.error_set_single,
@@ -3234,10 +3244,7 @@ pub const Type = extern union {
=> unreachable,
.empty_struct => self.castTag(.empty_struct).?.data,
.@"enum" => &self.castTag(.@"enum").?.scope,
.@"struct" => &self.castTag(.@"struct").?.scope,
.@"union" => &self.castTag(.@"union").?.scope,
.@"opaque" => &self.castTag(.@"opaque").?.scope,
.@"opaque" => &self.castTag(.@"opaque").?.data,
};
}
@@ -3296,6 +3303,10 @@ pub const Type = extern union {
}
}
pub fn isExhaustiveEnum(ty: Type) bool {
return false; // TODO
}
/// This enum does not directly correspond to `std.builtin.TypeId` because
/// it has extra enum tags in it, as a way of using less memory. For example,
/// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
@@ -3346,7 +3357,6 @@ pub const Type = extern union {
fn_ccc_void_no_args,
single_const_pointer_to_comptime_int,
anyerror_void_error_union,
@"anyframe",
const_slice_u8,
/// This is a special type for variadic parameters of a function call.
/// Casts to it will validate that the type can be passed to a c calling convetion function.
@@ -3379,13 +3389,9 @@ pub const Type = extern union {
optional_single_mut_pointer,
optional_single_const_pointer,
error_union,
anyframe_T,
error_set,
error_set_single,
empty_struct,
@"enum",
@"struct",
@"union",
@"opaque",
pub const last_no_payload_tag = Tag.inferred_alloc_const;
@@ -3435,7 +3441,6 @@ pub const Type = extern union {
.fn_ccc_void_no_args,
.single_const_pointer_to_comptime_int,
.anyerror_void_error_union,
.@"anyframe",
.const_slice_u8,
.inferred_alloc_const,
.inferred_alloc_mut,
@@ -3457,25 +3462,22 @@ pub const Type = extern union {
.optional,
.optional_single_mut_pointer,
.optional_single_const_pointer,
.anyframe_T,
=> Payload.ElemType,
.int_signed,
.int_unsigned,
=> Payload.Bits,
.error_set => Payload.ErrorSet,
.array => Payload.Array,
.array_sentinel => Payload.ArraySentinel,
.pointer => Payload.Pointer,
.function => Payload.Function,
.error_union => Payload.ErrorUnion,
.error_set => Payload.Decl,
.error_set_single => Payload.Name,
.empty_struct => Payload.ContainerScope,
.@"enum" => Payload.Enum,
.@"struct" => Payload.Struct,
.@"union" => Payload.Union,
.@"opaque" => Payload.Opaque,
.empty_struct => Payload.ContainerScope,
};
}
@@ -3550,6 +3552,13 @@ pub const Type = extern union {
},
};
pub const ErrorSet = struct {
pub const base_tag = Tag.error_set;
base: Payload = Payload{ .tag = base_tag },
data: *Module.ErrorSet,
};
pub const Pointer = struct {
pub const base_tag = Tag.pointer;
@@ -3598,13 +3607,8 @@ pub const Type = extern union {
pub const Opaque = struct {
base: Payload = .{ .tag = .@"opaque" },
scope: Module.Scope.Container,
data: Module.Scope.Container,
};
pub const Enum = @import("type/Enum.zig");
pub const Struct = @import("type/Struct.zig");
pub const Union = @import("type/Union.zig");
};
};

View File

@@ -1,55 +0,0 @@
const std = @import("std");
const zir = @import("../zir.zig");
const Value = @import("../value.zig").Value;
const Type = @import("../type.zig").Type;
const Module = @import("../Module.zig");
const Scope = Module.Scope;
const Enum = @This();
base: Type.Payload = .{ .tag = .@"enum" },
analysis: union(enum) {
queued: Zir,
in_progress,
resolved: Size,
failed,
},
scope: Scope.Container,
pub const Field = struct {
value: Value,
};
pub const Zir = struct {
body: zir.Body,
inst: *zir.Inst,
};
pub const Size = struct {
tag_type: Type,
fields: std.StringArrayHashMapUnmanaged(Field),
};
pub fn resolve(self: *Enum, mod: *Module, scope: *Scope) !void {
const zir = switch (self.analysis) {
.failed => return error.AnalysisFail,
.resolved => return,
.in_progress => {
return mod.fail(scope, src, "enum '{}' depends on itself", .{enum_name});
},
.queued => |zir| zir,
};
self.analysis = .in_progress;
// TODO
}
// TODO should this resolve the type or assert that it has already been resolved?
pub fn abiAlignment(self: *Enum, target: std.Target) u32 {
switch (self.analysis) {
.queued => unreachable, // alignment has not been resolved
.in_progress => unreachable, // alignment has not been resolved
.failed => unreachable, // type resolution failed
.resolved => |r| return r.tag_type.abiAlignment(target),
}
}

View File

@@ -1,56 +0,0 @@
const std = @import("std");
const zir = @import("../zir.zig");
const Value = @import("../value.zig").Value;
const Type = @import("../type.zig").Type;
const Module = @import("../Module.zig");
const Scope = Module.Scope;
const Struct = @This();
base: Type.Payload = .{ .tag = .@"struct" },
analysis: union(enum) {
queued: Zir,
zero_bits_in_progress,
zero_bits: Zero,
in_progress,
// alignment: Align,
resolved: Size,
failed,
},
scope: Scope.Container,
pub const Field = struct {
value: Value,
};
pub const Zir = struct {
body: zir.Body,
inst: *zir.Inst,
};
pub const Zero = struct {
is_zero_bits: bool,
fields: std.StringArrayHashMapUnmanaged(Field),
};
pub const Size = struct {
is_zero_bits: bool,
alignment: u32,
size: u32,
fields: std.StringArrayHashMapUnmanaged(Field),
};
pub fn resolveZeroBits(self: *Struct, mod: *Module, scope: *Scope) !void {
const zir = switch (self.analysis) {
.failed => return error.AnalysisFail,
.zero_bits_in_progress => {
return mod.fail(scope, src, "struct '{}' depends on itself", .{});
},
.queued => |zir| zir,
else => return,
};
self.analysis = .zero_bits_in_progress;
// TODO
}

View File

@@ -1,56 +0,0 @@
const std = @import("std");
const zir = @import("../zir.zig");
const Value = @import("../value.zig").Value;
const Type = @import("../type.zig").Type;
const Module = @import("../Module.zig");
const Scope = Module.Scope;
const Union = @This();
base: Type.Payload = .{ .tag = .@"struct" },
analysis: union(enum) {
queued: Zir,
zero_bits_in_progress,
zero_bits: Zero,
in_progress,
// alignment: Align,
resolved: Size,
failed,
},
scope: Scope.Container,
pub const Field = struct {
value: Value,
};
pub const Zir = struct {
body: zir.Body,
inst: *zir.Inst,
};
pub const Zero = struct {
is_zero_bits: bool,
fields: std.StringArrayHashMapUnmanaged(Field),
};
pub const Size = struct {
is_zero_bits: bool,
alignment: u32,
size: u32,
fields: std.StringArrayHashMapUnmanaged(Field),
};
pub fn resolveZeroBits(self: *Union, mod: *Module, scope: *Scope) !void {
const zir = switch (self.analysis) {
.failed => return error.AnalysisFail,
.zero_bits_in_progress => {
return mod.fail(scope, src, "union '{}' depends on itself", .{});
},
.queued => |zir| zir,
else => return,
};
self.analysis = .zero_bits_in_progress;
// TODO
}

View File

@@ -30,6 +30,8 @@ pub const Value = extern union {
i32_type,
u64_type,
i64_type,
u128_type,
i128_type,
usize_type,
isize_type,
c_short_type,
@@ -62,18 +64,18 @@ pub const Value = extern union {
single_const_pointer_to_comptime_int_type,
const_slice_u8_type,
enum_literal_type,
anyframe_type,
undef,
zero,
one,
void_value,
unreachable_value,
empty_struct_value,
empty_array,
null_value,
bool_true,
bool_false, // See last_no_payload_tag below.
bool_false,
empty_struct_value,
empty_array, // See last_no_payload_tag below.
// After this, the tag requires a payload.
ty,
@@ -100,14 +102,13 @@ pub const Value = extern union {
float_64,
float_128,
enum_literal,
error_set,
@"error",
error_union,
/// This is a special value that tracks a set of types that have been stored
/// to an inferred allocation. It does not support any of the normal value queries.
inferred_alloc,
pub const last_no_payload_tag = Tag.bool_false;
pub const last_no_payload_tag = Tag.empty_array;
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
pub fn Type(comptime t: Tag) type {
@@ -120,6 +121,8 @@ pub const Value = extern union {
.i32_type,
.u64_type,
.i64_type,
.u128_type,
.i128_type,
.usize_type,
.isize_type,
.c_short_type,
@@ -152,7 +155,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.enum_literal_type,
.anyframe_type,
.undef,
.zero,
.one,
@@ -193,7 +195,6 @@ pub const Value = extern union {
.float_32 => Payload.Float_32,
.float_64 => Payload.Float_64,
.float_128 => Payload.Float_128,
.error_set => Payload.ErrorSet,
.@"error" => Payload.Error,
.inferred_alloc => Payload.InferredAlloc,
};
@@ -275,6 +276,8 @@ pub const Value = extern union {
.i32_type,
.u64_type,
.i64_type,
.u128_type,
.i128_type,
.usize_type,
.isize_type,
.c_short_type,
@@ -307,7 +310,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.enum_literal_type,
.anyframe_type,
.undef,
.zero,
.one,
@@ -400,7 +402,6 @@ pub const Value = extern union {
return Value{ .ptr_otherwise = &new_payload.base };
},
.error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
.inferred_alloc => unreachable,
}
}
@@ -429,6 +430,8 @@ pub const Value = extern union {
.i32_type => return out_stream.writeAll("i32"),
.u64_type => return out_stream.writeAll("u64"),
.i64_type => return out_stream.writeAll("i64"),
.u128_type => return out_stream.writeAll("u128"),
.i128_type => return out_stream.writeAll("i128"),
.isize_type => return out_stream.writeAll("isize"),
.usize_type => return out_stream.writeAll("usize"),
.c_short_type => return out_stream.writeAll("c_short"),
@@ -461,7 +464,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
.const_slice_u8_type => return out_stream.writeAll("[]const u8"),
.enum_literal_type => return out_stream.writeAll("@Type(.EnumLiteral)"),
.anyframe_type => return out_stream.writeAll("anyframe"),
// TODO this should print `NAME{}`
.empty_struct_value => return out_stream.writeAll("struct {}{}"),
@@ -510,15 +512,6 @@ pub const Value = extern union {
.float_32 => return out_stream.print("{}", .{val.castTag(.float_32).?.data}),
.float_64 => return out_stream.print("{}", .{val.castTag(.float_64).?.data}),
.float_128 => return out_stream.print("{}", .{val.castTag(.float_128).?.data}),
.error_set => {
const error_set = val.castTag(.error_set).?.data;
try out_stream.writeAll("error{");
var it = error_set.fields.iterator();
while (it.next()) |entry| {
try out_stream.print("{},", .{entry.value});
}
return out_stream.writeAll("}");
},
.@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}),
// TODO to print this it should be error{ Set, Items }!T(val), but we need the type for that
.error_union => return out_stream.print("error_union_val({})", .{val.castTag(.error_union).?.data}),
@@ -557,6 +550,8 @@ pub const Value = extern union {
.i32_type => Type.initTag(.i32),
.u64_type => Type.initTag(.u64),
.i64_type => Type.initTag(.i64),
.u128_type => Type.initTag(.u128),
.i128_type => Type.initTag(.i128),
.usize_type => Type.initTag(.usize),
.isize_type => Type.initTag(.isize),
.c_short_type => Type.initTag(.c_short),
@@ -589,7 +584,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
.const_slice_u8_type => Type.initTag(.const_slice_u8),
.enum_literal_type => Type.initTag(.enum_literal),
.anyframe_type => Type.initTag(.@"anyframe"),
.int_type => {
const payload = self.castTag(.int_type).?.data;
@@ -602,10 +596,6 @@ pub const Value = extern union {
};
return Type.initPayload(&new.base);
},
.error_set => {
const payload = self.castTag(.error_set).?.data;
return Type.Tag.error_set.create(allocator, payload.decl);
},
.undef,
.zero,
@@ -654,6 +644,8 @@ pub const Value = extern union {
.i32_type,
.u64_type,
.i64_type,
.u128_type,
.i128_type,
.usize_type,
.isize_type,
.c_short_type,
@@ -686,7 +678,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.enum_literal_type,
.anyframe_type,
.null_value,
.function,
.extern_fn,
@@ -704,7 +695,6 @@ pub const Value = extern union {
.unreachable_value,
.empty_array,
.enum_literal,
.error_set,
.error_union,
.@"error",
.empty_struct_value,
@@ -741,6 +731,8 @@ pub const Value = extern union {
.i32_type,
.u64_type,
.i64_type,
.u128_type,
.i128_type,
.usize_type,
.isize_type,
.c_short_type,
@@ -773,7 +765,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.enum_literal_type,
.anyframe_type,
.null_value,
.function,
.extern_fn,
@@ -791,7 +782,6 @@ pub const Value = extern union {
.unreachable_value,
.empty_array,
.enum_literal,
.error_set,
.@"error",
.error_union,
.empty_struct_value,
@@ -828,6 +818,8 @@ pub const Value = extern union {
.i32_type,
.u64_type,
.i64_type,
.u128_type,
.i128_type,
.usize_type,
.isize_type,
.c_short_type,
@@ -860,7 +852,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.enum_literal_type,
.anyframe_type,
.null_value,
.function,
.extern_fn,
@@ -878,7 +869,6 @@ pub const Value = extern union {
.unreachable_value,
.empty_array,
.enum_literal,
.error_set,
.@"error",
.error_union,
.empty_struct_value,
@@ -942,6 +932,8 @@ pub const Value = extern union {
.i32_type,
.u64_type,
.i64_type,
.u128_type,
.i128_type,
.usize_type,
.isize_type,
.c_short_type,
@@ -974,7 +966,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.enum_literal_type,
.anyframe_type,
.null_value,
.function,
.extern_fn,
@@ -993,7 +984,6 @@ pub const Value = extern union {
.unreachable_value,
.empty_array,
.enum_literal,
.error_set,
.@"error",
.error_union,
.empty_struct_value,
@@ -1034,6 +1024,8 @@ pub const Value = extern union {
.i32_type,
.u64_type,
.i64_type,
.u128_type,
.i128_type,
.usize_type,
.isize_type,
.c_short_type,
@@ -1066,7 +1058,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.enum_literal_type,
.anyframe_type,
.null_value,
.function,
.extern_fn,
@@ -1084,7 +1075,6 @@ pub const Value = extern union {
.unreachable_value,
.empty_array,
.enum_literal,
.error_set,
.@"error",
.error_union,
.empty_struct_value,
@@ -1191,6 +1181,8 @@ pub const Value = extern union {
.i32_type,
.u64_type,
.i64_type,
.u128_type,
.i128_type,
.usize_type,
.isize_type,
.c_short_type,
@@ -1223,7 +1215,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.enum_literal_type,
.anyframe_type,
.bool_true,
.bool_false,
.null_value,
@@ -1244,7 +1235,6 @@ pub const Value = extern union {
.void_value,
.unreachable_value,
.enum_literal,
.error_set,
.@"error",
.error_union,
.empty_struct_value,
@@ -1275,6 +1265,8 @@ pub const Value = extern union {
.i32_type,
.u64_type,
.i64_type,
.u128_type,
.i128_type,
.usize_type,
.isize_type,
.c_short_type,
@@ -1307,7 +1299,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.enum_literal_type,
.anyframe_type,
.null_value,
.function,
.extern_fn,
@@ -1322,7 +1313,6 @@ pub const Value = extern union {
.unreachable_value,
.empty_array,
.enum_literal,
.error_set,
.@"error",
.error_union,
.empty_struct_value,
@@ -1427,6 +1417,8 @@ pub const Value = extern union {
.i32_type,
.u64_type,
.i64_type,
.u128_type,
.i128_type,
.usize_type,
.isize_type,
.c_short_type,
@@ -1459,18 +1451,12 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.enum_literal_type,
.anyframe_type,
.ty,
=> {
// Directly return Type.hash, toType can only fail for .int_type and .error_set.
// Directly return Type.hash, toType can only fail for .int_type.
var allocator = std.heap.FixedBufferAllocator.init(&[_]u8{});
return (self.toType(&allocator.allocator) catch unreachable).hash();
},
.error_set => {
// Payload.decl should be same for all instances of the type.
const payload = self.castTag(.error_set).?.data;
std.hash.autoHash(&hasher, payload.decl);
},
.int_type => {
const payload = self.castTag(.int_type).?.data;
var int_payload = Type.Payload.Bits{
@@ -1585,6 +1571,8 @@ pub const Value = extern union {
.i32_type,
.u64_type,
.i64_type,
.u128_type,
.i128_type,
.usize_type,
.isize_type,
.c_short_type,
@@ -1617,7 +1605,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.enum_literal_type,
.anyframe_type,
.zero,
.one,
.bool_true,
@@ -1641,7 +1628,6 @@ pub const Value = extern union {
.unreachable_value,
.empty_array,
.enum_literal,
.error_set,
.@"error",
.error_union,
.empty_struct_value,
@@ -1672,6 +1658,8 @@ pub const Value = extern union {
.i32_type,
.u64_type,
.i64_type,
.u128_type,
.i128_type,
.usize_type,
.isize_type,
.c_short_type,
@@ -1704,7 +1692,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.enum_literal_type,
.anyframe_type,
.zero,
.one,
.bool_true,
@@ -1728,7 +1715,6 @@ pub const Value = extern union {
.void_value,
.unreachable_value,
.enum_literal,
.error_set,
.@"error",
.error_union,
.empty_struct_value,
@@ -1776,6 +1762,8 @@ pub const Value = extern union {
.i32_type,
.u64_type,
.i64_type,
.u128_type,
.i128_type,
.usize_type,
.isize_type,
.c_short_type,
@@ -1808,7 +1796,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.enum_literal_type,
.anyframe_type,
.zero,
.one,
.empty_array,
@@ -1832,7 +1819,6 @@ pub const Value = extern union {
.float_128,
.void_value,
.enum_literal,
.error_set,
.@"error",
.error_union,
.empty_struct_value,
@@ -1858,6 +1844,8 @@ pub const Value = extern union {
.i32_type,
.u64_type,
.i64_type,
.u128_type,
.i128_type,
.usize_type,
.isize_type,
.c_short_type,
@@ -1890,7 +1878,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.enum_literal_type,
.anyframe_type,
.zero,
.one,
.null_value,
@@ -1915,7 +1902,6 @@ pub const Value = extern union {
.float_128,
.void_value,
.enum_literal,
.error_set,
.empty_struct_value,
=> null,
@@ -1960,6 +1946,8 @@ pub const Value = extern union {
.i32_type,
.u64_type,
.i64_type,
.u128_type,
.i128_type,
.usize_type,
.isize_type,
.c_short_type,
@@ -1992,8 +1980,6 @@ pub const Value = extern union {
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.enum_literal_type,
.anyframe_type,
.error_set,
=> true,
.zero,
@@ -2137,18 +2123,6 @@ pub const Value = extern union {
data: f128,
};
/// TODO move to type.zig
pub const ErrorSet = struct {
pub const base_tag = Tag.error_set;
base: Payload = .{ .tag = base_tag },
data: struct {
/// TODO revisit this when we have the concept of the error tag type
fields: std.StringHashMapUnmanaged(void),
decl: *Module.Decl,
},
};
pub const Error = struct {
base: Payload = .{ .tag = .@"error" },
data: struct {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -39,6 +39,21 @@ pub fn addCases(ctx: *TestContext) !void {
\\}
\\fn unused() void {}
, "yo!" ++ std.cstr.line_sep);
// Comptime return type and calling convention expected.
case.addError(
\\var x: i32 = 1234;
\\export fn main() x {
\\ return 0;
\\}
\\export fn foo() callconv(y) c_int {
\\ return 0;
\\}
\\var y: i32 = 1234;
, &.{
":2:18: error: unable to resolve comptime value",
":5:26: error: unable to resolve comptime value",
});
}
{
@@ -54,6 +69,42 @@ pub fn addCases(ctx: *TestContext) !void {
, "Hello, world!" ++ std.cstr.line_sep);
}
{
var case = ctx.exeFromCompiledC("@intToError", .{});
case.addCompareOutput(
\\pub export fn main() c_int {
\\ // comptime checks
\\ const a = error.A;
\\ const b = error.B;
\\ const c = @intToError(2);
\\ const d = @intToError(1);
\\ if (!(c == b)) unreachable;
\\ if (!(a == d)) unreachable;
\\ // runtime checks
\\ var x = error.A;
\\ var y = error.B;
\\ var z = @intToError(2);
\\ var f = @intToError(1);
\\ if (!(y == z)) unreachable;
\\ if (!(x == f)) unreachable;
\\ return 0;
\\}
, "");
case.addError(
\\pub export fn main() c_int {
\\ const c = @intToError(0);
\\ return 0;
\\}
, &.{":2:27: error: integer value 0 represents no error"});
case.addError(
\\pub export fn main() c_int {
\\ const c = @intToError(3);
\\ return 0;
\\}
, &.{":2:27: error: integer value 3 represents no error"});
}
{
var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64);
@@ -243,6 +294,134 @@ pub fn addCases(ctx: *TestContext) !void {
\\ return a - 4;
\\}
, "");
// Switch expression missing else case.
case.addError(
\\export fn main() c_int {
\\ var cond: c_int = 0;
\\ const a: c_int = switch (cond) {
\\ 1 => 1,
\\ 2 => 2,
\\ 3 => 3,
\\ 4 => 4,
\\ };
\\ return a - 4;
\\}
, &.{":3:22: error: switch must handle all possibilities"});
// Switch expression, has an unreachable prong.
case.addCompareOutput(
\\export fn main() c_int {
\\ var cond: c_int = 0;
\\ const a: c_int = switch (cond) {
\\ 1 => 1,
\\ 2 => 2,
\\ 99...300, 12 => 3,
\\ 0 => 4,
\\ 13 => unreachable,
\\ else => 5,
\\ };
\\ return a - 4;
\\}
, "");
// Switch expression, has an unreachable prong and prongs write
// to result locations.
case.addCompareOutput(
\\export fn main() c_int {
\\ var cond: c_int = 0;
\\ var a: c_int = switch (cond) {
\\ 1 => 1,
\\ 2 => 2,
\\ 99...300, 12 => 3,
\\ 0 => 4,
\\ 13 => unreachable,
\\ else => 5,
\\ };
\\ return a - 4;
\\}
, "");
// Integer switch expression has duplicate case value.
case.addError(
\\export fn main() c_int {
\\ var cond: c_int = 0;
\\ const a: c_int = switch (cond) {
\\ 1 => 1,
\\ 2 => 2,
\\ 96, 11...13, 97 => 3,
\\ 0 => 4,
\\ 90, 12 => 100,
\\ else => 5,
\\ };
\\ return a - 4;
\\}
, &.{
":8:13: error: duplicate switch value",
":6:15: note: previous value here",
});
// Boolean switch expression has duplicate case value.
case.addError(
\\export fn main() c_int {
\\ var a: bool = false;
\\ const b: c_int = switch (a) {
\\ false => 1,
\\ true => 2,
\\ false => 3,
\\ };
\\}
, &.{
":6:9: error: duplicate switch value",
});
// Sparse (no range capable) switch expression has duplicate case value.
case.addError(
\\export fn main() c_int {
\\ const A: type = i32;
\\ const b: c_int = switch (A) {
\\ i32 => 1,
\\ bool => 2,
\\ f64, i32 => 3,
\\ else => 4,
\\ };
\\}
, &.{
":6:14: error: duplicate switch value",
":4:9: note: previous value here",
});
// Ranges not allowed for some kinds of switches.
case.addError(
\\export fn main() c_int {
\\ const A: type = i32;
\\ const b: c_int = switch (A) {
\\ i32 => 1,
\\ bool => 2,
\\ f16...f64 => 3,
\\ else => 4,
\\ };
\\}
, &.{
":3:30: error: ranges not allowed when switching on type 'type'",
":6:12: note: range here",
});
// Switch expression has unreachable else prong.
case.addError(
\\export fn main() c_int {
\\ var a: u2 = 0;
\\ const b: i32 = switch (a) {
\\ 0 => 10,
\\ 1 => 20,
\\ 2 => 30,
\\ 3 => 40,
\\ else => 50,
\\ };
\\}
, &.{
":8:14: error: unreachable else prong; all cases already handled",
});
}
//{
// var case = ctx.exeFromCompiledC("optionals", .{});
@@ -271,6 +450,7 @@ pub fn addCases(ctx: *TestContext) !void {
// \\}
// , "");
//}
{
var case = ctx.exeFromCompiledC("errors", .{});
case.addCompareOutput(

View File

@@ -355,7 +355,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ const z = @TypeOf(true, 1);
\\ unreachable;
\\}
, &[_][]const u8{":2:29: error: incompatible types: 'bool' and 'comptime_int'"});
, &[_][]const u8{":2:15: error: incompatible types: 'bool' and 'comptime_int'"});
}
{
@@ -621,6 +621,43 @@ pub fn addCases(ctx: *TestContext) !void {
"hello\nhello\nhello\nhello\n",
);
// inline while requires the condition to be comptime known.
case.addError(
\\export fn _start() noreturn {
\\ var i: u32 = 0;
\\ inline while (i < 4) : (i += 1) print();
\\ assert(i == 4);
\\
\\ exit();
\\}
\\
\\fn print() void {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (1),
\\ [arg1] "{rdi}" (1),
\\ [arg2] "{rsi}" (@ptrToInt("hello\n")),
\\ [arg3] "{rdx}" (6)
\\ : "rcx", "r11", "memory"
\\ );
\\ return;
\\}
\\
\\pub fn assert(ok: bool) void {
\\ if (!ok) unreachable; // assertion failure
\\}
\\
\\fn exit() noreturn {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (231),
\\ [arg1] "{rdi}" (0)
\\ : "rcx", "r11", "memory"
\\ );
\\ unreachable;
\\}
, &[_][]const u8{":3:21: error: unable to resolve comptime value"});
// Labeled blocks (no conditional branch)
case.addCompareOutput(
\\export fn _start() noreturn {
@@ -1070,7 +1107,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\}
\\fn x() void {}
, &[_][]const u8{
":11:8: error: found compile log statement",
":9:5: error: found compile log statement",
":4:5: note: also here",
});
}
@@ -1294,10 +1331,9 @@ pub fn addCases(ctx: *TestContext) !void {
,
"",
);
// TODO this should be :8:21 not :8:19. we need to improve source locations
// to be relative to the containing Decl so that they can survive when the byte
// offset of a previous Decl changes. Here the change from 7 to 999 introduces
// +2 to the byte offset and makes the error location wrong by 2 bytes.
// This additionally tests that the compile error reports the correct source location.
// Without storing source locations relative to the owner decl, the compile error
// here would be off by 2 bytes (from the "7" -> "999").
case.addError(
\\export fn _start() noreturn {
\\ const y = fibonacci(999);
@@ -1318,7 +1354,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ );
\\ unreachable;
\\}
, &[_][]const u8{":8:19: error: evaluation exceeded 1000 backwards branches"});
, &[_][]const u8{":8:21: error: evaluation exceeded 1000 backwards branches"});
}
{
var case = ctx.exe("orelse at comptime", linux_x64);
@@ -1442,6 +1478,7 @@ pub fn addCases(ctx: *TestContext) !void {
,
"",
);
case.addCompareOutput(
\\export fn _start() noreturn {
\\ const i: anyerror!u64 = error.B;
@@ -1464,6 +1501,7 @@ pub fn addCases(ctx: *TestContext) !void {
,
"",
);
case.addCompareOutput(
\\export fn _start() noreturn {
\\ const a: anyerror!comptime_int = 42;
@@ -1485,11 +1523,12 @@ pub fn addCases(ctx: *TestContext) !void {
\\ unreachable;
\\}
, "");
case.addCompareOutput(
\\export fn _start() noreturn {
\\const a: anyerror!u32 = error.B;
\\_ = &(a catch |err| assert(err == error.B));
\\exit();
\\ const a: anyerror!u32 = error.B;
\\ _ = &(a catch |err| assert(err == error.B));
\\ exit();
\\}
\\fn assert(b: bool) void {
\\ if (!b) unreachable;
@@ -1504,6 +1543,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ unreachable;
\\}
, "");
case.addCompareOutput(
\\export fn _start() noreturn {
\\ const a: anyerror!u32 = error.Bar;