commit b36c07a95a6cf9b2cc120133b44cbd0673e6823a (tree)
parent b220be7a33a9835a1ec7a033e472830290332d57
Author: Andrew Kelley <andrew@ziglang.org>
Date: Sun, 1 Dec 2019 09:56:01 -0500
Merge remote-tracking branch 'origin/master' into remove-array-type-coercion
Diffstat:
65 files changed, 3021 insertions(+), 994 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
@@ -7455,6 +7455,10 @@ fn add(a: i32, b: i32) i32 { return a + b; }
Attempting to convert a number which is out of range of the destination type results in
safety-protected {#link|Undefined Behavior#}.
</p>
+ <p>
+ If {#syntax#}T{#endsyntax#} is {#syntax#}comptime_int{#endsyntax#},
+ then this is semantically equivalent to {#link|Type Coercion#}.
+ </p>
{#header_close#}
{#header_open|@intToEnum#}
@@ -8206,10 +8210,6 @@ test "integer truncation" {
This function always truncates the significant bits of the integer, regardless
of endianness on the target platform.
</p>
- <p>
- If {#syntax#}T{#endsyntax#} is {#syntax#}comptime_int{#endsyntax#},
- then this is semantically equivalent to {#link|Type Coercion#}.
- </p>
{#header_close#}
{#header_open|@Type#}
diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig
@@ -40,6 +40,14 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
.allocator = allocator,
};
}
+
+ /// Initialize with capacity to hold at least num elements.
+ /// Deinitialize with `deinit` or use `toOwnedSlice`.
+ pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
+ var self = Self.init(allocator);
+ try self.ensureCapacity(num);
+ return self;
+ }
/// Release all allocated memory.
pub fn deinit(self: Self) void {
@@ -271,6 +279,15 @@ test "std.ArrayList.init" {
testing.expect(list.capacity() == 0);
}
+test "std.ArrayList.initCapacity" {
+ var bytes: [1024]u8 = undefined;
+ const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
+ var list = try ArrayList(i8).initCapacity(allocator, 200);
+ defer list.deinit();
+ testing.expect(list.count() == 0);
+ testing.expect(list.capacity() >= 200);
+}
+
test "std.ArrayList.basic" {
var bytes: [1024]u8 = undefined;
const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
diff --git a/lib/std/buffer.zig b/lib/std/buffer.zig
@@ -16,13 +16,22 @@ pub const Buffer = struct {
mem.copy(u8, self.list.items, m);
return self;
}
-
+
+ /// Initialize memory to size bytes of undefined values.
/// Must deinitialize with deinit.
pub fn initSize(allocator: *Allocator, size: usize) !Buffer {
var self = initNull(allocator);
try self.resize(size);
return self;
}
+
+ /// Initialize with capacity to hold at least num bytes.
+ /// Must deinitialize with deinit.
+ pub fn initCapacity(allocator: *Allocator, num: usize) !Buffer {
+ var self = Buffer{ .list = try ArrayList(u8).initCapacity(allocator, num + 1) };
+ self.list.appendAssumeCapacity(0);
+ return self;
+ }
/// Must deinitialize with deinit.
/// None of the other operations are valid until you do one of these:
@@ -98,6 +107,13 @@ pub const Buffer = struct {
pub fn len(self: Buffer) usize {
return self.list.len - 1;
}
+
+ pub fn capacity(self: Buffer) usize {
+ return if (self.list.items.len > 0)
+ self.list.items.len - 1
+ else
+ 0;
+ }
pub fn append(self: *Buffer, m: []const u8) !void {
const old_len = self.len();
@@ -151,3 +167,21 @@ test "simple Buffer" {
try buf2.resize(4);
testing.expect(buf.startsWith(buf2.toSlice()));
}
+
+test "Buffer.initSize" {
+ var buf = try Buffer.initSize(debug.global_allocator, 3);
+ testing.expect(buf.len() == 3);
+ try buf.append("hello");
+ testing.expect(mem.eql(u8, buf.toSliceConst()[3..], "hello"));
+}
+
+test "Buffer.initCapacity" {
+ var buf = try Buffer.initCapacity(debug.global_allocator, 10);
+ testing.expect(buf.len() == 0);
+ testing.expect(buf.capacity() >= 10);
+ const old_cap = buf.capacity();
+ try buf.append("hello");
+ testing.expect(buf.len() == 5);
+ testing.expect(buf.capacity() == old_cap);
+ testing.expect(mem.eql(u8, buf.toSliceConst(), "hello"));
+}
diff --git a/lib/std/build.zig b/lib/std/build.zig
@@ -2062,14 +2062,28 @@ pub const RunStep = struct {
}
pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
- const PATH = if (builtin.os == .windows) "Path" else "PATH";
const env_map = self.getEnvMap();
- const prev_path = env_map.get(PATH) orelse {
- env_map.set(PATH, search_path) catch unreachable;
- return;
- };
- const new_path = self.builder.fmt("{}" ++ &[1]u8{fs.path.delimiter} ++ "{}", prev_path, search_path);
- env_map.set(PATH, new_path) catch unreachable;
+
+ var key: []const u8 = undefined;
+ var prev_path: ?[]const u8 = undefined;
+ if (builtin.os == .windows) {
+ key = "Path";
+ prev_path = env_map.get(key);
+ if (prev_path == null) {
+ key = "PATH";
+ prev_path = env_map.get(key);
+ }
+ } else {
+ key = "PATH";
+ prev_path = env_map.get(key);
+ }
+
+ if (prev_path) |pp| {
+ const new_path = self.builder.fmt("{}" ++ [1]u8{fs.path.delimiter} ++ "{}", pp, search_path);
+ env_map.set(key, new_path) catch unreachable;
+ } else {
+ env_map.set(key, search_path) catch unreachable;
+ }
}
pub fn getEnvMap(self: *RunStep) *BufMap {
@@ -2178,7 +2192,7 @@ const InstallArtifactStep = struct {
const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
try builder.updateFile(self.artifact.getOutputPath(), full_dest_path);
- if (self.artifact.isDynamicLibrary()) {
+ if (self.artifact.isDynamicLibrary() and self.artifact.target.wantSharedLibSymLinks()) {
try doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename, self.artifact.name_only_filename);
}
if (self.pdb_dir) |pdb_dir| {
@@ -2405,7 +2419,7 @@ fn findVcpkgRoot(allocator: *Allocator) !?[]const u8 {
const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
defer allocator.free(path_file);
- const file = fs.File.openRead(path_file) catch return null;
+ const file = fs.cwd().openFile(path_file, .{}) catch return null;
defer file.close();
const size = @intCast(usize, try file.getEndPos());
diff --git a/lib/std/c.zig b/lib/std/c.zig
@@ -220,6 +220,7 @@ pub extern "c" fn pthread_mutex_destroy(mutex: *pthread_mutex_t) c_int;
pub const PTHREAD_COND_INITIALIZER = pthread_cond_t{};
pub extern "c" fn pthread_cond_wait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t) c_int;
+pub extern "c" fn pthread_cond_timedwait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t, noalias abstime: *const timespec) c_int;
pub extern "c" fn pthread_cond_signal(cond: *pthread_cond_t) c_int;
pub extern "c" fn pthread_cond_destroy(cond: *pthread_cond_t) c_int;
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
@@ -1131,7 +1131,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
}
fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {
- var f = try File.openRead(line_info.file_name);
+ var f = try fs.cwd().openFile(line_info.file_name, .{});
defer f.close();
// TODO fstat and make sure that the file has the correct size
@@ -2089,7 +2089,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx));
gop.kv.value = MachOFile{
- .bytes = try std.fs.Dir.cwd().readFileAllocAligned(
+ .bytes = try std.fs.cwd().readFileAllocAligned(
di.ofiles.allocator,
ofile_path,
maxInt(usize),
@@ -2417,6 +2417,12 @@ extern fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: *con
std.debug.warn("Segmentation fault at address 0x{x}\n", addr);
switch (builtin.arch) {
+ .i386 => {
+ const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
+ const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_EIP]);
+ const bp = @intCast(usize, ctx.mcontext.gregs[os.REG_EBP]);
+ dumpStackTraceFromBase(bp, ip);
+ },
.x86_64 => {
const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_RIP]);
diff --git a/lib/std/event/channel.zig b/lib/std/event/channel.zig
@@ -54,6 +54,10 @@ pub fn Channel(comptime T: type) type {
/// For a zero length buffer, use `[0]T{}`.
/// TODO https://github.com/ziglang/zig/issues/2765
pub fn init(self: *SelfChannel, buffer: []T) void {
+ // The ring buffer implementation only works with power of 2 buffer sizes
+ // because of relying on subtracting across zero. For example (0 -% 1) % 10 == 5
+ assert(buffer.len == 0 or @popCount(usize, buffer.len) == 1);
+
self.* = SelfChannel{
.buffer_len = 0,
.buffer_nodes = buffer,
@@ -184,11 +188,11 @@ pub fn Channel(comptime T: type) type {
const get_node = &self.getters.get().?.data;
switch (get_node.data) {
GetNode.Data.Normal => |info| {
- info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
+ info.ptr.* = self.buffer_nodes[(self.buffer_index -% self.buffer_len) % self.buffer_nodes.len];
},
GetNode.Data.OrNull => |info| {
_ = self.or_null_queue.remove(info.or_null);
- info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
+ info.ptr.* = self.buffer_nodes[(self.buffer_index -% self.buffer_len) % self.buffer_nodes.len];
},
}
global_event_loop.onNextTick(get_node.tick_node);
@@ -222,7 +226,7 @@ pub fn Channel(comptime T: type) type {
while (self.buffer_len != self.buffer_nodes.len and put_count != 0) {
const put_node = &self.putters.get().?.data;
- self.buffer_nodes[self.buffer_index] = put_node.data;
+ self.buffer_nodes[self.buffer_index % self.buffer_nodes.len] = put_node.data;
global_event_loop.onNextTick(put_node.tick_node);
self.buffer_index +%= 1;
self.buffer_len += 1;
@@ -283,6 +287,29 @@ test "std.event.Channel" {
await putter;
}
+test "std.event.Channel wraparound" {
+
+ // TODO provide a way to run tests in evented I/O mode
+ if (!std.io.is_async) return error.SkipZigTest;
+
+ const channel_size = 2;
+
+ var buf : [channel_size]i32 = undefined;
+ var channel: Channel(i32) = undefined;
+ channel.init(&buf);
+ defer channel.deinit();
+
+ // add items to channel and pull them out until
+ // the buffer wraps around, make sure it doesn't crash.
+ var result : i32 = undefined;
+ channel.put(5);
+ testing.expectEqual(@as(i32, 5), channel.get());
+ channel.put(6);
+ testing.expectEqual(@as(i32, 6), channel.get());
+ channel.put(7);
+ testing.expectEqual(@as(i32, 7), channel.get());
+}
+
async fn testChannelGetter(channel: *Channel(i32)) void {
const value1 = channel.get();
testing.expect(value1 == 1234);
diff --git a/lib/std/event/fs.zig b/lib/std/event/fs.zig
@@ -735,24 +735,26 @@ pub fn Watch(comptime V: type) type {
allocator: *Allocator,
const OsData = switch (builtin.os) {
- .macosx, .freebsd, .netbsd, .dragonfly => struct {
- file_table: FileTable,
- table_lock: event.Lock,
-
- const FileTable = std.StringHashMap(*Put);
- const Put = struct {
- putter_frame: @Frame(kqPutEvents),
- cancelled: bool = false,
- value: V,
- };
- },
-
+ // TODO https://github.com/ziglang/zig/issues/3778
+ .macosx, .freebsd, .netbsd, .dragonfly => KqOsData,
.linux => LinuxOsData,
.windows => WindowsOsData,
else => @compileError("Unsupported OS"),
};
+ const KqOsData = struct {
+ file_table: FileTable,
+ table_lock: event.Lock,
+
+ const FileTable = std.StringHashMap(*Put);
+ const Put = struct {
+ putter_frame: @Frame(kqPutEvents),
+ cancelled: bool = false,
+ value: V,
+ };
+ };
+
const WindowsOsData = struct {
table_lock: event.Lock,
dir_table: DirTable,
@@ -1291,7 +1293,7 @@ pub fn Watch(comptime V: type) type {
os.linux.EINVAL => unreachable,
os.linux.EFAULT => unreachable,
os.linux.EAGAIN => {
- global_event_loop.linuxWaitFd(self.os_data.inotify_fd, os.linux.EPOLLET | os.linux.EPOLLIN);
+ global_event_loop.linuxWaitFd(self.os_data.inotify_fd, os.linux.EPOLLET | os.linux.EPOLLIN | os.EPOLLONESHOT);
},
else => unreachable,
}
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
@@ -13,8 +13,6 @@ pub const File = @import("fs/file.zig").File;
pub const symLink = os.symlink;
pub const symLinkC = os.symlinkC;
-pub const deleteFile = os.unlink;
-pub const deleteFileC = os.unlinkC;
pub const rename = os.rename;
pub const renameC = os.renameC;
pub const renameW = os.renameW;
@@ -88,13 +86,15 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {
/// If any of the directories do not exist for dest_path, they are created.
/// TODO https://github.com/ziglang/zig/issues/2885
pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus {
- var src_file = try File.openRead(source_path);
+ const my_cwd = cwd();
+
+ var src_file = try my_cwd.openFile(source_path, .{});
defer src_file.close();
const src_stat = try src_file.stat();
check_dest_stat: {
const dest_stat = blk: {
- var dest_file = File.openRead(dest_path) catch |err| switch (err) {
+ var dest_file = my_cwd.openFile(dest_path, .{}) catch |err| switch (err) {
error.FileNotFound => break :check_dest_stat,
else => |e| return e,
};
@@ -157,7 +157,7 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil
/// in the same directory as dest_path.
/// Destination file will have the same mode as the source file.
pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
- var in_file = try File.openRead(source_path);
+ var in_file = try cwd().openFile(source_path, .{});
defer in_file.close();
const mode = try in_file.mode();
@@ -180,7 +180,7 @@ pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
/// merged and readily available,
/// there is a possibility of power loss or application termination leaving temporary files present
pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
- var in_file = try File.openRead(source_path);
+ var in_file = try cwd().openFile(source_path, .{});
defer in_file.close();
var atomic_file = try AtomicFile.init(dest_path, mode);
@@ -206,8 +206,6 @@ pub const AtomicFile = struct {
/// dest_path must remain valid for the lifetime of AtomicFile
/// call finish to atomically replace dest_path with contents
- /// TODO once we have null terminated pointers, use the
- /// openWriteNoClobberN function
pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile {
const dirname = path.dirname(dest_path);
var rand_buf: [12]u8 = undefined;
@@ -224,15 +222,19 @@ pub const AtomicFile = struct {
tmp_path_buf[tmp_path_len] = 0;
+ const my_cwd = cwd();
+
while (true) {
try crypto.randomBytes(rand_buf[0..]);
b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], &rand_buf);
- const file = File.openWriteNoClobberC(@ptrCast([*:0]u8, &tmp_path_buf), mode) catch |err| switch (err) {
+ // TODO https://github.com/ziglang/zig/issues/3770 to clean up this @ptrCast
+ const file = my_cwd.createFileC(
+ @ptrCast([*:0]u8, &tmp_path_buf),
+ .{ .mode = mode, .exclusive = true },
+ ) catch |err| switch (err) {
error.PathAlreadyExists => continue,
- // TODO zig should figure out that this error set does not include PathAlreadyExists since
- // it is handled in the above switch
- else => return err,
+ else => |e| return e,
};
return AtomicFile{
@@ -248,7 +250,7 @@ pub const AtomicFile = struct {
pub fn deinit(self: *AtomicFile) void {
if (!self.finished) {
self.file.close();
- deleteFileC(@ptrCast([*:0]u8, &self.tmp_path_buf)) catch {};
+ cwd().deleteFileC(@ptrCast([*:0]u8, &self.tmp_path_buf)) catch {};
self.finished = true;
}
}
@@ -350,12 +352,12 @@ pub fn deleteTree(full_path: []const u8) !void {
CannotDeleteRootDirectory,
}.CannotDeleteRootDirectory;
- var dir = try Dir.cwd().openDirList(dirname);
+ var dir = try cwd().openDirList(dirname);
defer dir.close();
return dir.deleteTree(path.basename(full_path));
} else {
- return Dir.cwd().deleteTree(full_path);
+ return cwd().deleteTree(full_path);
}
}
@@ -657,17 +659,6 @@ pub const Dir = struct {
}
}
- /// Returns an handle to the current working directory that is open for traversal.
- /// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
- /// On POSIX targets, this function is comptime-callable.
- pub fn cwd() Dir {
- if (builtin.os == .windows) {
- return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
- } else {
- return Dir{ .fd = os.AT_FDCWD };
- }
- }
-
pub const OpenError = error{
FileNotFound,
NotDir,
@@ -683,12 +674,12 @@ pub const Dir = struct {
DeviceBusy,
} || os.UnexpectedError;
- /// Deprecated; call `Dir.cwd().openDirList` directly.
+ /// Deprecated; call `cwd().openDirList` directly.
pub fn open(dir_path: []const u8) OpenError!Dir {
return cwd().openDirList(dir_path);
}
- /// Deprecated; call `Dir.cwd().openDirListC` directly.
+ /// Deprecated; call `cwd().openDirListC` directly.
pub fn openC(dir_path_c: [*:0]const u8) OpenError!Dir {
return cwd().openDirListC(dir_path_c);
}
@@ -698,29 +689,110 @@ pub const Dir = struct {
self.* = undefined;
}
- /// Call `File.close` on the result when done.
- pub fn openRead(self: Dir, sub_path: []const u8) File.OpenError!File {
+ /// Opens a file for reading or writing, without attempting to create a new file.
+ /// Call `File.close` to release the resource.
+ /// Asserts that the path parameter has no null bytes.
+ pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
+ if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
if (builtin.os == .windows) {
const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
- return self.openReadW(&path_w);
+ return self.openFileW(&path_w, flags);
}
const path_c = try os.toPosixPath(sub_path);
- return self.openReadC(&path_c);
+ return self.openFileC(&path_c, flags);
}
- /// Call `File.close` on the result when done.
- pub fn openReadC(self: Dir, sub_path: [*:0]const u8) File.OpenError!File {
+ /// Same as `openFile` but the path parameter is null-terminated.
+ pub fn openFileC(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File {
if (builtin.os == .windows) {
const path_w = try os.windows.cStrToPrefixedFileW(sub_path);
- return self.openReadW(&path_w);
+ return self.openFileW(&path_w, flags);
}
const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
- const flags = O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC;
- const fd = try os.openatC(self.fd, sub_path, flags, 0);
- return File.openHandle(fd);
+ const os_flags = O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read)
+ @as(u32, os.O_RDWR)
+ else if (flags.write)
+ @as(u32, os.O_WRONLY)
+ else
+ @as(u32, os.O_RDONLY);
+ const fd = try os.openatC(self.fd, sub_path, os_flags, 0);
+ return File{ .handle = fd };
+ }
+
+ /// Same as `openFile` but Windows-only and the path parameter is
+ /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
+ pub fn openFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File {
+ const w = os.windows;
+ const access_mask = w.SYNCHRONIZE |
+ (if (flags.read) @as(u32, w.GENERIC_READ) else 0) |
+ (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0);
+ return self.openFileWindows(sub_path_w, access_mask, w.FILE_OPEN);
}
- pub fn openReadW(self: Dir, sub_path_w: [*:0]const u16) File.OpenError!File {
+ /// Creates, opens, or overwrites a file with write access.
+ /// Call `File.close` on the result when done.
+ /// Asserts that the path parameter has no null bytes.
+ pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
+ if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
+ if (builtin.os == .windows) {
+ const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
+ return self.createFileW(&path_w, flags);
+ }
+ const path_c = try os.toPosixPath(sub_path);
+ return self.createFileC(&path_c, flags);
+ }
+
+ /// Same as `createFile` but the path parameter is null-terminated.
+ pub fn createFileC(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File {
+ if (builtin.os == .windows) {
+ const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
+ return self.createFileW(&path_w, flags);
+ }
+ const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
+ const os_flags = O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC |
+ (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) |
+ (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) |
+ (if (flags.exclusive) @as(u32, os.O_EXCL) else 0);
+ const fd = try os.openatC(self.fd, sub_path_c, os_flags, flags.mode);
+ return File{ .handle = fd };
+ }
+
+ /// Same as `createFile` but Windows-only and the path parameter is
+ /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
+ pub fn createFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File {
+ const w = os.windows;
+ const access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE |
+ (if (flags.read) @as(u32, w.GENERIC_READ) else 0);
+ const creation = if (flags.exclusive)
+ @as(u32, w.FILE_CREATE)
+ else if (flags.truncate)
+ @as(u32, w.FILE_OVERWRITE_IF)
+ else
+ @as(u32, w.FILE_OPEN_IF);
+ return self.openFileWindows(sub_path_w, access_mask, creation);
+ }
+
+ /// Deprecated; call `openFile` directly.
+ pub fn openRead(self: Dir, sub_path: []const u8) File.OpenError!File {
+ return self.openFile(sub_path, .{});
+ }
+
+ /// Deprecated; call `openFileC` directly.
+ pub fn openReadC(self: Dir, sub_path: [*:0]const u8) File.OpenError!File {
+ return self.openFileC(sub_path, .{});
+ }
+
+ /// Deprecated; call `openFileW` directly.
+ pub fn openReadW(self: Dir, sub_path: [*:0]const u16) File.OpenError!File {
+ return self.openFileW(sub_path, .{});
+ }
+
+ pub fn openFileWindows(
+ self: Dir,
+ sub_path_w: [*:0]const u16,
+ access_mask: os.windows.ACCESS_MASK,
+ creation: os.windows.ULONG,
+ ) File.OpenError!File {
const w = os.windows;
var result = File{ .handle = undefined };
@@ -750,13 +822,13 @@ pub const Dir = struct {
var io: w.IO_STATUS_BLOCK = undefined;
const rc = w.ntdll.NtCreateFile(
&result.handle,
- w.GENERIC_READ | w.SYNCHRONIZE,
+ access_mask,
&attr,
&io,
null,
w.FILE_ATTRIBUTE_NORMAL,
- w.FILE_SHARE_READ,
- w.FILE_OPEN,
+ w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,
+ creation,
w.FILE_NON_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT,
null,
0,
@@ -771,6 +843,7 @@ pub const Dir = struct {
w.STATUS.ACCESS_DENIED => return error.AccessDenied,
w.STATUS.PIPE_BUSY => return error.PipeBusy,
w.STATUS.OBJECT_PATH_SYNTAX_BAD => unreachable,
+ w.STATUS.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
else => return w.unexpectedStatus(rc),
}
}
@@ -790,7 +863,10 @@ pub const Dir = struct {
/// list the contents of a directory, open it with `openDirList`.
///
/// Call `close` on the result when done.
+ ///
+ /// Asserts that the path parameter has no null bytes.
pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir {
+ if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
if (builtin.os == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.openDirTraverseW(&sub_path_w);
@@ -805,7 +881,10 @@ pub const Dir = struct {
/// same and may be more efficient.
///
/// Call `close` on the result when done.
+ ///
+ /// Asserts that the path parameter has no null bytes.
pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir {
+ if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
if (builtin.os == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.openDirListW(&sub_path_w);
@@ -920,9 +999,12 @@ pub const Dir = struct {
pub const DeleteFileError = os.UnlinkError;
/// Delete a file name and possibly the file it refers to, based on an open directory handle.
+ /// Asserts that the path parameter has no null bytes.
pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
- const sub_path_c = try os.toPosixPath(sub_path);
- return self.deleteFileC(&sub_path_c);
+ os.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) {
+ error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
+ else => |e| return e,
+ };
}
/// Same as `deleteFile` except the parameter is null-terminated.
@@ -933,6 +1015,14 @@ pub const Dir = struct {
};
}
+ /// Same as `deleteFile` except the parameter is WTF-16 encoded.
+ pub fn deleteFileW(self: Dir, sub_path_w: [*:0]const u16) DeleteFileError!void {
+ os.unlinkatW(self.fd, sub_path_w, 0) catch |err| switch (err) {
+ error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
+ else => |e| return e,
+ };
+ }
+
pub const DeleteDirError = error{
DirNotEmpty,
FileNotFound,
@@ -951,7 +1041,9 @@ pub const Dir = struct {
/// Returns `error.DirNotEmpty` if the directory is not empty.
/// To delete a directory recursively, see `deleteTree`.
+ /// Asserts that the path parameter has no null bytes.
pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
+ if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
if (builtin.os == .windows) {
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
return self.deleteDirW(&sub_path_w);
@@ -979,7 +1071,9 @@ pub const Dir = struct {
/// Read value of a symbolic link.
/// The return value is a slice of `buffer`, from index `0`.
+ /// Asserts that the path parameter has no null bytes.
pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
+ if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
const sub_path_c = try os.toPosixPath(sub_path);
return self.readLinkC(&sub_path_c, buffer);
}
@@ -1190,8 +1284,94 @@ pub const Dir = struct {
}
}
}
+
+ /// Writes content to the file system, creating a new file if it does not exist, truncating
+ /// if it already exists.
+ pub fn writeFile(self: Dir, sub_path: []const u8, data: []const u8) !void {
+ var file = try self.createFile(sub_path, .{});
+ defer file.close();
+ try file.write(data);
+ }
};
+/// Returns an handle to the current working directory that is open for traversal.
+/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
+/// On POSIX targets, this function is comptime-callable.
+pub fn cwd() Dir {
+ if (builtin.os == .windows) {
+ return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
+ } else {
+ return Dir{ .fd = os.AT_FDCWD };
+ }
+}
+
+/// Opens a file for reading or writing, without attempting to create a new file, based on an absolute path.
+/// Call `File.close` to release the resource.
+/// Asserts that the path is absolute. See `Dir.openFile` for a function that
+/// operates on both absolute and relative paths.
+/// Asserts that the path parameter has no null bytes. See `openFileAbsoluteC` for a function
+/// that accepts a null-terminated path.
+pub fn openFileAbsolute(absolute_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
+ assert(path.isAbsolute(absolute_path));
+ return cwd().openFile(absolute_path, flags);
+}
+
+/// Same as `openFileAbsolute` but the path parameter is null-terminated.
+pub fn openFileAbsoluteC(absolute_path_c: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File {
+ assert(path.isAbsoluteC(absolute_path_c));
+ return cwd().openFileC(absolute_path_c, flags);
+}
+
+/// Same as `openFileAbsolute` but the path parameter is WTF-16 encoded.
+pub fn openFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File {
+ assert(path.isAbsoluteW(absolute_path_w));
+ return cwd().openFileW(absolute_path_w, flags);
+}
+
+/// Creates, opens, or overwrites a file with write access, based on an absolute path.
+/// Call `File.close` to release the resource.
+/// Asserts that the path is absolute. See `Dir.createFile` for a function that
+/// operates on both absolute and relative paths.
+/// Asserts that the path parameter has no null bytes. See `createFileAbsoluteC` for a function
+/// that accepts a null-terminated path.
+pub fn createFileAbsolute(absolute_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
+ assert(path.isAbsolute(absolute_path));
+ return cwd().createFile(absolute_path, flags);
+}
+
+/// Same as `createFileAbsolute` but the path parameter is null-terminated.
+pub fn createFileAbsoluteC(absolute_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File {
+ assert(path.isAbsoluteC(absolute_path_c));
+ return cwd().createFileC(absolute_path_c, flags);
+}
+
+/// Same as `createFileAbsolute` but the path parameter is WTF-16 encoded.
+pub fn createFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File {
+ assert(path.isAbsoluteW(absolute_path_w));
+ return cwd().createFileW(absolute_path_w, flags);
+}
+
+/// Delete a file name and possibly the file it refers to, based on an absolute path.
+/// Asserts that the path is absolute. See `Dir.deleteFile` for a function that
+/// operates on both absolute and relative paths.
+/// Asserts that the path parameter has no null bytes.
+pub fn deleteFileAbsolute(absolute_path: []const u8) DeleteFileError!void {
+ assert(path.isAbsolute(absolute_path));
+ return cwd().deleteFile(absolute_path);
+}
+
+/// Same as `deleteFileAbsolute` except the parameter is null-terminated.
+pub fn deleteFileAbsoluteC(absolute_path_c: [*:0]const u8) DeleteFileError!void {
+ assert(path.isAbsoluteC(absolute_path_c));
+ return cwd().deleteFileC(absolute_path_c);
+}
+
+/// Same as `deleteFileAbsolute` except the parameter is WTF-16 encoded.
+pub fn deleteFileAbsoluteW(absolute_path_w: [*:0]const u16) DeleteFileError!void {
+ assert(path.isAbsoluteW(absolute_path_w));
+ return cwd().deleteFileW(absolute_path_w);
+}
+
pub const Walker = struct {
stack: std.ArrayList(StackItem),
name_buffer: std.Buffer,
@@ -1264,7 +1444,7 @@ pub const Walker = struct {
pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
assert(!mem.endsWith(u8, dir_path, path.sep_str));
- var dir = try Dir.cwd().openDirList(dir_path);
+ var dir = try cwd().openDirList(dir_path);
errdefer dir.close();
var name_buffer = try std.Buffer.init(allocator, dir_path);
@@ -1298,18 +1478,18 @@ pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfE
pub fn openSelfExe() OpenSelfExeError!File {
if (builtin.os == .linux) {
- return File.openReadC("/proc/self/exe");
+ return openFileAbsoluteC("/proc/self/exe", .{});
}
if (builtin.os == .windows) {
const wide_slice = selfExePathW();
const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice);
- return Dir.cwd().openReadW(&prefixed_path_w);
+ return cwd().openReadW(&prefixed_path_w);
}
var buf: [MAX_PATH_BYTES]u8 = undefined;
const self_exe_path = try selfExePath(&buf);
buf[self_exe_path.len] = 0;
- // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731
- return File.openReadC(@ptrCast([*:0]u8, self_exe_path.ptr));
+ // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
+ return openFileAbsoluteC(@ptrCast([*:0]u8, self_exe_path.ptr), .{});
}
test "openSelfExe" {
diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig
@@ -25,105 +25,87 @@ pub const File = struct {
pub const OpenError = windows.CreateFileError || os.OpenError;
- /// Deprecated; call `std.fs.Dir.openRead` directly.
+ /// TODO https://github.com/ziglang/zig/issues/3802
+ pub const OpenFlags = struct {
+ read: bool = true,
+ write: bool = false,
+ };
+
+ /// TODO https://github.com/ziglang/zig/issues/3802
+ pub const CreateFlags = struct {
+ /// Whether the file will be created with read access.
+ read: bool = false,
+
+ /// If the file already exists, and is a regular file, and the access
+ /// mode allows writing, it will be truncated to length 0.
+ truncate: bool = true,
+
+ /// Ensures that this open call creates the file, otherwise causes
+ /// `error.FileAlreadyExists` to be returned.
+ exclusive: bool = false,
+
+ /// For POSIX systems this is the file system mode the file will
+ /// be created with.
+ mode: Mode = default_mode,
+ };
+
+ /// Deprecated; call `std.fs.Dir.openFile` directly.
pub fn openRead(path: []const u8) OpenError!File {
- return std.fs.Dir.cwd().openRead(path);
+ return std.fs.cwd().openFile(path, .{});
}
- /// Deprecated; call `std.fs.Dir.openReadC` directly.
+ /// Deprecated; call `std.fs.Dir.openFileC` directly.
pub fn openReadC(path_c: [*:0]const u8) OpenError!File {
- return std.fs.Dir.cwd().openReadC(path_c);
+ return std.fs.cwd().openFileC(path_c, .{});
}
- /// Deprecated; call `std.fs.Dir.openReadW` directly.
+ /// Deprecated; call `std.fs.Dir.openFileW` directly.
pub fn openReadW(path_w: [*]const u16) OpenError!File {
- return std.fs.Dir.cwd().openReadW(path_w);
+ return std.fs.cwd().openFileW(path_w, .{});
}
- /// Calls `openWriteMode` with `default_mode` for the mode.
- /// TODO: deprecate this and move it to `std.fs.Dir`.
+ /// Deprecated; call `std.fs.Dir.createFile` directly.
pub fn openWrite(path: []const u8) OpenError!File {
- return openWriteMode(path, default_mode);
+ return std.fs.cwd().createFile(path, .{});
}
- /// If the path does not exist it will be created.
- /// If a file already exists in the destination it will be truncated.
- /// Call close to clean up.
- /// TODO: deprecate this and move it to `std.fs.Dir`.
+ /// Deprecated; call `std.fs.Dir.createFile` directly.
pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File {
- if (builtin.os == .windows) {
- const path_w = try windows.sliceToPrefixedFileW(path);
- return openWriteModeW(&path_w, file_mode);
- }
- const path_c = try os.toPosixPath(path);
- return openWriteModeC(&path_c, file_mode);
+ return std.fs.cwd().createFile(path, .{ .mode = file_mode });
}
- /// Same as `openWriteMode` except `path` is null-terminated.
- /// TODO: deprecate this and move it to `std.fs.Dir`.
- pub fn openWriteModeC(path: [*:0]const u8, file_mode: Mode) OpenError!File {
- if (builtin.os == .windows) {
- const path_w = try windows.cStrToPrefixedFileW(path);
- return openWriteModeW(&path_w, file_mode);
- }
- const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
- const flags = O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC;
- const fd = try os.openC(path, flags, file_mode);
- return openHandle(fd);
+ /// Deprecated; call `std.fs.Dir.createFileC` directly.
+ pub fn openWriteModeC(path_c: [*:0]const u8, file_mode: Mode) OpenError!File {
+ return std.fs.cwd().createFileC(path_c, .{ .mode = file_mode });
}
- /// Same as `openWriteMode` except `path` is null-terminated and UTF16LE encoded
- /// TODO: deprecate this and move it to `std.fs.Dir`.
+ /// Deprecated; call `std.fs.Dir.createFileW` directly.
pub fn openWriteModeW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File {
- const handle = try windows.CreateFileW(
- path_w,
- windows.GENERIC_WRITE,
- windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
- null,
- windows.CREATE_ALWAYS,
- windows.FILE_ATTRIBUTE_NORMAL,
- null,
- );
- return openHandle(handle);
+ return std.fs.cwd().createFileW(path_w, .{ .mode = file_mode });
}
- /// If the path does not exist it will be created.
- /// If a file already exists in the destination this returns OpenError.PathAlreadyExists
- /// Call close to clean up.
- /// TODO: deprecate this and move it to `std.fs.Dir`.
+ /// Deprecated; call `std.fs.Dir.createFile` directly.
pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File {
- if (builtin.os == .windows) {
- const path_w = try windows.sliceToPrefixedFileW(path);
- return openWriteNoClobberW(&path_w, file_mode);
- }
- const path_c = try os.toPosixPath(path);
- return openWriteNoClobberC(&path_c, file_mode);
+ return std.fs.cwd().createFile(path, .{
+ .mode = file_mode,
+ .exclusive = true,
+ });
}
- /// TODO: deprecate this and move it to `std.fs.Dir`.
- pub fn openWriteNoClobberC(path: [*:0]const u8, file_mode: Mode) OpenError!File {
- if (builtin.os == .windows) {
- const path_w = try windows.cStrToPrefixedFileW(path);
- return openWriteNoClobberW(&path_w, file_mode);
- }
- const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
- const flags = O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_EXCL;
- const fd = try os.openC(path, flags, file_mode);
- return openHandle(fd);
+ /// Deprecated; call `std.fs.Dir.createFileC` directly.
+ pub fn openWriteNoClobberC(path_c: [*:0]const u8, file_mode: Mode) OpenError!File {
+ return std.fs.cwd().createFileC(path_c, .{
+ .mode = file_mode,
+ .exclusive = true,
+ });
}
- /// TODO: deprecate this and move it to `std.fs.Dir`.
+ /// Deprecated; call `std.fs.Dir.createFileW` directly.
pub fn openWriteNoClobberW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File {
- const handle = try windows.CreateFileW(
- path_w,
- windows.GENERIC_WRITE,
- windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
- null,
- windows.CREATE_NEW,
- windows.FILE_ATTRIBUTE_NORMAL,
- null,
- );
- return openHandle(handle);
+ return std.fs.cwd().createFileW(path_w, .{
+ .mode = file_mode,
+ .exclusive = true,
+ });
}
pub fn openHandle(handle: os.fd_t) File {
@@ -246,6 +228,7 @@ pub const File = struct {
windows.STATUS.SUCCESS => {},
windows.STATUS.BUFFER_OVERFLOW => {},
windows.STATUS.INVALID_PARAMETER => unreachable,
+ windows.STATUS.ACCESS_DENIED => return error.AccessDenied,
else => return windows.unexpectedStatus(rc),
}
return Stat{
diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig
@@ -130,6 +130,14 @@ test "join" {
testJoinPosix(&[_][]const u8{ "a/", "/c" }, "a/c");
}
+pub fn isAbsoluteC(path_c: [*:0]const u8) bool {
+ if (builtin.os == .windows) {
+ return isAbsoluteWindowsC(path_c);
+ } else {
+ return isAbsolutePosixC(path_c);
+ }
+}
+
pub fn isAbsolute(path: []const u8) bool {
if (builtin.os == .windows) {
return isAbsoluteWindows(path);
@@ -138,7 +146,7 @@ pub fn isAbsolute(path: []const u8) bool {
}
}
-pub fn isAbsoluteW(path_w: [*]const u16) bool {
+pub fn isAbsoluteW(path_w: [*:0]const u16) bool {
if (path_w[0] == '/')
return true;
@@ -176,10 +184,33 @@ pub fn isAbsoluteWindows(path: []const u8) bool {
return false;
}
+pub fn isAbsoluteWindowsC(path_c: [*:0]const u8) bool {
+ if (path_c[0] == '/')
+ return true;
+
+ if (path_c[0] == '\\') {
+ return true;
+ }
+ if (path_c[0] == 0 or path_c[1] == 0 or path_c[2] == 0) {
+ return false;
+ }
+ if (path_c[1] == ':') {
+ if (path_c[2] == '/')
+ return true;
+ if (path_c[2] == '\\')
+ return true;
+ }
+ return false;
+}
+
pub fn isAbsolutePosix(path: []const u8) bool {
return path[0] == sep_posix;
}
+pub fn isAbsolutePosixC(path_c: [*:0]const u8) bool {
+ return path_c[0] == sep_posix;
+}
+
test "isAbsoluteWindows" {
testIsAbsoluteWindows("/", true);
testIsAbsoluteWindows("//", true);
diff --git a/lib/std/io.zig b/lib/std/io.zig
@@ -61,17 +61,14 @@ pub const COutStream = @import("io/c_out_stream.zig").COutStream;
pub const InStream = @import("io/in_stream.zig").InStream;
pub const OutStream = @import("io/out_stream.zig").OutStream;
-/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
+/// Deprecated; use `std.fs.Dir.writeFile`.
pub fn writeFile(path: []const u8, data: []const u8) !void {
- var file = try File.openWrite(path);
- defer file.close();
- try file.write(data);
+ return fs.cwd().writeFile(path, data);
}
-/// On success, caller owns returned buffer.
-/// This function is deprecated; use `std.fs.Dir.readFileAlloc`.
+/// Deprecated; use `std.fs.Dir.readFileAlloc`.
pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
- return fs.Dir.cwd().readFileAlloc(allocator, path, math.maxInt(usize));
+ return fs.cwd().readFileAlloc(allocator, path, math.maxInt(usize));
}
pub fn BufferedInStream(comptime Error: type) type {
diff --git a/lib/std/io/test.zig b/lib/std/io/test.zig
@@ -14,12 +14,14 @@ test "write a file, read it, then delete it" {
var raw_bytes: [200 * 1024]u8 = undefined;
var allocator = &std.heap.FixedBufferAllocator.init(raw_bytes[0..]).allocator;
+ const cwd = fs.cwd();
+
var data: [1024]u8 = undefined;
var prng = DefaultPrng.init(1234);
prng.random.bytes(data[0..]);
const tmp_file_name = "temp_test_file.txt";
{
- var file = try File.openWrite(tmp_file_name);
+ var file = try cwd.createFile(tmp_file_name, .{});
defer file.close();
var file_out_stream = file.outStream();
@@ -32,8 +34,8 @@ test "write a file, read it, then delete it" {
}
{
- // make sure openWriteNoClobber doesn't harm the file
- if (File.openWriteNoClobber(tmp_file_name, File.default_mode)) |file| {
+ // Make sure the exclusive flag is honored.
+ if (cwd.createFile(tmp_file_name, .{ .exclusive = true })) |file| {
unreachable;
} else |err| {
std.debug.assert(err == File.OpenError.PathAlreadyExists);
@@ -41,7 +43,7 @@ test "write a file, read it, then delete it" {
}
{
- var file = try File.openRead(tmp_file_name);
+ var file = try cwd.openFile(tmp_file_name, .{});
defer file.close();
const file_size = try file.getEndPos();
@@ -58,7 +60,7 @@ test "write a file, read it, then delete it" {
expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], &data));
expect(mem.eql(u8, contents[contents.len - "end".len ..], "end"));
}
- try fs.deleteFile(tmp_file_name);
+ try cwd.deleteFile(tmp_file_name);
}
test "BufferOutStream" {
@@ -274,7 +276,7 @@ test "BitOutStream" {
test "BitStreams with File Stream" {
const tmp_file_name = "temp_test_file.txt";
{
- var file = try File.openWrite(tmp_file_name);
+ var file = try fs.cwd().createFile(tmp_file_name, .{});
defer file.close();
var file_out = file.outStream();
@@ -291,7 +293,7 @@ test "BitStreams with File Stream" {
try bit_stream.flushBits();
}
{
- var file = try File.openRead(tmp_file_name);
+ var file = try fs.cwd().openFile(tmp_file_name, .{});
defer file.close();
var file_in = file.inStream();
@@ -316,7 +318,7 @@ test "BitStreams with File Stream" {
expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1));
}
- try fs.deleteFile(tmp_file_name);
+ try fs.cwd().deleteFile(tmp_file_name);
}
fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
@@ -599,7 +601,7 @@ test "c out stream" {
const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile;
defer {
_ = std.c.fclose(out_file);
- fs.deleteFileC(filename) catch {};
+ fs.cwd().deleteFileC(filename) catch {};
}
const out_stream = &io.COutStream.init(out_file).stream;
@@ -608,10 +610,10 @@ test "c out stream" {
test "File seek ops" {
const tmp_file_name = "temp_test_file.txt";
- var file = try File.openWrite(tmp_file_name);
+ var file = try fs.cwd().createFile(tmp_file_name, .{});
defer {
file.close();
- fs.deleteFile(tmp_file_name) catch {};
+ fs.cwd().deleteFile(tmp_file_name) catch {};
}
try file.write(&([_]u8{0x55} ** 8192));
@@ -632,10 +634,10 @@ test "File seek ops" {
test "updateTimes" {
const tmp_file_name = "just_a_temporary_file.txt";
- var file = try File.openWrite(tmp_file_name);
+ var file = try fs.cwd().createFile(tmp_file_name, .{ .read = true });
defer {
file.close();
- std.fs.deleteFile(tmp_file_name) catch {};
+ std.fs.cwd().deleteFile(tmp_file_name) catch {};
}
var stat_old = try file.stat();
// Set atime and mtime to 5s before
diff --git a/lib/std/math.zig b/lib/std/math.zig
@@ -25,18 +25,6 @@ pub const ln2 = 0.693147180559945309417232121458176568;
/// ln(10)
pub const ln10 = 2.302585092994045684017991454684364208;
-/// π/2
-pub const pi_2 = 1.570796326794896619231321691639751442;
-
-/// π/4
-pub const pi_4 = 0.785398163397448309615660845819875721;
-
-/// 1/Ï€
-pub const one_pi = 0.318309886183790671537767526745028724;
-
-/// 2/Ï€
-pub const two_pi = 0.636619772367581343075535053490057448;
-
/// 2/sqrt(Ï€)
pub const two_sqrtpi = 1.128379167095512573896158903121545172;
diff --git a/lib/std/mutex.zig b/lib/std/mutex.zig
@@ -1,13 +1,12 @@
const std = @import("std.zig");
const builtin = @import("builtin");
const testing = std.testing;
-const SpinLock = std.SpinLock;
-const ThreadParker = std.ThreadParker;
+const ResetEvent = std.ResetEvent;
/// Lock may be held only once. If the same thread
/// tries to acquire the same mutex twice, it deadlocks.
-/// This type supports static initialization and is based off of Golang 1.13 runtime.lock_futex:
-/// https://github.com/golang/go/blob/master/src/runtime/lock_futex.go
+/// This type supports static initialization and is based off of Webkit's WTF Lock (via rust parking_lot)
+/// https://github.com/Amanieu/parking_lot/blob/master/core/src/word_lock.rs
/// When an application is built in single threaded release mode, all the functions are
/// no-ops. In single threaded debug mode, there is deadlock detection.
pub const Mutex = if (builtin.single_threaded)
@@ -39,80 +38,119 @@ pub const Mutex = if (builtin.single_threaded)
}
else
struct {
- state: State, // TODO: make this an enum
- parker: ThreadParker,
+ state: usize,
- const State = enum(u32) {
- Unlocked,
- Sleeping,
- Locked,
- };
+ const MUTEX_LOCK: usize = 1 << 0;
+ const QUEUE_LOCK: usize = 1 << 1;
+ const QUEUE_MASK: usize = ~(MUTEX_LOCK | QUEUE_LOCK);
+ const QueueNode = std.atomic.Stack(ResetEvent).Node;
/// number of iterations to spin yielding the cpu
const SPIN_CPU = 4;
- /// number of iterations to perform in the cpu yield loop
+ /// number of iterations to spin in the cpu yield loop
const SPIN_CPU_COUNT = 30;
/// number of iterations to spin yielding the thread
const SPIN_THREAD = 1;
pub fn init() Mutex {
- return Mutex{
- .state = .Unlocked,
- .parker = ThreadParker.init(),
- };
+ return Mutex{ .state = 0 };
}
pub fn deinit(self: *Mutex) void {
- self.parker.deinit();
+ self.* = undefined;
}
pub const Held = struct {
mutex: *Mutex,
pub fn release(self: Held) void {
- switch (@atomicRmw(State, &self.mutex.state, .Xchg, .Unlocked, .Release)) {
- .Locked => {},
- .Sleeping => self.mutex.parker.unpark(@ptrCast(*const u32, &self.mutex.state)),
- .Unlocked => unreachable, // unlocking an unlocked mutex
- else => unreachable, // should never be anything else
+ // since MUTEX_LOCK is the first bit, we can use (.Sub) instead of (.And, ~MUTEX_LOCK).
+ // this is because .Sub may be implemented more efficiently than the latter
+ // (e.g. `lock xadd` vs `cmpxchg` loop on x86)
+ const state = @atomicRmw(usize, &self.mutex.state, .Sub, MUTEX_LOCK, .Release);
+ if ((state & QUEUE_MASK) != 0 and (state & QUEUE_LOCK) == 0) {
+ self.mutex.releaseSlow(state);
}
}
};
pub fn acquire(self: *Mutex) Held {
- // Try and speculatively grab the lock.
- // If it fails, the state is either Locked or Sleeping
- // depending on if theres a thread stuck sleeping below.
- var state = @atomicRmw(State, &self.state, .Xchg, .Locked, .Acquire);
- if (state == .Unlocked)
- return Held{ .mutex = self };
+ // fast path close to SpinLock fast path
+ if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic)) |current_state| {
+ self.acquireSlow(current_state);
+ }
+ return Held{ .mutex = self };
+ }
+ fn acquireSlow(self: *Mutex, current_state: usize) void {
+ var spin: usize = 0;
+ var state = current_state;
while (true) {
- // try and acquire the lock using cpu spinning on failure
- var spin: usize = 0;
- while (spin < SPIN_CPU) : (spin += 1) {
- var value = @atomicLoad(State, &self.state, .Monotonic);
- while (value == .Unlocked)
- value = @cmpxchgWeak(State, &self.state, .Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self };
- SpinLock.yield(SPIN_CPU_COUNT);
+
+ // try and acquire the lock if unlocked
+ if ((state & MUTEX_LOCK) == 0) {
+ state = @cmpxchgWeak(usize, &self.state, state, state | MUTEX_LOCK, .Acquire, .Monotonic) orelse return;
+ continue;
+ }
+
+ // spin only if the waiting queue isn't empty and when it hasn't spun too much already
+ if ((state & QUEUE_MASK) == 0 and spin < SPIN_CPU + SPIN_THREAD) {
+ if (spin < SPIN_CPU) {
+ std.SpinLock.yield(SPIN_CPU_COUNT);
+ } else {
+ std.os.sched_yield() catch std.time.sleep(0);
+ }
+ state = @atomicLoad(usize, &self.state, .Monotonic);
+ continue;
}
- // try and acquire the lock using thread rescheduling on failure
- spin = 0;
- while (spin < SPIN_THREAD) : (spin += 1) {
- var value = @atomicLoad(State, &self.state, .Monotonic);
- while (value == .Unlocked)
- value = @cmpxchgWeak(State, &self.state, .Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self };
- std.os.sched_yield() catch std.time.sleep(1);
+ // thread should block, try and add this event to the waiting queue
+ var node = QueueNode{
+ .next = @intToPtr(?*QueueNode, state & QUEUE_MASK),
+ .data = ResetEvent.init(),
+ };
+ defer node.data.deinit();
+ const new_state = @ptrToInt(&node) | (state & ~QUEUE_MASK);
+ state = @cmpxchgWeak(usize, &self.state, state, new_state, .Release, .Monotonic) orelse {
+ // node is in the queue, wait until a `held.release()` wakes us up.
+ _ = node.data.wait(null) catch unreachable;
+ spin = 0;
+ state = @atomicLoad(usize, &self.state, .Monotonic);
+ continue;
+ };
+ }
+ }
+
+ fn releaseSlow(self: *Mutex, current_state: usize) void {
+ // grab the QUEUE_LOCK in order to signal a waiting queue node's event.
+ var state = current_state;
+ while (true) {
+ if ((state & QUEUE_LOCK) != 0 or (state & QUEUE_MASK) == 0)
+ return;
+ state = @cmpxchgWeak(usize, &self.state, state, state | QUEUE_LOCK, .Acquire, .Monotonic) orelse break;
+ }
+
+ while (true) {
+ // barrier needed to observe incoming state changes
+ defer @fence(.Acquire);
+
+ // the mutex is currently locked. try to unset the QUEUE_LOCK and let the locker wake up the next node.
+ // avoids waking up multiple sleeping threads which try to acquire the lock again which increases contention.
+ if ((state & MUTEX_LOCK) != 0) {
+ state = @cmpxchgWeak(usize, &self.state, state, state & ~QUEUE_LOCK, .Release, .Monotonic) orelse return;
+ continue;
}
- // failed to acquire the lock, go to sleep until woken up by `Held.release()`
- if (@atomicRmw(State, &self.state, .Xchg, .Sleeping, .Acquire) == .Unlocked)
- return Held{ .mutex = self };
- state = .Sleeping;
- self.parker.park(@ptrCast(*const u32, &self.state), @enumToInt(State.Sleeping));
+ // try to pop the top node on the waiting queue stack to wake it up
+ // while at the same time unsetting the QUEUE_LOCK.
+ const node = @intToPtr(*QueueNode, state & QUEUE_MASK);
+ const new_state = @ptrToInt(node.next) | (state & MUTEX_LOCK);
+ state = @cmpxchgWeak(usize, &self.state, state, new_state, .Release, .Monotonic) orelse {
+ _ = node.data.set(false);
+ return;
+ };
}
}
};
diff --git a/lib/std/net.zig b/lib/std/net.zig
@@ -812,7 +812,7 @@ fn linuxLookupNameFromHosts(
family: os.sa_family_t,
port: u16,
) !void {
- const file = fs.File.openReadC("/etc/hosts") catch |err| switch (err) {
+ const file = fs.openFileAbsoluteC("/etc/hosts", .{}) catch |err| switch (err) {
error.FileNotFound,
error.NotDir,
error.AccessDenied,
@@ -1006,7 +1006,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {
};
errdefer rc.deinit();
- const file = fs.File.openReadC("/etc/resolv.conf") catch |err| switch (err) {
+ const file = fs.openFileAbsoluteC("/etc/resolv.conf", .{}) catch |err| switch (err) {
error.FileNotFound,
error.NotDir,
error.AccessDenied,
diff --git a/lib/std/os.zig b/lib/std/os.zig
@@ -798,7 +798,7 @@ pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, e
path_buf[search_path.len] = '/';
mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice);
path_buf[search_path.len + file_slice.len + 1] = 0;
- // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731
+ // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
err = execveC(@ptrCast([*:0]u8, &path_buf), child_argv, envp);
switch (err) {
error.AccessDenied => seen_eacces = true,
@@ -834,7 +834,7 @@ pub fn execvpe(
@memcpy(arg_buf.ptr, arg.ptr, arg.len);
arg_buf[arg.len] = 0;
- // TODO avoid @ptrCast using slice syntax with https://github.com/ziglang/zig/issues/3731
+ // TODO avoid @ptrCast using slice syntax with https://github.com/ziglang/zig/issues/3770
argv_buf[i] = @ptrCast([*:0]u8, arg_buf.ptr);
}
argv_buf[argv_slice.len] = null;
@@ -842,7 +842,7 @@ pub fn execvpe(
const envp_buf = try createNullDelimitedEnvMap(allocator, env_map);
defer freeNullDelimitedEnvMap(allocator, envp_buf);
- // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731
+ // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
const argv_ptr = @ptrCast([*:null]?[*:0]u8, argv_buf.ptr);
return execvpeC(argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr);
@@ -863,12 +863,12 @@ pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.
@memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len);
env_buf[env_buf.len - 1] = 0;
- // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731
+ // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
envp_buf[i] = @ptrCast([*:0]u8, env_buf.ptr);
}
assert(i == envp_count);
}
- // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731
+ // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
assert(envp_buf[envp_count] == null);
return @ptrCast([*:null]?[*:0]u8, envp_buf.ptr)[0..envp_count];
}
@@ -1087,7 +1087,9 @@ pub const UnlinkatError = UnlinkError || error{
};
/// Delete a file name and possibly the file it refers to, based on an open directory handle.
+/// Asserts that the path parameter has no null bytes.
pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
+ if (std.debug.runtime_safety) for (file_path) |byte| assert(byte != 0);
if (builtin.os == .windows) {
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
return unlinkatW(dirfd, &file_path_w, flags);
@@ -2026,7 +2028,10 @@ pub fn waitpid(pid: i32, flags: u32) u32 {
}
}
-pub const FStatError = error{SystemResources} || UnexpectedError;
+pub const FStatError = error{
+ SystemResources,
+ AccessDenied,
+} || UnexpectedError;
pub fn fstat(fd: fd_t) FStatError!Stat {
var stat: Stat = undefined;
@@ -2036,6 +2041,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat {
EINVAL => unreachable,
EBADF => unreachable, // Always a race condition.
ENOMEM => return error.SystemResources,
+ EACCES => return error.AccessDenied,
else => |err| return unexpectedErrno(err),
}
}
@@ -2045,6 +2051,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat {
EINVAL => unreachable,
EBADF => unreachable, // Always a race condition.
ENOMEM => return error.SystemResources,
+ EACCES => return error.AccessDenied,
else => |err| return unexpectedErrno(err),
}
}
diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig
@@ -9,6 +9,7 @@ pub usingnamespace switch (builtin.arch) {
};
pub usingnamespace switch (builtin.arch) {
+ .i386 => @import("linux/i386.zig"),
.x86_64 => @import("linux/x86_64.zig"),
.aarch64 => @import("linux/arm64.zig"),
.arm => @import("linux/arm-eabi.zig"),
diff --git a/lib/std/os/bits/linux/arm-eabi.zig b/lib/std/os/bits/linux/arm-eabi.zig
@@ -466,7 +466,6 @@ pub const MAP_LOCKED = 0x2000;
/// don't check for reservations
pub const MAP_NORESERVE = 0x4000;
-pub const VDSO_USEFUL = true;
pub const VDSO_CGT_SYM = "__vdso_clock_gettime";
pub const VDSO_CGT_VER = "LINUX_2.6";
diff --git a/lib/std/os/bits/linux/arm64.zig b/lib/std/os/bits/linux/arm64.zig
@@ -358,7 +358,6 @@ pub const MAP_LOCKED = 0x2000;
/// don't check for reservations
pub const MAP_NORESERVE = 0x4000;
-pub const VDSO_USEFUL = true;
pub const VDSO_CGT_SYM = "__kernel_clock_gettime";
pub const VDSO_CGT_VER = "LINUX_2.6.39";
diff --git a/lib/std/os/bits/linux/i386.zig b/lib/std/os/bits/linux/i386.zig
@@ -0,0 +1,642 @@
+// i386-specific declarations that are intended to be imported into the POSIX namespace.
+// This does include Linux-only APIs.
+
+const std = @import("../../../std.zig");
+const linux = std.os.linux;
+const socklen_t = linux.socklen_t;
+const iovec = linux.iovec;
+const iovec_const = linux.iovec_const;
+const uid_t = linux.uid_t;
+const gid_t = linux.gid_t;
+const stack_t = linux.stack_t;
+const sigset_t = linux.sigset_t;
+
+pub const SYS_restart_syscall = 0;
+pub const SYS_exit = 1;
+pub const SYS_fork = 2;
+pub const SYS_read = 3;
+pub const SYS_write = 4;
+pub const SYS_open = 5;
+pub const SYS_close = 6;
+pub const SYS_waitpid = 7;
+pub const SYS_creat = 8;
+pub const SYS_link = 9;
+pub const SYS_unlink = 10;
+pub const SYS_execve = 11;
+pub const SYS_chdir = 12;
+pub const SYS_time = 13;
+pub const SYS_mknod = 14;
+pub const SYS_chmod = 15;
+pub const SYS_lchown = 16;
+pub const SYS_break = 17;
+pub const SYS_oldstat = 18;
+pub const SYS_lseek = 19;
+pub const SYS_getpid = 20;
+pub const SYS_mount = 21;
+pub const SYS_umount = 22;
+pub const SYS_setuid = 23;
+pub const SYS_getuid = 24;
+pub const SYS_stime = 25;
+pub const SYS_ptrace = 26;
+pub const SYS_alarm = 27;
+pub const SYS_oldfstat = 28;
+pub const SYS_pause = 29;
+pub const SYS_utime = 30;
+pub const SYS_stty = 31;
+pub const SYS_gtty = 32;
+pub const SYS_access = 33;
+pub const SYS_nice = 34;
+pub const SYS_ftime = 35;
+pub const SYS_sync = 36;
+pub const SYS_kill = 37;
+pub const SYS_rename = 38;
+pub const SYS_mkdir = 39;
+pub const SYS_rmdir = 40;
+pub const SYS_dup = 41;
+pub const SYS_pipe = 42;
+pub const SYS_times = 43;
+pub const SYS_prof = 44;
+pub const SYS_brk = 45;
+pub const SYS_setgid = 46;
+pub const SYS_getgid = 47;
+pub const SYS_signal = 48;
+pub const SYS_geteuid = 49;
+pub const SYS_getegid = 50;
+pub const SYS_acct = 51;
+pub const SYS_umount2 = 52;
+pub const SYS_lock = 53;
+pub const SYS_ioctl = 54;
+pub const SYS_fcntl = 55;
+pub const SYS_mpx = 56;
+pub const SYS_setpgid = 57;
+pub const SYS_ulimit = 58;
+pub const SYS_oldolduname = 59;
+pub const SYS_umask = 60;
+pub const SYS_chroot = 61;
+pub const SYS_ustat = 62;
+pub const SYS_dup2 = 63;
+pub const SYS_getppid = 64;
+pub const SYS_getpgrp = 65;
+pub const SYS_setsid = 66;
+pub const SYS_sigaction = 67;
+pub const SYS_sgetmask = 68;
+pub const SYS_ssetmask = 69;
+pub const SYS_setreuid = 70;
+pub const SYS_setregid = 71;
+pub const SYS_sigsuspend = 72;
+pub const SYS_sigpending = 73;
+pub const SYS_sethostname = 74;
+pub const SYS_setrlimit = 75;
+pub const SYS_getrlimit = 76;
+pub const SYS_getrusage = 77;
+pub const SYS_gettimeofday = 78;
+pub const SYS_settimeofday = 79;
+pub const SYS_getgroups = 80;
+pub const SYS_setgroups = 81;
+pub const SYS_select = 82;
+pub const SYS_symlink = 83;
+pub const SYS_oldlstat = 84;
+pub const SYS_readlink = 85;
+pub const SYS_uselib = 86;
+pub const SYS_swapon = 87;
+pub const SYS_reboot = 88;
+pub const SYS_readdir = 89;
+pub const SYS_mmap = 90;
+pub const SYS_munmap = 91;
+pub const SYS_truncate = 92;
+pub const SYS_ftruncate = 93;
+pub const SYS_fchmod = 94;
+pub const SYS_fchown = 95;
+pub const SYS_getpriority = 96;
+pub const SYS_setpriority = 97;
+pub const SYS_profil = 98;
+pub const SYS_statfs = 99;
+pub const SYS_fstatfs = 100;
+pub const SYS_ioperm = 101;
+pub const SYS_socketcall = 102;
+pub const SYS_syslog = 103;
+pub const SYS_setitimer = 104;
+pub const SYS_getitimer = 105;
+pub const SYS_stat = 106;
+pub const SYS_lstat = 107;
+pub const SYS_fstat = 108;
+pub const SYS_olduname = 109;
+pub const SYS_iopl = 110;
+pub const SYS_vhangup = 111;
+pub const SYS_idle = 112;
+pub const SYS_vm86old = 113;
+pub const SYS_wait4 = 114;
+pub const SYS_swapoff = 115;
+pub const SYS_sysinfo = 116;
+pub const SYS_ipc = 117;
+pub const SYS_fsync = 118;
+pub const SYS_sigreturn = 119;
+pub const SYS_clone = 120;
+pub const SYS_setdomainname = 121;
+pub const SYS_uname = 122;
+pub const SYS_modify_ldt = 123;
+pub const SYS_adjtimex = 124;
+pub const SYS_mprotect = 125;
+pub const SYS_sigprocmask = 126;
+pub const SYS_create_module = 127;
+pub const SYS_init_module = 128;
+pub const SYS_delete_module = 129;
+pub const SYS_get_kernel_syms = 130;
+pub const SYS_quotactl = 131;
+pub const SYS_getpgid = 132;
+pub const SYS_fchdir = 133;
+pub const SYS_bdflush = 134;
+pub const SYS_sysfs = 135;
+pub const SYS_personality = 136;
+pub const SYS_afs_syscall = 137;
+pub const SYS_setfsuid = 138;
+pub const SYS_setfsgid = 139;
+pub const SYS__llseek = 140;
+pub const SYS_getdents = 141;
+pub const SYS__newselect = 142;
+pub const SYS_flock = 143;
+pub const SYS_msync = 144;
+pub const SYS_readv = 145;
+pub const SYS_writev = 146;
+pub const SYS_getsid = 147;
+pub const SYS_fdatasync = 148;
+pub const SYS__sysctl = 149;
+pub const SYS_mlock = 150;
+pub const SYS_munlock = 151;
+pub const SYS_mlockall = 152;
+pub const SYS_munlockall = 153;
+pub const SYS_sched_setparam = 154;
+pub const SYS_sched_getparam = 155;
+pub const SYS_sched_setscheduler = 156;
+pub const SYS_sched_getscheduler = 157;
+pub const SYS_sched_yield = 158;
+pub const SYS_sched_get_priority_max = 159;
+pub const SYS_sched_get_priority_min = 160;
+pub const SYS_sched_rr_get_interval = 161;
+pub const SYS_nanosleep = 162;
+pub const SYS_mremap = 163;
+pub const SYS_setresuid = 164;
+pub const SYS_getresuid = 165;
+pub const SYS_vm86 = 166;
+pub const SYS_query_module = 167;
+pub const SYS_poll = 168;
+pub const SYS_nfsservctl = 169;
+pub const SYS_setresgid = 170;
+pub const SYS_getresgid = 171;
+pub const SYS_prctl = 172;
+pub const SYS_rt_sigreturn = 173;
+pub const SYS_rt_sigaction = 174;
+pub const SYS_rt_sigprocmask = 175;
+pub const SYS_rt_sigpending = 176;
+pub const SYS_rt_sigtimedwait = 177;
+pub const SYS_rt_sigqueueinfo = 178;
+pub const SYS_rt_sigsuspend = 179;
+pub const SYS_pread64 = 180;
+pub const SYS_pwrite64 = 181;
+pub const SYS_chown = 182;
+pub const SYS_getcwd = 183;
+pub const SYS_capget = 184;
+pub const SYS_capset = 185;
+pub const SYS_sigaltstack = 186;
+pub const SYS_sendfile = 187;
+pub const SYS_getpmsg = 188;
+pub const SYS_putpmsg = 189;
+pub const SYS_vfork = 190;
+pub const SYS_ugetrlimit = 191;
+pub const SYS_mmap2 = 192;
+pub const SYS_truncate64 = 193;
+pub const SYS_ftruncate64 = 194;
+pub const SYS_stat64 = 195;
+pub const SYS_lstat64 = 196;
+pub const SYS_fstat64 = 197;
+pub const SYS_lchown32 = 198;
+pub const SYS_getuid32 = 199;
+pub const SYS_getgid32 = 200;
+pub const SYS_geteuid32 = 201;
+pub const SYS_getegid32 = 202;
+pub const SYS_setreuid32 = 203;
+pub const SYS_setregid32 = 204;
+pub const SYS_getgroups32 = 205;
+pub const SYS_setgroups32 = 206;
+pub const SYS_fchown32 = 207;
+pub const SYS_setresuid32 = 208;
+pub const SYS_getresuid32 = 209;
+pub const SYS_setresgid32 = 210;
+pub const SYS_getresgid32 = 211;
+pub const SYS_chown32 = 212;
+pub const SYS_setuid32 = 213;
+pub const SYS_setgid32 = 214;
+pub const SYS_setfsuid32 = 215;
+pub const SYS_setfsgid32 = 216;
+pub const SYS_pivot_root = 217;
+pub const SYS_mincore = 218;
+pub const SYS_madvise = 219;
+pub const SYS_getdents64 = 220;
+pub const SYS_fcntl64 = 221;
+pub const SYS_gettid = 224;
+pub const SYS_readahead = 225;
+pub const SYS_setxattr = 226;
+pub const SYS_lsetxattr = 227;
+pub const SYS_fsetxattr = 228;
+pub const SYS_getxattr = 229;
+pub const SYS_lgetxattr = 230;
+pub const SYS_fgetxattr = 231;
+pub const SYS_listxattr = 232;
+pub const SYS_llistxattr = 233;
+pub const SYS_flistxattr = 234;
+pub const SYS_removexattr = 235;
+pub const SYS_lremovexattr = 236;
+pub const SYS_fremovexattr = 237;
+pub const SYS_tkill = 238;
+pub const SYS_sendfile64 = 239;
+pub const SYS_futex = 240;
+pub const SYS_sched_setaffinity = 241;
+pub const SYS_sched_getaffinity = 242;
+pub const SYS_set_thread_area = 243;
+pub const SYS_get_thread_area = 244;
+pub const SYS_io_setup = 245;
+pub const SYS_io_destroy = 246;
+pub const SYS_io_getevents = 247;
+pub const SYS_io_submit = 248;
+pub const SYS_io_cancel = 249;
+pub const SYS_fadvise64 = 250;
+pub const SYS_exit_group = 252;
+pub const SYS_lookup_dcookie = 253;
+pub const SYS_epoll_create = 254;
+pub const SYS_epoll_ctl = 255;
+pub const SYS_epoll_wait = 256;
+pub const SYS_remap_file_pages = 257;
+pub const SYS_set_tid_address = 258;
+pub const SYS_timer_create = 259;
+pub const SYS_timer_settime = SYS_timer_create + 1;
+pub const SYS_timer_gettime = SYS_timer_create + 2;
+pub const SYS_timer_getoverrun = SYS_timer_create + 3;
+pub const SYS_timer_delete = SYS_timer_create + 4;
+pub const SYS_clock_settime = SYS_timer_create + 5;
+pub const SYS_clock_gettime = SYS_timer_create + 6;
+pub const SYS_clock_getres = SYS_timer_create + 7;
+pub const SYS_clock_nanosleep = SYS_timer_create + 8;
+pub const SYS_statfs64 = 268;
+pub const SYS_fstatfs64 = 269;
+pub const SYS_tgkill = 270;
+pub const SYS_utimes = 271;
+pub const SYS_fadvise64_64 = 272;
+pub const SYS_vserver = 273;
+pub const SYS_mbind = 274;
+pub const SYS_get_mempolicy = 275;
+pub const SYS_set_mempolicy = 276;
+pub const SYS_mq_open = 277;
+pub const SYS_mq_unlink = SYS_mq_open + 1;
+pub const SYS_mq_timedsend = SYS_mq_open + 2;
+pub const SYS_mq_timedreceive = SYS_mq_open + 3;
+pub const SYS_mq_notify = SYS_mq_open + 4;
+pub const SYS_mq_getsetattr = SYS_mq_open + 5;
+pub const SYS_kexec_load = 283;
+pub const SYS_waitid = 284;
+pub const SYS_add_key = 286;
+pub const SYS_request_key = 287;
+pub const SYS_keyctl = 288;
+pub const SYS_ioprio_set = 289;
+pub const SYS_ioprio_get = 290;
+pub const SYS_inotify_init = 291;
+pub const SYS_inotify_add_watch = 292;
+pub const SYS_inotify_rm_watch = 293;
+pub const SYS_migrate_pages = 294;
+pub const SYS_openat = 295;
+pub const SYS_mkdirat = 296;
+pub const SYS_mknodat = 297;
+pub const SYS_fchownat = 298;
+pub const SYS_futimesat = 299;
+pub const SYS_fstatat64 = 300;
+pub const SYS_unlinkat = 301;
+pub const SYS_renameat = 302;
+pub const SYS_linkat = 303;
+pub const SYS_symlinkat = 304;
+pub const SYS_readlinkat = 305;
+pub const SYS_fchmodat = 306;
+pub const SYS_faccessat = 307;
+pub const SYS_pselect6 = 308;
+pub const SYS_ppoll = 309;
+pub const SYS_unshare = 310;
+pub const SYS_set_robust_list = 311;
+pub const SYS_get_robust_list = 312;
+pub const SYS_splice = 313;
+pub const SYS_sync_file_range = 314;
+pub const SYS_tee = 315;
+pub const SYS_vmsplice = 316;
+pub const SYS_move_pages = 317;
+pub const SYS_getcpu = 318;
+pub const SYS_epoll_pwait = 319;
+pub const SYS_utimensat = 320;
+pub const SYS_signalfd = 321;
+pub const SYS_timerfd_create = 322;
+pub const SYS_eventfd = 323;
+pub const SYS_fallocate = 324;
+pub const SYS_timerfd_settime = 325;
+pub const SYS_timerfd_gettime = 326;
+pub const SYS_signalfd4 = 327;
+pub const SYS_eventfd2 = 328;
+pub const SYS_epoll_create1 = 329;
+pub const SYS_dup3 = 330;
+pub const SYS_pipe2 = 331;
+pub const SYS_inotify_init1 = 332;
+pub const SYS_preadv = 333;
+pub const SYS_pwritev = 334;
+pub const SYS_rt_tgsigqueueinfo = 335;
+pub const SYS_perf_event_open = 336;
+pub const SYS_recvmmsg = 337;
+pub const SYS_fanotify_init = 338;
+pub const SYS_fanotify_mark = 339;
+pub const SYS_prlimit64 = 340;
+pub const SYS_name_to_handle_at = 341;
+pub const SYS_open_by_handle_at = 342;
+pub const SYS_clock_adjtime = 343;
+pub const SYS_syncfs = 344;
+pub const SYS_sendmmsg = 345;
+pub const SYS_setns = 346;
+pub const SYS_process_vm_readv = 347;
+pub const SYS_process_vm_writev = 348;
+pub const SYS_kcmp = 349;
+pub const SYS_finit_module = 350;
+pub const SYS_sched_setattr = 351;
+pub const SYS_sched_getattr = 352;
+pub const SYS_renameat2 = 353;
+pub const SYS_seccomp = 354;
+pub const SYS_getrandom = 355;
+pub const SYS_memfd_create = 356;
+pub const SYS_bpf = 357;
+pub const SYS_execveat = 358;
+pub const SYS_socket = 359;
+pub const SYS_socketpair = 360;
+pub const SYS_bind = 361;
+pub const SYS_connect = 362;
+pub const SYS_listen = 363;
+pub const SYS_accept4 = 364;
+pub const SYS_getsockopt = 365;
+pub const SYS_setsockopt = 366;
+pub const SYS_getsockname = 367;
+pub const SYS_getpeername = 368;
+pub const SYS_sendto = 369;
+pub const SYS_sendmsg = 370;
+pub const SYS_recvfrom = 371;
+pub const SYS_recvmsg = 372;
+pub const SYS_shutdown = 373;
+pub const SYS_userfaultfd = 374;
+pub const SYS_membarrier = 375;
+pub const SYS_mlock2 = 376;
+pub const SYS_copy_file_range = 377;
+pub const SYS_preadv2 = 378;
+pub const SYS_pwritev2 = 379;
+pub const SYS_pkey_mprotect = 380;
+pub const SYS_pkey_alloc = 381;
+pub const SYS_pkey_free = 382;
+pub const SYS_statx = 383;
+pub const SYS_arch_prctl = 384;
+pub const SYS_io_pgetevents = 385;
+pub const SYS_rseq = 386;
+pub const SYS_semget = 393;
+pub const SYS_semctl = 394;
+pub const SYS_shmget = 395;
+pub const SYS_shmctl = 396;
+pub const SYS_shmat = 397;
+pub const SYS_shmdt = 398;
+pub const SYS_msgget = 399;
+pub const SYS_msgsnd = 400;
+pub const SYS_msgrcv = 401;
+pub const SYS_msgctl = 402;
+pub const SYS_clock_gettime64 = 403;
+pub const SYS_clock_settime64 = 404;
+pub const SYS_clock_adjtime64 = 405;
+pub const SYS_clock_getres_time64 = 406;
+pub const SYS_clock_nanosleep_time64 = 407;
+pub const SYS_timer_gettime64 = 408;
+pub const SYS_timer_settime64 = 409;
+pub const SYS_timerfd_gettime64 = 410;
+pub const SYS_timerfd_settime64 = 411;
+pub const SYS_utimensat_time64 = 412;
+pub const SYS_pselect6_time64 = 413;
+pub const SYS_ppoll_time64 = 414;
+pub const SYS_io_pgetevents_time64 = 416;
+pub const SYS_recvmmsg_time64 = 417;
+pub const SYS_mq_timedsend_time64 = 418;
+pub const SYS_mq_timedreceive_time64 = 419;
+pub const SYS_semtimedop_time64 = 420;
+pub const SYS_rt_sigtimedwait_time64 = 421;
+pub const SYS_futex_time64 = 422;
+pub const SYS_sched_rr_get_interval_time64 = 423;
+pub const SYS_pidfd_send_signal = 424;
+pub const SYS_io_uring_setup = 425;
+pub const SYS_io_uring_enter = 426;
+pub const SYS_io_uring_register = 427;
+pub const SYS_open_tree = 428;
+pub const SYS_move_mount = 429;
+pub const SYS_fsopen = 430;
+pub const SYS_fsconfig = 431;
+pub const SYS_fsmount = 432;
+pub const SYS_fspick = 433;
+
+pub const O_CREAT = 0o100;
+pub const O_EXCL = 0o200;
+pub const O_NOCTTY = 0o400;
+pub const O_TRUNC = 0o1000;
+pub const O_APPEND = 0o2000;
+pub const O_NONBLOCK = 0o4000;
+pub const O_DSYNC = 0o10000;
+pub const O_SYNC = 0o4010000;
+pub const O_RSYNC = 0o4010000;
+pub const O_DIRECTORY = 0o200000;
+pub const O_NOFOLLOW = 0o400000;
+pub const O_CLOEXEC = 0o2000000;
+
+pub const O_ASYNC = 0o20000;
+pub const O_DIRECT = 0o40000;
+pub const O_LARGEFILE = 0o100000;
+pub const O_NOATIME = 0o1000000;
+pub const O_PATH = 0o10000000;
+pub const O_TMPFILE = 0o20200000;
+pub const O_NDELAY = O_NONBLOCK;
+
+pub const F_DUPFD = 0;
+pub const F_GETFD = 1;
+pub const F_SETFD = 2;
+pub const F_GETFL = 3;
+pub const F_SETFL = 4;
+
+pub const F_SETOWN = 8;
+pub const F_GETOWN = 9;
+pub const F_SETSIG = 10;
+pub const F_GETSIG = 11;
+
+pub const F_GETLK = 12;
+pub const F_SETLK = 13;
+pub const F_SETLKW = 14;
+
+pub const F_SETOWN_EX = 15;
+pub const F_GETOWN_EX = 16;
+
+pub const F_GETOWNER_UIDS = 17;
+
+pub const MAP_NORESERVE = 0x4000;
+pub const MAP_GROWSDOWN = 0x0100;
+pub const MAP_DENYWRITE = 0x0800;
+pub const MAP_EXECUTABLE = 0x1000;
+pub const MAP_LOCKED = 0x2000;
+pub const MAP_32BIT = 0x40;
+
+pub const MMAP2_UNIT = 4096;
+
+pub const VDSO_CGT_SYM = "__vdso_clock_gettime";
+pub const VDSO_CGT_VER = "LINUX_2.6";
+
+pub const msghdr = extern struct {
+ msg_name: ?*sockaddr,
+ msg_namelen: socklen_t,
+ msg_iov: [*]iovec,
+ msg_iovlen: i32,
+ msg_control: ?*c_void,
+ msg_controllen: socklen_t,
+ msg_flags: i32,
+};
+
+pub const msghdr_const = extern struct {
+ msg_name: ?*const sockaddr,
+ msg_namelen: socklen_t,
+ msg_iov: [*]iovec_const,
+ msg_iovlen: i32,
+ msg_control: ?*c_void,
+ msg_controllen: socklen_t,
+ msg_flags: i32,
+};
+
+pub const blksize_t = i32;
+pub const nlink_t = u32;
+pub const time_t = isize;
+pub const mode_t = u32;
+pub const off_t = i64;
+pub const ino_t = u64;
+pub const dev_t = u64;
+pub const blkcnt_t = i64;
+
+/// Renamed to Stat to not conflict with the stat function.
+/// atime, mtime, and ctime have functions to return `timespec`,
+/// because although this is a POSIX API, the layout and names of
+/// the structs are inconsistent across operating systems, and
+/// in C, macros are used to hide the differences. Here we use
+/// methods to accomplish this.
+pub const Stat = extern struct {
+ dev: dev_t,
+ __dev_padding: u32,
+ __ino_truncated: u32,
+ mode: mode_t,
+ nlink: nlink_t,
+ uid: uid_t,
+ gid: gid_t,
+ rdev: dev_t,
+ __rdev_padding: u32,
+ size: off_t,
+ blksize: blksize_t,
+ blocks: blkcnt_t,
+ atim: timespec,
+ mtim: timespec,
+ ctim: timespec,
+ ino: ino_t,
+
+ pub fn atime(self: Stat) timespec {
+ return self.atim;
+ }
+
+ pub fn mtime(self: Stat) timespec {
+ return self.mtim;
+ }
+
+ pub fn ctime(self: Stat) timespec {
+ return self.ctim;
+ }
+};
+
+pub const timespec = extern struct {
+ tv_sec: i32,
+ tv_nsec: i32,
+};
+
+pub const timeval = extern struct {
+ tv_sec: i32,
+ tv_usec: i32,
+};
+
+pub const timezone = extern struct {
+ tz_minuteswest: i32,
+ tz_dsttime: i32,
+};
+
+pub const mcontext_t = extern struct {
+ gregs: [19]usize,
+ fpregs: [*]u8,
+ oldmask: usize,
+ cr2: usize,
+};
+
+pub const REG_GS = 0;
+pub const REG_FS = 1;
+pub const REG_ES = 2;
+pub const REG_DS = 3;
+pub const REG_EDI = 4;
+pub const REG_ESI = 5;
+pub const REG_EBP = 6;
+pub const REG_ESP = 7;
+pub const REG_EBX = 8;
+pub const REG_EDX = 9;
+pub const REG_ECX = 10;
+pub const REG_EAX = 11;
+pub const REG_TRAPNO = 12;
+pub const REG_ERR = 13;
+pub const REG_EIP = 14;
+pub const REG_CS = 15;
+pub const REG_EFL = 16;
+pub const REG_UESP = 17;
+pub const REG_SS = 18;
+
+pub const ucontext_t = extern struct {
+ flags: usize,
+ link: *ucontext_t,
+ stack: stack_t,
+ mcontext: mcontext_t,
+ sigmask: sigset_t,
+ regspace: [64]u64,
+};
+
+pub const Elf_Symndx = u32;
+
+pub const user_desc = packed struct {
+ entry_number: u32,
+ base_addr: u32,
+ limit: u32,
+ seg_32bit: u1,
+ contents: u2,
+ read_exec_only: u1,
+ limit_in_pages: u1,
+ seg_not_present: u1,
+ useable: u1,
+};
+
+// socketcall() call numbers
+pub const SC_socket = 1;
+pub const SC_bind = 2;
+pub const SC_connect = 3;
+pub const SC_listen = 4;
+pub const SC_accept = 5;
+pub const SC_getsockname = 6;
+pub const SC_getpeername = 7;
+pub const SC_socketpair = 8;
+pub const SC_send = 9;
+pub const SC_recv = 10;
+pub const SC_sendto = 11;
+pub const SC_recvfrom = 12;
+pub const SC_shutdown = 13;
+pub const SC_setsockopt = 14;
+pub const SC_getsockopt = 15;
+pub const SC_sendmsg = 16;
+pub const SC_recvmsg = 17;
+pub const SC_accept4 = 18;
+pub const SC_recvmmsg = 19;
+pub const SC_sendmmsg = 20;
diff --git a/lib/std/os/bits/linux/mipsel.zig b/lib/std/os/bits/linux/mipsel.zig
@@ -454,7 +454,6 @@ pub const SO_PEERSEC = 30;
pub const SO_SNDBUFFORCE = 31;
pub const SO_RCVBUFFORCE = 33;
-pub const VDSO_USEFUL = true;
pub const VDSO_CGT_SYM = "__kernel_clock_gettime";
pub const VDSO_CGT_VER = "LINUX_2.6.39";
diff --git a/lib/std/os/bits/linux/x86_64.zig b/lib/std/os/bits/linux/x86_64.zig
@@ -420,7 +420,6 @@ pub const MAP_LOCKED = 0x2000;
/// don't check for reservations
pub const MAP_NORESERVE = 0x4000;
-pub const VDSO_USEFUL = true;
pub const VDSO_CGT_SYM = "__vdso_clock_gettime";
pub const VDSO_CGT_VER = "LINUX_2.6";
pub const VDSO_GETCPU_SYM = "__vdso_getcpu";
diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig
@@ -14,6 +14,7 @@ const vdso = @import("linux/vdso.zig");
const dl = @import("../dynamic_library.zig");
pub usingnamespace switch (builtin.arch) {
+ .i386 => @import("linux/i386.zig"),
.x86_64 => @import("linux/x86_64.zig"),
.aarch64 => @import("linux/arm64.zig"),
.arm => @import("linux/arm-eabi.zig"),
@@ -743,26 +744,44 @@ pub fn sigismember(set: *const sigset_t, sig: u6) bool {
}
pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_getsockname, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len) });
+ }
return syscall3(SYS_getsockname, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len));
}
pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_getpeername, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len) });
+ }
return syscall3(SYS_getpeername, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len));
}
pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_socket, &[3]usize{ domain, socket_type, protocol });
+ }
return syscall3(SYS_socket, domain, socket_type, protocol);
}
pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_setsockopt, &[5]usize{ @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen) });
+ }
return syscall5(SYS_setsockopt, @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen));
}
pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_getsockopt, &[5]usize{ @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen) });
+ }
return syscall5(SYS_getsockopt, @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen));
}
pub fn sendmsg(fd: i32, msg: *msghdr_const, flags: u32) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_sendmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags });
+ }
return syscall3(SYS_sendmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags);
}
@@ -807,42 +826,72 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize
}
pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_connect, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), len });
+ }
return syscall3(SYS_connect, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), len);
}
pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_recvmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags });
+ }
return syscall3(SYS_recvmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags);
}
pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_recvfrom, &[6]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen) });
+ }
return syscall6(SYS_recvfrom, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen));
}
pub fn shutdown(fd: i32, how: i32) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_shutdown, &[2]usize{ @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, how)) });
+ }
return syscall2(SYS_shutdown, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, how)));
}
pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_bind, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @intCast(usize, len) });
+ }
return syscall3(SYS_bind, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @intCast(usize, len));
}
pub fn listen(fd: i32, backlog: u32) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_listen, &[2]usize{ @bitCast(usize, @as(isize, fd)), backlog });
+ }
return syscall2(SYS_listen, @bitCast(usize, @as(isize, fd)), backlog);
}
pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_sendto, &[6]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen) });
+ }
return syscall6(SYS_sendto, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen));
}
pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_socketpair, &[4]usize{ @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0]) });
+ }
return syscall4(SYS_socketpair, @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0]));
}
pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_accept, &[4]usize{ fd, addr, len, 0 });
+ }
return accept4(fd, addr, len, 0);
}
pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize {
+ if (builtin.arch == .i386) {
+ return socketcall(SC_accept4, &[4]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len), flags });
+ }
return syscall4(SYS_accept4, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len), flags);
}
diff --git a/lib/std/os/linux/i386.zig b/lib/std/os/linux/i386.zig
@@ -0,0 +1,119 @@
+usingnamespace @import("../bits.zig");
+
+pub fn syscall0(number: usize) usize {
+ return asm volatile ("int $0x80"
+ : [ret] "={eax}" (-> usize)
+ : [number] "{eax}" (number)
+ : "memory"
+ );
+}
+
+pub fn syscall1(number: usize, arg1: usize) usize {
+ return asm volatile ("int $0x80"
+ : [ret] "={eax}" (-> usize)
+ : [number] "{eax}" (number),
+ [arg1] "{ebx}" (arg1)
+ : "memory"
+ );
+}
+
+pub fn syscall2(number: usize, arg1: usize, arg2: usize) usize {
+ return asm volatile ("int $0x80"
+ : [ret] "={eax}" (-> usize)
+ : [number] "{eax}" (number),
+ [arg1] "{ebx}" (arg1),
+ [arg2] "{ecx}" (arg2)
+ : "memory"
+ );
+}
+
+pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {
+ return asm volatile ("int $0x80"
+ : [ret] "={eax}" (-> usize)
+ : [number] "{eax}" (number),
+ [arg1] "{ebx}" (arg1),
+ [arg2] "{ecx}" (arg2),
+ [arg3] "{edx}" (arg3)
+ : "memory"
+ );
+}
+
+pub fn syscall4(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize {
+ return asm volatile ("int $0x80"
+ : [ret] "={eax}" (-> usize)
+ : [number] "{eax}" (number),
+ [arg1] "{ebx}" (arg1),
+ [arg2] "{ecx}" (arg2),
+ [arg3] "{edx}" (arg3),
+ [arg4] "{esi}" (arg4)
+ : "memory"
+ );
+}
+
+pub fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize {
+ return asm volatile ("int $0x80"
+ : [ret] "={eax}" (-> usize)
+ : [number] "{eax}" (number),
+ [arg1] "{ebx}" (arg1),
+ [arg2] "{ecx}" (arg2),
+ [arg3] "{edx}" (arg3),
+ [arg4] "{esi}" (arg4),
+ [arg5] "{edi}" (arg5)
+ : "memory"
+ );
+}
+
+pub fn syscall6(
+ number: usize,
+ arg1: usize,
+ arg2: usize,
+ arg3: usize,
+ arg4: usize,
+ arg5: usize,
+ arg6: usize,
+) usize {
+ return asm volatile (
+ \\ push %%ebp
+ \\ mov %[arg6], %%ebp
+ \\ int $0x80
+ \\ pop %%ebp
+ : [ret] "={eax}" (-> usize)
+ : [number] "{eax}" (number),
+ [arg1] "{ebx}" (arg1),
+ [arg2] "{ecx}" (arg2),
+ [arg3] "{edx}" (arg3),
+ [arg4] "{esi}" (arg4),
+ [arg5] "{edi}" (arg5),
+ [arg6] "rm" (arg6)
+ : "memory"
+ );
+}
+
+pub fn socketcall(call: usize, args: [*]usize) usize {
+ return asm volatile ("int $0x80"
+ : [ret] "={eax}" (-> usize)
+ : [number] "{eax}" (@as(usize, SYS_socketcall)),
+ [arg1] "{ebx}" (call),
+ [arg2] "{ecx}" (@ptrToInt(args))
+ : "memory"
+ );
+}
+
+/// This matches the libc clone function.
+pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
+
+pub nakedcc fn restore() void {
+ return asm volatile ("int $0x80"
+ :
+ : [number] "{eax}" (@as(usize, SYS_sigreturn))
+ : "memory"
+ );
+}
+
+pub nakedcc fn restore_rt() void {
+ return asm volatile ("int $0x80"
+ :
+ : [number] "{eax}" (@as(usize, SYS_rt_sigreturn))
+ : "memory"
+ );
+}
diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig
@@ -4,6 +4,7 @@ const linux = std.os.linux;
const mem = std.mem;
const elf = std.elf;
const expect = std.testing.expect;
+const fs = std.fs;
test "getpid" {
expect(linux.getpid() != 0);
@@ -45,14 +46,12 @@ test "timer" {
err = linux.epoll_wait(@intCast(i32, epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1);
}
-const File = std.fs.File;
-
test "statx" {
const tmp_file_name = "just_a_temporary_file.txt";
- var file = try File.openWrite(tmp_file_name);
+ var file = try fs.cwd().createFile(tmp_file_name, .{});
defer {
file.close();
- std.fs.deleteFile(tmp_file_name) catch {};
+ fs.cwd().deleteFile(tmp_file_name) catch {};
}
var statx_buf: linux.Statx = undefined;
diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig
@@ -109,12 +109,38 @@ const TLSImage = struct {
tcb_offset: usize,
dtv_offset: usize,
data_offset: usize,
+ // Only used on the i386 architecture
+ gdt_entry_number: usize,
};
pub var tls_image: ?TLSImage = null;
pub fn setThreadPointer(addr: usize) void {
switch (builtin.arch) {
+ .i386 => {
+ var user_desc = std.os.linux.user_desc{
+ .entry_number = tls_image.?.gdt_entry_number,
+ .base_addr = addr,
+ .limit = 0xfffff,
+ .seg_32bit = 1,
+ .contents = 0, // Data
+ .read_exec_only = 0,
+ .limit_in_pages = 1,
+ .seg_not_present = 0,
+ .useable = 1,
+ };
+ const rc = std.os.linux.syscall1(std.os.linux.SYS_set_thread_area, @ptrToInt(&user_desc));
+ assert(rc == 0);
+
+ const gdt_entry_number = user_desc.entry_number;
+ // We have to keep track of our slot as it's also needed for clone()
+ tls_image.?.gdt_entry_number = gdt_entry_number;
+ // Update the %gs selector
+ asm volatile ("movl %[gs_val], %%gs"
+ :
+ : [gs_val] "r" (gdt_entry_number << 3 | 3)
+ );
+ },
.x86_64 => {
const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl, std.os.linux.ARCH_SET_FS, addr);
assert(rc == 0);
@@ -238,6 +264,7 @@ pub fn initTLS() ?*elf.Phdr {
.tcb_offset = tcb_offset,
.dtv_offset = dtv_offset,
.data_offset = data_offset,
+ .gdt_entry_number = @bitCast(usize, @as(isize, -1)),
};
}
diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig
@@ -20,7 +20,7 @@ test "makePath, put some files in it, deleteTree" {
try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");
try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");
try fs.deleteTree("os_test_tmp");
- if (fs.Dir.cwd().openDirTraverse("os_test_tmp")) |dir| {
+ if (fs.cwd().openDirTraverse("os_test_tmp")) |dir| {
@panic("expected error");
} else |err| {
expect(err == error.FileNotFound);
@@ -111,7 +111,7 @@ test "AtomicFile" {
const content = try io.readFileAlloc(allocator, test_out_file);
expect(mem.eql(u8, content, test_content));
- try fs.deleteFile(test_out_file);
+ try fs.cwd().deleteFile(test_out_file);
}
test "thread local storage" {
diff --git a/lib/std/os/wasi.zig b/lib/std/os/wasi.zig
@@ -12,8 +12,8 @@ comptime {
assert(@alignOf(u16) == 2);
assert(@alignOf(i32) == 4);
assert(@alignOf(u32) == 4);
- assert(@alignOf(i64) == 8);
- assert(@alignOf(u64) == 8);
+ // assert(@alignOf(i64) == 8);
+ // assert(@alignOf(u64) == 8);
}
pub const iovec_t = iovec;
diff --git a/lib/std/parker.zig b/lib/std/parker.zig
@@ -1,180 +0,0 @@
-const std = @import("std.zig");
-const builtin = @import("builtin");
-const time = std.time;
-const testing = std.testing;
-const assert = std.debug.assert;
-const SpinLock = std.SpinLock;
-const linux = std.os.linux;
-const windows = std.os.windows;
-
-pub const ThreadParker = switch (builtin.os) {
- .linux => if (builtin.link_libc) PosixParker else LinuxParker,
- .windows => WindowsParker,
- else => if (builtin.link_libc) PosixParker else SpinParker,
-};
-
-const SpinParker = struct {
- pub fn init() SpinParker {
- return SpinParker{};
- }
- pub fn deinit(self: *SpinParker) void {}
-
- pub fn unpark(self: *SpinParker, ptr: *const u32) void {}
-
- pub fn park(self: *SpinParker, ptr: *const u32, expected: u32) void {
- var backoff = SpinLock.Backoff.init();
- while (@atomicLoad(u32, ptr, .Acquire) == expected)
- backoff.yield();
- }
-};
-
-const LinuxParker = struct {
- pub fn init() LinuxParker {
- return LinuxParker{};
- }
- pub fn deinit(self: *LinuxParker) void {}
-
- pub fn unpark(self: *LinuxParker, ptr: *const u32) void {
- const rc = linux.futex_wake(@ptrCast(*const i32, ptr), linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1);
- assert(linux.getErrno(rc) == 0);
- }
-
- pub fn park(self: *LinuxParker, ptr: *const u32, expected: u32) void {
- const value = @intCast(i32, expected);
- while (@atomicLoad(u32, ptr, .Acquire) == expected) {
- const rc = linux.futex_wait(@ptrCast(*const i32, ptr), linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, value, null);
- switch (linux.getErrno(rc)) {
- 0, linux.EAGAIN => return,
- linux.EINTR => continue,
- linux.EINVAL => unreachable,
- else => continue,
- }
- }
- }
-};
-
-const WindowsParker = struct {
- waiters: u32,
-
- pub fn init() WindowsParker {
- return WindowsParker{ .waiters = 0 };
- }
- pub fn deinit(self: *WindowsParker) void {}
-
- pub fn unpark(self: *WindowsParker, ptr: *const u32) void {
- const key = @ptrCast(*const c_void, ptr);
- const handle = getEventHandle() orelse return;
-
- var waiting = @atomicLoad(u32, &self.waiters, .Monotonic);
- while (waiting != 0) {
- waiting = @cmpxchgWeak(u32, &self.waiters, waiting, waiting - 1, .Acquire, .Monotonic) orelse {
- const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null);
- assert(rc == 0);
- return;
- };
- }
- }
-
- pub fn park(self: *WindowsParker, ptr: *const u32, expected: u32) void {
- var spin = SpinLock.Backoff.init();
- const ev_handle = getEventHandle();
- const key = @ptrCast(*const c_void, ptr);
-
- while (@atomicLoad(u32, ptr, .Monotonic) == expected) {
- if (ev_handle) |handle| {
- _ = @atomicRmw(u32, &self.waiters, .Add, 1, .Release);
- const rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, null);
- assert(rc == 0);
- } else {
- spin.yield();
- }
- }
- }
-
- var event_handle = std.lazyInit(windows.HANDLE);
-
- fn getEventHandle() ?windows.HANDLE {
- if (event_handle.get()) |handle_ptr|
- return handle_ptr.*;
- defer event_handle.resolve();
-
- const access_mask = windows.GENERIC_READ | windows.GENERIC_WRITE;
- if (windows.ntdll.NtCreateKeyedEvent(&event_handle.data, access_mask, null, 0) != 0)
- return null;
- return event_handle.data;
- }
-};
-
-const PosixParker = struct {
- cond: c.pthread_cond_t,
- mutex: c.pthread_mutex_t,
-
- const c = std.c;
-
- pub fn init() PosixParker {
- return PosixParker{
- .cond = c.PTHREAD_COND_INITIALIZER,
- .mutex = c.PTHREAD_MUTEX_INITIALIZER,
- };
- }
-
- pub fn deinit(self: *PosixParker) void {
- // On dragonfly, the destroy functions return EINVAL if they were initialized statically.
- const retm = c.pthread_mutex_destroy(&self.mutex);
- assert(retm == 0 or retm == (if (builtin.os == .dragonfly) os.EINVAL else 0));
- const retc = c.pthread_cond_destroy(&self.cond);
- assert(retc == 0 or retc == (if (builtin.os == .dragonfly) os.EINVAL else 0));
- }
-
- pub fn unpark(self: *PosixParker, ptr: *const u32) void {
- assert(c.pthread_mutex_lock(&self.mutex) == 0);
- defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
- assert(c.pthread_cond_signal(&self.cond) == 0);
- }
-
- pub fn park(self: *PosixParker, ptr: *const u32, expected: u32) void {
- assert(c.pthread_mutex_lock(&self.mutex) == 0);
- defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
- while (@atomicLoad(u32, ptr, .Acquire) == expected)
- assert(c.pthread_cond_wait(&self.cond, &self.mutex) == 0);
- }
-};
-
-test "std.ThreadParker" {
- if (builtin.single_threaded)
- return error.SkipZigTest;
-
- const Context = struct {
- parker: ThreadParker,
- data: u32,
-
- fn receiver(self: *@This()) void {
- self.parker.park(&self.data, 0); // receives 1
- assert(@atomicRmw(u32, &self.data, .Xchg, 2, .SeqCst) == 1); // sends 2
- self.parker.unpark(&self.data); // wakes up waiters on 2
- self.parker.park(&self.data, 2); // receives 3
- assert(@atomicRmw(u32, &self.data, .Xchg, 4, .SeqCst) == 3); // sends 4
- self.parker.unpark(&self.data); // wakes up waiters on 4
- }
-
- fn sender(self: *@This()) void {
- assert(@atomicRmw(u32, &self.data, .Xchg, 1, .SeqCst) == 0); // sends 1
- self.parker.unpark(&self.data); // wakes up waiters on 1
- self.parker.park(&self.data, 1); // receives 2
- assert(@atomicRmw(u32, &self.data, .Xchg, 3, .SeqCst) == 2); // sends 3
- self.parker.unpark(&self.data); // wakes up waiters on 3
- self.parker.park(&self.data, 3); // receives 4
- }
- };
-
- var context = Context{
- .parker = ThreadParker.init(),
- .data = 0,
- };
- defer context.parker.deinit();
-
- var receiver = try std.Thread.spawn(&context, Context.receiver);
- defer receiver.wait();
-
- context.sender();
-}
diff --git a/lib/std/pdb.zig b/lib/std/pdb.zig
@@ -6,6 +6,7 @@ const mem = std.mem;
const os = std.os;
const warn = std.debug.warn;
const coff = std.coff;
+const fs = std.fs;
const File = std.fs.File;
const ArrayList = std.ArrayList;
@@ -469,7 +470,7 @@ pub const Pdb = struct {
msf: Msf,
pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void {
- self.in_file = try File.openRead(file_name);
+ self.in_file = try fs.cwd().openFile(file_name, .{});
self.allocator = coff_ptr.allocator;
self.coff = coff_ptr;
diff --git a/lib/std/reset_event.zig b/lib/std/reset_event.zig
@@ -0,0 +1,433 @@
+const std = @import("std.zig");
+const builtin = @import("builtin");
+const testing = std.testing;
+const assert = std.debug.assert;
+const Backoff = std.SpinLock.Backoff;
+const c = std.c;
+const os = std.os;
+const time = std.time;
+const linux = os.linux;
+const windows = os.windows;
+
+/// A resource object which supports blocking until signaled.
+/// Once finished, the `deinit()` method should be called for correctness.
+pub const ResetEvent = struct {
+ os_event: OsEvent,
+
+ pub fn init() ResetEvent {
+ return ResetEvent{ .os_event = OsEvent.init() };
+ }
+
+ pub fn deinit(self: *ResetEvent) void {
+ self.os_event.deinit();
+ self.* = undefined;
+ }
+
+ /// Returns whether or not the event is currenetly set
+ pub fn isSet(self: *ResetEvent) bool {
+ return self.os_event.isSet();
+ }
+
+ /// Sets the event if not already set and
+ /// wakes up AT LEAST one thread waiting the event.
+ /// Returns whether or not a thread was woken up.
+ pub fn set(self: *ResetEvent, auto_reset: bool) bool {
+ return self.os_event.set(auto_reset);
+ }
+
+ /// Resets the event to its original, unset state.
+ /// Returns whether or not the event was currently set before un-setting.
+ pub fn reset(self: *ResetEvent) bool {
+ return self.os_event.reset();
+ }
+
+ const WaitError = error{
+ /// The thread blocked longer than the maximum time specified.
+ TimedOut,
+ };
+
+ /// Wait for the event to be set by blocking the current thread.
+ /// Optionally provided timeout in nanoseconds which throws an
+ /// `error.TimedOut` if the thread blocked AT LEAST longer than specified.
+ /// Returns whether or not the thread blocked from the event being unset at the time of calling.
+ pub fn wait(self: *ResetEvent, timeout_ns: ?u64) WaitError!bool {
+ return self.os_event.wait(timeout_ns);
+ }
+};
+
+const OsEvent = if (builtin.single_threaded) DebugEvent else switch (builtin.os) {
+ .windows => WindowsEvent,
+ .linux => if (builtin.link_libc) PosixEvent else LinuxEvent,
+ else => if (builtin.link_libc) PosixEvent else SpinEvent,
+};
+
+const DebugEvent = struct {
+ is_set: @typeOf(set_init),
+
+ const set_init = if (std.debug.runtime_safety) false else {};
+
+ pub fn init() DebugEvent {
+ return DebugEvent{ .is_set = set_init };
+ }
+
+ pub fn deinit(self: *DebugEvent) void {
+ self.* = undefined;
+ }
+
+ pub fn isSet(self: *DebugEvent) bool {
+ if (!std.debug.runtime_safety)
+ return true;
+ return self.is_set;
+ }
+
+ pub fn set(self: *DebugEvent, auto_reset: bool) bool {
+ if (std.debug.runtime_safety)
+ self.is_set = !auto_reset;
+ return false;
+ }
+
+ pub fn reset(self: *DebugEvent) bool {
+ if (!std.debug.runtime_safety)
+ return false;
+ const was_set = self.is_set;
+ self.is_set = false;
+ return was_set;
+ }
+
+ pub fn wait(self: *DebugEvent, timeout: ?u64) ResetEvent.WaitError!bool {
+ if (std.debug.runtime_safety and !self.is_set)
+ @panic("deadlock detected");
+ return ResetEvent.WaitError.TimedOut;
+ }
+};
+
+fn AtomicEvent(comptime FutexImpl: type) type {
+ return struct {
+ state: u32,
+
+ const IS_SET: u32 = 1 << 0;
+ const WAIT_MASK = ~IS_SET;
+
+ pub const Self = @This();
+ pub const Futex = FutexImpl;
+
+ pub fn init() Self {
+ return Self{ .state = 0 };
+ }
+
+ pub fn deinit(self: *Self) void {
+ self.* = undefined;
+ }
+
+ pub fn isSet(self: *const Self) bool {
+ const state = @atomicLoad(u32, &self.state, .Acquire);
+ return (state & IS_SET) != 0;
+ }
+
+ pub fn reset(self: *Self) bool {
+ const old_state = @atomicRmw(u32, &self.state, .Xchg, 0, .Monotonic);
+ return (old_state & IS_SET) != 0;
+ }
+
+ pub fn set(self: *Self, auto_reset: bool) bool {
+ const new_state = if (auto_reset) 0 else IS_SET;
+ const old_state = @atomicRmw(u32, &self.state, .Xchg, new_state, .Release);
+ if ((old_state & WAIT_MASK) == 0) {
+ return false;
+ }
+
+ Futex.wake(&self.state);
+ return true;
+ }
+
+ pub fn wait(self: *Self, timeout: ?u64) ResetEvent.WaitError!bool {
+ var dummy_value: u32 = undefined;
+ const wait_token = @truncate(u32, @ptrToInt(&dummy_value));
+
+ var state = @atomicLoad(u32, &self.state, .Monotonic);
+ while (true) {
+ if ((state & IS_SET) != 0)
+ return false;
+ state = @cmpxchgWeak(u32, &self.state, state, wait_token, .Acquire, .Monotonic) orelse break;
+ }
+
+ try Futex.wait(&self.state, wait_token, timeout);
+ return true;
+ }
+ };
+}
+
+const SpinEvent = AtomicEvent(struct {
+ fn wake(ptr: *const u32) void {}
+
+ fn wait(ptr: *const u32, expected: u32, timeout: ?u64) ResetEvent.WaitError!void {
+ // TODO: handle platforms where time.Timer.start() fails
+ var spin = Backoff.init();
+ var timer = if (timeout == null) null else time.Timer.start() catch unreachable;
+ while (@atomicLoad(u32, ptr, .Acquire) == expected) {
+ spin.yield();
+ if (timeout) |timeout_ns| {
+ if (timer.?.read() > timeout_ns)
+ return ResetEvent.WaitError.TimedOut;
+ }
+ }
+ }
+});
+
+const LinuxEvent = AtomicEvent(struct {
+ fn wake(ptr: *const u32) void {
+ const key = @ptrCast(*const i32, ptr);
+ const rc = linux.futex_wake(key, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1);
+ assert(linux.getErrno(rc) == 0);
+ }
+
+ fn wait(ptr: *const u32, expected: u32, timeout: ?u64) ResetEvent.WaitError!void {
+ var ts: linux.timespec = undefined;
+ var ts_ptr: ?*linux.timespec = null;
+ if (timeout) |timeout_ns| {
+ ts_ptr = &ts;
+ ts.tv_sec = @intCast(isize, timeout_ns / time.ns_per_s);
+ ts.tv_nsec = @intCast(isize, timeout_ns % time.ns_per_s);
+ }
+
+ const key = @ptrCast(*const i32, ptr);
+ const key_expect = @bitCast(i32, expected);
+ while (@atomicLoad(i32, key, .Acquire) == key_expect) {
+ const rc = linux.futex_wait(key, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, key_expect, ts_ptr);
+ switch (linux.getErrno(rc)) {
+ 0, linux.EAGAIN => break,
+ linux.EINTR => continue,
+ linux.ETIMEDOUT => return ResetEvent.WaitError.TimedOut,
+ else => unreachable,
+ }
+ }
+ }
+});
+
+const WindowsEvent = AtomicEvent(struct {
+ fn wake(ptr: *const u32) void {
+ if (getEventHandle()) |handle| {
+ const key = @ptrCast(*const c_void, ptr);
+ const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null);
+ assert(rc == 0);
+ }
+ }
+
+ fn wait(ptr: *const u32, expected: u32, timeout: ?u64) ResetEvent.WaitError!void {
+ // fallback to spinlock if NT Keyed Events arent available
+ const handle = getEventHandle() orelse {
+ return SpinEvent.Futex.wait(ptr, expected, timeout);
+ };
+
+ // NT uses timeouts in units of 100ns with negative value being relative
+ var timeout_ptr: ?*windows.LARGE_INTEGER = null;
+ var timeout_value: windows.LARGE_INTEGER = undefined;
+ if (timeout) |timeout_ns| {
+ timeout_ptr = &timeout_value;
+ timeout_value = -@intCast(windows.LARGE_INTEGER, timeout_ns / 100);
+ }
+
+ // NtWaitForKeyedEvent doesnt have spurious wake-ups
+ if (@atomicLoad(u32, ptr, .Acquire) == expected) {
+ const key = @ptrCast(*const c_void, ptr);
+ const rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, timeout_ptr);
+ switch (rc) {
+ 0 => {},
+ windows.WAIT_TIMEOUT => return ResetEvent.WaitError.TimedOut,
+ else => unreachable,
+ }
+ }
+ }
+
+ var keyed_state = State.Uninitialized;
+ var keyed_handle: ?windows.HANDLE = null;
+
+ const State = enum(u8) {
+ Uninitialized,
+ Intializing,
+ Initialized,
+ };
+
+ fn getEventHandle() ?windows.HANDLE {
+ var spin = Backoff.init();
+ var state = @atomicLoad(State, &keyed_state, .Monotonic);
+
+ while (true) {
+ switch (state) {
+ .Initialized => {
+ return keyed_handle;
+ },
+ .Intializing => {
+ spin.yield();
+ state = @atomicLoad(State, &keyed_state, .Acquire);
+ },
+ .Uninitialized => state = @cmpxchgWeak(State, &keyed_state, state, .Intializing, .Acquire, .Monotonic) orelse {
+ var handle: windows.HANDLE = undefined;
+ const access_mask = windows.GENERIC_READ | windows.GENERIC_WRITE;
+ if (windows.ntdll.NtCreateKeyedEvent(&handle, access_mask, null, 0) == 0)
+ keyed_handle = handle;
+ @atomicStore(State, &keyed_state, .Initialized, .Release);
+ return keyed_handle;
+ },
+ }
+ }
+ }
+});
+
+const PosixEvent = struct {
+ state: u32,
+ cond: c.pthread_cond_t,
+ mutex: c.pthread_mutex_t,
+
+ const IS_SET: u32 = 1;
+
+ pub fn init() PosixEvent {
+ return PosixEvent{
+ .state = .0,
+ .cond = c.PTHREAD_COND_INITIALIZER,
+ .mutex = c.PTHREAD_MUTEX_INITIALIZER,
+ };
+ }
+
+ pub fn deinit(self: *PosixEvent) void {
+ // On dragonfly, the destroy functions return EINVAL if they were initialized statically.
+ const retm = c.pthread_mutex_destroy(&self.mutex);
+ assert(retm == 0 or retm == (if (builtin.os == .dragonfly) os.EINVAL else 0));
+ const retc = c.pthread_cond_destroy(&self.cond);
+ assert(retc == 0 or retc == (if (builtin.os == .dragonfly) os.EINVAL else 0));
+ }
+
+ pub fn isSet(self: *PosixEvent) bool {
+ assert(c.pthread_mutex_lock(&self.mutex) == 0);
+ defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
+
+ return self.state == IS_SET;
+ }
+
+ pub fn reset(self: *PosixEvent) bool {
+ assert(c.pthread_mutex_lock(&self.mutex) == 0);
+ defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
+
+ const was_set = self.state == IS_SET;
+ self.state = 0;
+ return was_set;
+ }
+
+ pub fn set(self: *PosixEvent, auto_reset: bool) bool {
+ assert(c.pthread_mutex_lock(&self.mutex) == 0);
+ defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
+
+ const had_waiter = self.state > IS_SET;
+ self.state = if (auto_reset) 0 else IS_SET;
+ if (had_waiter) {
+ assert(c.pthread_cond_signal(&self.cond) == 0);
+ }
+ return had_waiter;
+ }
+
+ pub fn wait(self: *PosixEvent, timeout: ?u64) ResetEvent.WaitError!bool {
+ assert(c.pthread_mutex_lock(&self.mutex) == 0);
+ defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
+
+ if (self.state == IS_SET)
+ return false;
+
+ var ts: os.timespec = undefined;
+ if (timeout) |timeout_ns| {
+ var timeout_abs = timeout_ns;
+ if (comptime std.Target.current.isDarwin()) {
+ var tv: os.darwin.timeval = undefined;
+ assert(os.darwin.gettimeofday(&tv, null) == 0);
+ timeout_abs += @intCast(u64, tv.tv_sec) * time.second;
+ timeout_abs += @intCast(u64, tv.tv_usec) * time.microsecond;
+ } else {
+ os.clock_gettime(os.CLOCK_REALTIME, &ts) catch unreachable;
+ timeout_abs += @intCast(u64, ts.tv_sec) * time.second;
+ timeout_abs += @intCast(u64, ts.tv_nsec);
+ }
+ ts.tv_sec = @intCast(@typeOf(ts.tv_sec), @divFloor(timeout_abs, time.second));
+ ts.tv_nsec = @intCast(@typeOf(ts.tv_nsec), @mod(timeout_abs, time.second));
+ }
+
+ var dummy_value: u32 = undefined;
+ var wait_token = @truncate(u32, @ptrToInt(&dummy_value));
+ self.state = wait_token;
+
+ while (self.state == wait_token) {
+ const rc = switch (timeout == null) {
+ true => c.pthread_cond_wait(&self.cond, &self.mutex),
+ else => c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts),
+ };
+ // TODO: rc appears to be the positive error code making os.errno() always return 0 on linux
+ switch (std.math.max(@as(c_int, os.errno(rc)), rc)) {
+ 0 => {},
+ os.ETIMEDOUT => return ResetEvent.WaitError.TimedOut,
+ os.EINVAL => unreachable,
+ os.EPERM => unreachable,
+ else => unreachable,
+ }
+ }
+ return true;
+ }
+};
+
+test "std.ResetEvent" {
+ // TODO
+ if (builtin.single_threaded)
+ return error.SkipZigTest;
+
+ var event = ResetEvent.init();
+ defer event.deinit();
+
+ // test event setting
+ testing.expect(event.isSet() == false);
+ testing.expect(event.set(false) == false);
+ testing.expect(event.isSet() == true);
+
+ // test event resetting
+ testing.expect(event.reset() == true);
+ testing.expect(event.isSet() == false);
+ testing.expect(event.reset() == false);
+
+ // test cross thread signaling
+ const Context = struct {
+ event: ResetEvent,
+ value: u128,
+
+ fn receiver(self: *@This()) void {
+ // wait for the sender to notify us with updated value
+ assert(self.value == 0);
+ assert((self.event.wait(1 * time.second) catch unreachable) == true);
+ assert(self.value == 1);
+
+ // wait for sender to sleep, then notify it of new value
+ time.sleep(50 * time.millisecond);
+ self.value = 2;
+ assert(self.event.set(false) == true);
+ }
+
+ fn sender(self: *@This()) !void {
+ // wait for the receiver() to start wait()'ing
+ time.sleep(50 * time.millisecond);
+
+ // update value to 1 and notify the receiver()
+ assert(self.value == 0);
+ self.value = 1;
+ assert(self.event.set(true) == true);
+
+ // wait for the receiver to update the value & notify us
+ assert((try self.event.wait(1 * time.second)) == true);
+ assert(self.value == 2);
+ }
+ };
+
+ _ = event.reset();
+ var context = Context{
+ .event = event,
+ .value = 0,
+ };
+
+ var receiver = try std.Thread.spawn(&context, Context.receiver);
+ defer receiver.wait();
+ try context.sender();
+}
+\ No newline at end of file
diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig
@@ -197,6 +197,49 @@ extern fn __stack_chk_fail() noreturn {
// across .o file boundaries. fix comptime @ptrCast of nakedcc functions.
nakedcc fn clone() void {
switch (builtin.arch) {
+ .i386 => {
+ // __clone(func, stack, flags, arg, ptid, tls, ctid)
+ // +8, +12, +16, +20, +24, +28, +32
+ // syscall(SYS_clone, flags, stack, ptid, tls, ctid)
+ // eax, ebx, ecx, edx, esi, edi
+ asm volatile (
+ \\ push %%ebp
+ \\ mov %%esp,%%ebp
+ \\ push %%ebx
+ \\ push %%esi
+ \\ push %%edi
+ \\ // Setup the arguments
+ \\ mov 16(%%ebp),%%ebx
+ \\ mov 12(%%ebp),%%ecx
+ \\ and $-16,%%ecx
+ \\ sub $20,%%ecx
+ \\ mov 20(%%ebp),%%eax
+ \\ mov %%eax,4(%%ecx)
+ \\ mov 8(%%ebp),%%eax
+ \\ mov %%eax,0(%%ecx)
+ \\ mov 24(%%ebp),%%edx
+ \\ mov 28(%%ebp),%%esi
+ \\ mov 32(%%ebp),%%edi
+ \\ mov $120,%%eax
+ \\ int $128
+ \\ test %%eax,%%eax
+ \\ jnz 1f
+ \\ pop %%eax
+ \\ xor %%ebp,%%ebp
+ \\ call *%%eax
+ \\ mov %%eax,%%ebx
+ \\ xor %%eax,%%eax
+ \\ inc %%eax
+ \\ int $128
+ \\ hlt
+ \\1:
+ \\ pop %%edi
+ \\ pop %%esi
+ \\ pop %%ebx
+ \\ pop %%ebp
+ \\ ret
+ );
+ },
.x86_64 => {
asm volatile (
\\ xor %%eax,%%eax
diff --git a/lib/std/std.zig b/lib/std/std.zig
@@ -16,6 +16,7 @@ pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;
pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian;
pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
pub const Progress = @import("progress.zig").Progress;
+pub const ResetEvent = @import("reset_event.zig").ResetEvent;
pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList;
pub const SpinLock = @import("spinlock.zig").SpinLock;
@@ -23,7 +24,6 @@ pub const StringHashMap = @import("hash_map.zig").StringHashMap;
pub const TailQueue = @import("linked_list.zig").TailQueue;
pub const Target = @import("target.zig").Target;
pub const Thread = @import("thread.zig").Thread;
-pub const ThreadParker = @import("parker.zig").ThreadParker;
pub const atomic = @import("atomic.zig");
pub const base64 = @import("base64.zig");
diff --git a/lib/std/testing.zig b/lib/std/testing.zig
@@ -89,7 +89,26 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void {
if (union_info.tag_type == null) {
@compileError("Unable to compare untagged union values");
}
- @compileError("TODO implement testing.expectEqual for tagged unions");
+
+ const TagType = @TagType(@typeOf(expected));
+
+ const expectedTag = @as(TagType, expected);
+ const actualTag = @as(TagType, actual);
+
+ expectEqual(expectedTag, actualTag);
+
+ // we only reach this loop if the tags are equal
+ inline for (std.meta.fields(@typeOf(actual))) |fld| {
+ if (std.mem.eql(u8, fld.name, @tagName(actualTag))) {
+ expectEqual(@field(expected, fld.name), @field(actual, fld.name));
+ return;
+ }
+ }
+
+ // we iterate over *all* union fields
+ // => we should never get here as the loop above is
+ // including all possible values.
+ unreachable;
},
.Optional => {
@@ -124,6 +143,19 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void {
}
}
+test "expectEqual.union(enum)"
+{
+ const T = union(enum) {
+ a: i32,
+ b: f32,
+ };
+
+ const a10 = T { .a = 10 };
+ const a20 = T { .a = 20 };
+
+ expectEqual(a10, a10);
+}
+
/// This function is intended to be used only in tests. When the two slices are not
/// equal, prints diagnostics to stderr to show exactly how they are not equal,
/// then aborts.
diff --git a/lib/std/thread.zig b/lib/std/thread.zig
@@ -314,11 +314,38 @@ pub const Thread = struct {
os.CLONE_THREAD | os.CLONE_SYSVSEM | os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID |
os.CLONE_DETACHED;
var newtls: usize = undefined;
+ // This structure is only needed when targeting i386
+ var user_desc: if (builtin.arch == .i386) os.linux.user_desc else void = undefined;
+
if (os.linux.tls.tls_image) |tls_img| {
- newtls = os.linux.tls.copyTLS(mmap_addr + tls_start_offset);
+ if (builtin.arch == .i386) {
+ user_desc = os.linux.user_desc{
+ .entry_number = tls_img.gdt_entry_number,
+ .base_addr = os.linux.tls.copyTLS(mmap_addr + tls_start_offset),
+ .limit = 0xfffff,
+ .seg_32bit = 1,
+ .contents = 0, // Data
+ .read_exec_only = 0,
+ .limit_in_pages = 1,
+ .seg_not_present = 0,
+ .useable = 1,
+ };
+ newtls = @ptrToInt(&user_desc);
+ } else {
+ newtls = os.linux.tls.copyTLS(mmap_addr + tls_start_offset);
+ }
flags |= os.CLONE_SETTLS;
}
- const rc = os.linux.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
+
+ const rc = os.linux.clone(
+ MainFuncs.linuxThreadMain,
+ mmap_addr + stack_end_offset,
+ flags,
+ arg,
+ &thread_ptr.data.handle,
+ newtls,
+ &thread_ptr.data.handle,
+ );
switch (os.errno(rc)) {
0 => return thread_ptr,
os.EAGAIN => return error.ThreadQuotaExceeded,
diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig
@@ -25,10 +25,10 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
const context = llvm_handle.node.data;
- const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory;
+ const module = llvm.ModuleCreateWithNameInContext(comp.name.toSliceConst(), context) orelse return error.OutOfMemory;
defer llvm.DisposeModule(module);
- llvm.SetTarget(module, comp.llvm_triple.ptr());
+ llvm.SetTarget(module, comp.llvm_triple.toSliceConst());
llvm.SetDataLayout(module, comp.target_layout_str);
if (util.getObjectFormat(comp.target) == .coff) {
@@ -48,23 +48,23 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
const producer = try std.Buffer.allocPrint(
&code.arena.allocator,
"zig {}.{}.{}",
- u32(c.ZIG_VERSION_MAJOR),
- u32(c.ZIG_VERSION_MINOR),
- u32(c.ZIG_VERSION_PATCH),
+ @as(u32, c.ZIG_VERSION_MAJOR),
+ @as(u32, c.ZIG_VERSION_MINOR),
+ @as(u32, c.ZIG_VERSION_PATCH),
);
const flags = "";
const runtime_version = 0;
const compile_unit_file = llvm.CreateFile(
dibuilder,
- comp.name.ptr(),
- comp.root_package.root_src_dir.ptr(),
+ comp.name.toSliceConst(),
+ comp.root_package.root_src_dir.toSliceConst(),
) orelse return error.OutOfMemory;
const is_optimized = comp.build_mode != .Debug;
const compile_unit = llvm.CreateCompileUnit(
dibuilder,
DW.LANG_C99,
compile_unit_file,
- producer.ptr(),
+ producer.toSliceConst(),
is_optimized,
flags,
runtime_version,
@@ -99,7 +99,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
// verify the llvm module when safety is on
if (std.debug.runtime_safety) {
- var error_ptr: ?[*]u8 = null;
+ var error_ptr: ?[*:0]u8 = null;
_ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr);
}
@@ -108,12 +108,12 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
const is_small = comp.build_mode == .ReleaseSmall;
const is_debug = comp.build_mode == .Debug;
- var err_msg: [*]u8 = undefined;
+ var err_msg: [*:0]u8 = undefined;
// TODO integrate this with evented I/O
if (llvm.TargetMachineEmitToFile(
comp.target_machine,
module,
- output_path.ptr(),
+ output_path.toSliceConst(),
llvm.EmitBinary,
&err_msg,
is_debug,
@@ -154,7 +154,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
- fn_val.symbol_name.ptr(),
+ fn_val.symbol_name.toSliceConst(),
llvm_fn_type,
) orelse return error.OutOfMemory;
@@ -379,7 +379,7 @@ fn renderLoadUntyped(
ptr: *llvm.Value,
alignment: Type.Pointer.Align,
vol: Type.Pointer.Vol,
- name: [*]const u8,
+ name: [*:0]const u8,
) !*llvm.Value {
const result = llvm.BuildLoad(ofile.builder, ptr, name) orelse return error.OutOfMemory;
switch (vol) {
@@ -390,7 +390,7 @@ fn renderLoadUntyped(
return result;
}
-fn renderLoad(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer, name: [*]const u8) !*llvm.Value {
+fn renderLoad(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer, name: [*:0]const u8) !*llvm.Value {
return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name);
}
@@ -438,7 +438,7 @@ pub fn renderAlloca(
) !*llvm.Value {
const llvm_var_type = try var_type.getLlvmType(ofile.arena, ofile.context);
const name_with_null = try std.cstr.addNullByte(ofile.arena, name);
- const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, name_with_null.ptr) orelse return error.OutOfMemory;
+ const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, @ptrCast([*:0]const u8, name_with_null.ptr)) orelse return error.OutOfMemory;
llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm_var_type));
return result;
}
diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig
@@ -93,7 +93,7 @@ pub const ZigCompiler = struct {
return LlvmHandle{ .node = node };
}
- pub async fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
+ pub fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
if (self.native_libc.start()) |ptr| return ptr;
try self.native_libc.data.findNative(self.allocator);
self.native_libc.resolve();
@@ -133,62 +133,62 @@ pub const Compilation = struct {
zig_std_dir: []const u8,
/// lazily created when we need it
- tmp_dir: event.Future(BuildError![]u8),
+ tmp_dir: event.Future(BuildError![]u8) = event.Future(BuildError![]u8).init(),
- version_major: u32,
- version_minor: u32,
- version_patch: u32,
+ version_major: u32 = 0,
+ version_minor: u32 = 0,
+ version_patch: u32 = 0,
- linker_script: ?[]const u8,
- out_h_path: ?[]const u8,
+ linker_script: ?[]const u8 = null,
+ out_h_path: ?[]const u8 = null,
- is_test: bool,
- each_lib_rpath: bool,
- strip: bool,
+ is_test: bool = false,
+ each_lib_rpath: bool = false,
+ strip: bool = false,
is_static: bool,
- linker_rdynamic: bool,
+ linker_rdynamic: bool = false,
- clang_argv: []const []const u8,
- lib_dirs: []const []const u8,
- rpath_list: []const []const u8,
- assembly_files: []const []const u8,
+ clang_argv: []const []const u8 = [_][]const u8{},
+ lib_dirs: []const []const u8 = [_][]const u8{},
+ rpath_list: []const []const u8 = [_][]const u8{},
+ assembly_files: []const []const u8 = [_][]const u8{},
/// paths that are explicitly provided by the user to link against
- link_objects: []const []const u8,
+ link_objects: []const []const u8 = [_][]const u8{},
/// functions that have their own objects that we need to link
/// it uses an optional pointer so that tombstone removals are possible
- fn_link_set: event.Locked(FnLinkSet),
+ fn_link_set: event.Locked(FnLinkSet) = event.Locked(FnLinkSet).init(FnLinkSet.init()),
pub const FnLinkSet = std.TailQueue(?*Value.Fn);
- windows_subsystem_windows: bool,
- windows_subsystem_console: bool,
+ windows_subsystem_windows: bool = false,
+ windows_subsystem_console: bool = false,
link_libs_list: ArrayList(*LinkLib),
- libc_link_lib: ?*LinkLib,
+ libc_link_lib: ?*LinkLib = null,
- err_color: errmsg.Color,
+ err_color: errmsg.Color = .Auto,
- verbose_tokenize: bool,
- verbose_ast_tree: bool,
- verbose_ast_fmt: bool,
- verbose_cimport: bool,
- verbose_ir: bool,
- verbose_llvm_ir: bool,
- verbose_link: bool,
+ verbose_tokenize: bool = false,
+ verbose_ast_tree: bool = false,
+ verbose_ast_fmt: bool = false,
+ verbose_cimport: bool = false,
+ verbose_ir: bool = false,
+ verbose_llvm_ir: bool = false,
+ verbose_link: bool = false,
- darwin_frameworks: []const []const u8,
- darwin_version_min: DarwinVersionMin,
+ darwin_frameworks: []const []const u8 = [_][]const u8{},
+ darwin_version_min: DarwinVersionMin = .None,
- test_filters: []const []const u8,
- test_name_prefix: ?[]const u8,
+ test_filters: []const []const u8 = [_][]const u8{},
+ test_name_prefix: ?[]const u8 = null,
- emit_file_type: Emit,
+ emit_file_type: Emit = .Binary,
kind: Kind,
- link_out_file: ?[]const u8,
+ link_out_file: ?[]const u8 = null,
events: *event.Channel(Event),
exported_symbol_names: event.Locked(Decl.Table),
@@ -213,7 +213,7 @@ pub const Compilation = struct {
target_machine: *llvm.TargetMachine,
target_data_ref: *llvm.TargetData,
- target_layout_str: [*]u8,
+ target_layout_str: [*:0]u8,
target_ptr_bits: u32,
/// for allocating things which have the same lifetime as this Compilation
@@ -222,16 +222,16 @@ pub const Compilation = struct {
root_package: *Package,
std_package: *Package,
- override_libc: ?*LibCInstallation,
+ override_libc: ?*LibCInstallation = null,
/// need to wait on this group before deinitializing
deinit_group: event.Group(void),
- // destroy_frame: @Frame(createAsync),
- // main_loop_frame: @Frame(Compilation.mainLoop),
- main_loop_future: event.Future(void),
+ destroy_frame: *@Frame(createAsync),
+ main_loop_frame: *@Frame(Compilation.mainLoop),
+ main_loop_future: event.Future(void) = event.Future(void).init(),
- have_err_ret_tracing: bool,
+ have_err_ret_tracing: bool = false,
/// not locked because it is read-only
primitive_type_table: TypeTable,
@@ -243,7 +243,9 @@ pub const Compilation = struct {
c_int_types: [CInt.list.len]*Type.Int,
- // fs_watch: *fs.Watch(*Scope.Root),
+ fs_watch: *fs.Watch(*Scope.Root),
+
+ cancelled: bool = false,
const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
@@ -348,7 +350,9 @@ pub const Compilation = struct {
zig_lib_dir: []const u8,
) !*Compilation {
var optional_comp: ?*Compilation = null;
- var frame = async createAsync(
+ var frame = try zig_compiler.allocator.create(@Frame(createAsync));
+ errdefer zig_compiler.allocator.destroy(frame);
+ frame.* = async createAsync(
&optional_comp,
zig_compiler,
name,
@@ -359,11 +363,11 @@ pub const Compilation = struct {
is_static,
zig_lib_dir,
);
+ // TODO causes segfault
+ // return optional_comp orelse if (await frame) |_| unreachable else |err| err;
if (optional_comp) |comp| {
return comp;
- } else {
- if (await frame) |_| unreachable else |err| return err;
- }
+ } else if (await frame) |_| unreachable else |err| return err;
}
async fn createAsync(
@@ -389,50 +393,13 @@ pub const Compilation = struct {
.build_mode = build_mode,
.zig_lib_dir = zig_lib_dir,
.zig_std_dir = undefined,
- .tmp_dir = event.Future(BuildError![]u8).init(),
- // .destroy_frame = @frame(),
- // .main_loop_frame = undefined,
- .main_loop_future = event.Future(void).init(),
+ .destroy_frame = @frame(),
+ .main_loop_frame = undefined,
.name = undefined,
.llvm_triple = undefined,
-
- .version_major = 0,
- .version_minor = 0,
- .version_patch = 0,
-
- .verbose_tokenize = false,
- .verbose_ast_tree = false,
- .verbose_ast_fmt = false,
- .verbose_cimport = false,
- .verbose_ir = false,
- .verbose_llvm_ir = false,
- .verbose_link = false,
-
- .linker_script = null,
- .out_h_path = null,
- .is_test = false,
- .each_lib_rpath = false,
- .strip = false,
.is_static = is_static,
- .linker_rdynamic = false,
- .clang_argv = &[_][]const u8{},
- .lib_dirs = &[_][]const u8{},
- .rpath_list = &[_][]const u8{},
- .assembly_files = &[_][]const u8{},
- .link_objects = &[_][]const u8{},
- .fn_link_set = event.Locked(FnLinkSet).init(FnLinkSet.init()),
- .windows_subsystem_windows = false,
- .windows_subsystem_console = false,
.link_libs_list = undefined,
- .libc_link_lib = null,
- .err_color = errmsg.Color.Auto,
- .darwin_frameworks = &[_][]const u8{},
- .darwin_version_min = DarwinVersionMin.None,
- .test_filters = &[_][]const u8{},
- .test_name_prefix = null,
- .emit_file_type = Emit.Binary,
- .link_out_file = null,
.exported_symbol_names = event.Locked(Decl.Table).init(Decl.Table.init(allocator)),
.prelink_group = event.Group(BuildError!void).init(allocator),
.deinit_group = event.Group(void).init(allocator),
@@ -462,11 +429,9 @@ pub const Compilation = struct {
.root_package = undefined,
.std_package = undefined,
- .override_libc = null,
- .have_err_ret_tracing = false,
.primitive_type_table = undefined,
- // .fs_watch = undefined,
+ .fs_watch = undefined,
};
comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
comp.primitive_type_table = TypeTable.init(comp.arena());
@@ -538,13 +503,16 @@ pub const Compilation = struct {
comp.root_package = try Package.create(comp.arena(), ".", "");
}
- // comp.fs_watch = try fs.Watch(*Scope.Root).create(16);
- // defer comp.fs_watch.destroy();
+ comp.fs_watch = try fs.Watch(*Scope.Root).init(allocator, 16);
+ defer comp.fs_watch.deinit();
try comp.initTypes();
defer comp.primitive_type_table.deinit();
- // comp.main_loop_frame = async comp.mainLoop();
+ comp.main_loop_frame = try allocator.create(@Frame(mainLoop));
+ defer allocator.destroy(comp.main_loop_frame);
+
+ comp.main_loop_frame.* = async comp.mainLoop();
// Set this to indicate that initialization completed successfully.
// from here on out we must not return an error.
// This must occur before the first suspend/await.
@@ -563,7 +531,7 @@ pub const Compilation = struct {
}
/// it does ref the result because it could be an arbitrary integer size
- pub async fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type {
+ pub fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type {
if (name.len >= 2) {
switch (name[0]) {
'i', 'u' => blk: {
@@ -757,8 +725,11 @@ pub const Compilation = struct {
}
pub fn destroy(self: *Compilation) void {
- // await self.main_loop_frame;
- // resume self.destroy_frame;
+ const allocator = self.gpa();
+ self.cancelled = true;
+ await self.main_loop_frame;
+ resume self.destroy_frame;
+ allocator.destroy(self.destroy_frame);
}
fn start(self: *Compilation) void {
@@ -771,7 +742,7 @@ pub const Compilation = struct {
var build_result = self.initialCompile();
- while (true) {
+ while (!self.cancelled) {
const link_result = if (build_result) blk: {
break :blk self.maybeLink();
} else |err| err;
@@ -799,47 +770,47 @@ pub const Compilation = struct {
self.events.put(Event{ .Error = err });
}
- // // First, get an item from the watch channel, waiting on the channel.
- // var group = event.Group(BuildError!void).init(self.gpa());
- // {
- // const ev = (self.fs_watch.channel.get()) catch |err| {
- // build_result = err;
- // continue;
- // };
- // const root_scope = ev.data;
- // group.call(rebuildFile, self, root_scope) catch |err| {
- // build_result = err;
- // continue;
- // };
- // }
- // // Next, get all the items from the channel that are buffered up.
- // while (self.fs_watch.channel.getOrNull()) |ev_or_err| {
- // if (ev_or_err) |ev| {
- // const root_scope = ev.data;
- // group.call(rebuildFile, self, root_scope) catch |err| {
- // build_result = err;
- // continue;
- // };
- // } else |err| {
- // build_result = err;
- // continue;
- // }
- // }
- // build_result = group.wait();
+ // First, get an item from the watch channel, waiting on the channel.
+ var group = event.Group(BuildError!void).init(self.gpa());
+ {
+ const ev = (self.fs_watch.channel.get()) catch |err| {
+ build_result = err;
+ continue;
+ };
+ const root_scope = ev.data;
+ group.call(rebuildFile, self, root_scope) catch |err| {
+ build_result = err;
+ continue;
+ };
+ }
+ // Next, get all the items from the channel that are buffered up.
+ while (self.fs_watch.channel.getOrNull()) |ev_or_err| {
+ if (ev_or_err) |ev| {
+ const root_scope = ev.data;
+ group.call(rebuildFile, self, root_scope) catch |err| {
+ build_result = err;
+ continue;
+ };
+ } else |err| {
+ build_result = err;
+ continue;
+ }
+ }
+ build_result = group.wait();
}
}
- async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) !void {
+ async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) BuildError!void {
const tree_scope = blk: {
- const source_code = "";
- // const source_code = fs.readFile(
- // root_scope.realpath,
- // max_src_size,
- // ) catch |err| {
- // try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err));
- // return;
- // };
- // errdefer self.gpa().free(source_code);
+ const source_code = fs.readFile(
+ self.gpa(),
+ root_scope.realpath,
+ max_src_size,
+ ) catch |err| {
+ try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err));
+ return;
+ };
+ errdefer self.gpa().free(source_code);
const tree = try std.zig.parse(self.gpa(), source_code);
errdefer {
@@ -877,7 +848,7 @@ pub const Compilation = struct {
try decl_group.wait();
}
- async fn rebuildChangedDecls(
+ fn rebuildChangedDecls(
self: *Compilation,
group: *event.Group(BuildError!void),
locked_table: *Decl.Table,
@@ -966,7 +937,7 @@ pub const Compilation = struct {
}
}
- async fn initialCompile(self: *Compilation) !void {
+ fn initialCompile(self: *Compilation) !void {
if (self.root_src_path) |root_src_path| {
const root_scope = blk: {
// TODO async/await std.fs.realpath
@@ -985,7 +956,7 @@ pub const Compilation = struct {
}
}
- async fn maybeLink(self: *Compilation) !void {
+ fn maybeLink(self: *Compilation) !void {
(self.prelink_group.wait()) catch |err| switch (err) {
error.SemanticAnalysisFailed => {},
else => return err,
@@ -1169,11 +1140,10 @@ pub const Compilation = struct {
return link_lib;
}
- /// cancels itself so no need to await or cancel the promise.
async fn startFindingNativeLibC(self: *Compilation) void {
- std.event.Loop.instance.?.yield();
+ event.Loop.startCpuBoundOperation();
// we don't care if it fails, we're just trying to kick off the future resolution
- _ = (self.zig_compiler.getNativeLibC()) catch return;
+ _ = self.zig_compiler.getNativeLibC() catch return;
}
/// General Purpose Allocator. Must free when done.
@@ -1188,7 +1158,7 @@ pub const Compilation = struct {
/// If the temporary directory for this compilation has not been created, it creates it.
/// Then it creates a random file name in that dir and returns it.
- pub async fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer {
+ pub fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer {
const tmp_dir = try self.getTmpDir();
const file_prefix = self.getRandomFileName();
@@ -1204,14 +1174,14 @@ pub const Compilation = struct {
/// If the temporary directory for this Compilation has not been created, creates it.
/// Then returns it. The directory is unique to this Compilation and cleaned up when
/// the Compilation deinitializes.
- async fn getTmpDir(self: *Compilation) ![]const u8 {
+ fn getTmpDir(self: *Compilation) ![]const u8 {
if (self.tmp_dir.start()) |ptr| return ptr.*;
self.tmp_dir.data = self.getTmpDirImpl();
self.tmp_dir.resolve();
return self.tmp_dir.data;
}
- async fn getTmpDirImpl(self: *Compilation) ![]u8 {
+ fn getTmpDirImpl(self: *Compilation) ![]u8 {
const comp_dir_name = self.getRandomFileName();
const zig_dir_path = try getZigDir(self.gpa());
defer self.gpa().free(zig_dir_path);
@@ -1221,7 +1191,7 @@ pub const Compilation = struct {
return tmp_dir;
}
- async fn getRandomFileName(self: *Compilation) [12]u8 {
+ fn getRandomFileName(self: *Compilation) [12]u8 {
// here we replace the standard +/ with -_ so that it can be used in a file name
const b64_fs_encoder = std.base64.Base64Encoder.init(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
@@ -1247,20 +1217,23 @@ pub const Compilation = struct {
}
/// Returns a value which has been ref()'d once
- async fn analyzeConstValue(
+ fn analyzeConstValue(
comp: *Compilation,
tree_scope: *Scope.AstTree,
scope: *Scope,
node: *ast.Node,
expected_type: *Type,
) !*Value {
- const analyzed_code = try comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type);
+ var frame = try comp.gpa().create(@Frame(genAndAnalyzeCode));
+ defer comp.gpa().destroy(frame);
+ frame.* = async comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type);
+ const analyzed_code = try await frame;
defer analyzed_code.destroy(comp.gpa());
return analyzed_code.getCompTimeResult(comp);
}
- async fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type {
+ fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type {
const meta_type = &Type.MetaType.get(comp).base;
defer meta_type.base.deref(comp);
@@ -1291,7 +1264,7 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib
}
/// The function that actually does the generation.
-async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
+fn generateDecl(comp: *Compilation, decl: *Decl) !void {
switch (decl.id) {
.Var => @panic("TODO"),
.Fn => {
@@ -1302,7 +1275,7 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
}
}
-async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
+fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
const tree_scope = fn_decl.base.tree_scope;
const body_node = fn_decl.fn_proto.body_node orelse return generateDeclFnProto(comp, fn_decl);
@@ -1319,7 +1292,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
// The Decl.Fn owns the initial 1 reference count
const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
- fn_decl.value = Decl.Fn.Val{ .Fn = fn_val };
+ fn_decl.value = .{ .Fn = fn_val };
symbol_name_consumed = true;
// Define local parameter variables
@@ -1354,12 +1327,15 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
try fn_type.non_key.Normal.variable_list.append(var_scope);
}
- const analyzed_code = try comp.genAndAnalyzeCode(
+ var frame = try comp.gpa().create(@Frame(Compilation.genAndAnalyzeCode));
+ defer comp.gpa().destroy(frame);
+ frame.* = async comp.genAndAnalyzeCode(
tree_scope,
fn_val.child_scope,
body_node,
fn_type.key.data.Normal.return_type,
);
+ const analyzed_code = try await frame;
errdefer analyzed_code.destroy(comp.gpa());
assert(fn_val.block_scope != null);
@@ -1386,7 +1362,7 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 {
return std.fs.getAppDataDir(allocator, "zig");
}
-async fn analyzeFnType(
+fn analyzeFnType(
comp: *Compilation,
tree_scope: *Scope.AstTree,
scope: *Scope,
@@ -1448,7 +1424,7 @@ async fn analyzeFnType(
return fn_type;
}
-async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
+fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
const fn_type = try analyzeFnType(
comp,
fn_decl.base.tree_scope,
@@ -1463,6 +1439,6 @@ async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
// The Decl.Fn owns the initial 1 reference count
const fn_proto_val = try Value.FnProto.create(comp, fn_type, symbol_name);
- fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val };
+ fn_decl.value = .{ .FnProto = fn_proto_val };
symbol_name_consumed = true;
}
diff --git a/src-self-hosted/decl.zig b/src-self-hosted/decl.zig
@@ -69,15 +69,12 @@ pub const Decl = struct {
pub const Fn = struct {
base: Decl,
- value: Val,
- fn_proto: *ast.Node.FnProto,
-
- // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
- pub const Val = union(enum) {
+ value: union(enum) {
Unresolved,
Fn: *Value.Fn,
FnProto: *Value.FnProto,
- };
+ },
+ fn_proto: *ast.Node.FnProto,
pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
return if (self.fn_proto.extern_export_inline_token) |tok_index| x: {
diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig
@@ -110,7 +110,7 @@ pub const Inst = struct {
unreachable;
}
- pub async fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst {
+ pub fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst {
switch (base.id) {
.Return => return @fieldParentPtr(Return, "base", base).analyze(ira),
.Const => return @fieldParentPtr(Const, "base", base).analyze(ira),
@@ -422,7 +422,7 @@ pub const Inst = struct {
return false;
}
- pub async fn analyze(self: *const Ref, ira: *Analyze) !*Inst {
+ pub fn analyze(self: *const Ref, ira: *Analyze) !*Inst {
const target = try self.params.target.getAsParam();
if (ira.getCompTimeValOrNullUndefOk(target)) |val| {
@@ -472,7 +472,7 @@ pub const Inst = struct {
return false;
}
- pub async fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst {
+ pub fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst {
(ira.irb.comp.resolveDecl(self.params.decl)) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => return error.SemanticAnalysisFailed,
@@ -516,7 +516,7 @@ pub const Inst = struct {
return false;
}
- pub async fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst {
+ pub fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst {
switch (self.params.var_scope.data) {
.Const => @panic("TODO"),
.Param => |param| {
@@ -563,7 +563,7 @@ pub const Inst = struct {
return false;
}
- pub async fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst {
+ pub fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst {
const target = try self.params.target.getAsParam();
const target_type = target.getKnownType();
if (target_type.id != .Pointer) {
@@ -645,7 +645,7 @@ pub const Inst = struct {
return false;
}
- pub async fn analyze(self: *const PtrType, ira: *Analyze) !*Inst {
+ pub fn analyze(self: *const PtrType, ira: *Analyze) !*Inst {
const child_type = try self.params.child_type.getAsConstType(ira);
// if (child_type->id == TypeTableEntryIdUnreachable) {
// ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed"));
@@ -658,7 +658,7 @@ pub const Inst = struct {
const amt = try align_inst.getAsConstAlign(ira);
break :blk Type.Pointer.Align{ .Override = amt };
} else blk: {
- break :blk Type.Pointer.Align{ .Abi = {} };
+ break :blk .Abi;
};
const ptr_type = try Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
.child_type = child_type,
@@ -927,7 +927,7 @@ pub const Variable = struct {
pub const BasicBlock = struct {
ref_count: usize,
- name_hint: [*]const u8, // must be a C string literal
+ name_hint: [*:0]const u8,
debug_id: usize,
scope: *Scope,
instruction_list: std.ArrayList(*Inst),
@@ -1051,7 +1051,7 @@ pub const Builder = struct {
}
/// No need to clean up resources thanks to the arena allocator.
- pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*]const u8) !*BasicBlock {
+ pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*:0]const u8) !*BasicBlock {
const basic_block = try self.arena().create(BasicBlock);
basic_block.* = BasicBlock{
.ref_count = 0,
@@ -1078,6 +1078,14 @@ pub const Builder = struct {
self.current_basic_block = basic_block;
}
+ pub fn genNodeRecursive(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst {
+ const alloc = irb.comp.gpa();
+ var frame = try alloc.create(@Frame(genNode));
+ defer alloc.destroy(frame);
+ frame.* = async irb.genNode(node, scope, lval);
+ return await frame;
+ }
+
pub async fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst {
switch (node.id) {
.Root => unreachable,
@@ -1157,7 +1165,7 @@ pub const Builder = struct {
},
.GroupedExpression => {
const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node);
- return irb.genNode(grouped_expr.expr, scope, lval);
+ return irb.genNodeRecursive(grouped_expr.expr, scope, lval);
},
.BuiltinCall => return error.Unimplemented,
.ErrorSetDecl => return error.Unimplemented,
@@ -1186,14 +1194,14 @@ pub const Builder = struct {
}
}
- async fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst {
- const fn_ref = try irb.genNode(suffix_op.lhs, scope, .None);
+ fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst {
+ const fn_ref = try irb.genNodeRecursive(suffix_op.lhs.node, scope, .None);
const args = try irb.arena().alloc(*Inst, call.params.len);
var it = call.params.iterator(0);
var i: usize = 0;
while (it.next()) |arg_node_ptr| : (i += 1) {
- args[i] = try irb.genNode(arg_node_ptr.*, scope, .None);
+ args[i] = try irb.genNodeRecursive(arg_node_ptr.*, scope, .None);
}
//bool is_async = node->data.fn_call_expr.is_async;
@@ -1214,7 +1222,7 @@ pub const Builder = struct {
//return ir_lval_wrap(irb, scope, fn_call, lval);
}
- async fn genPtrType(
+ fn genPtrType(
irb: *Builder,
prefix_op: *ast.Node.PrefixOp,
ptr_info: ast.Node.PrefixOp.PtrInfo,
@@ -1238,7 +1246,7 @@ pub const Builder = struct {
//} else {
// align_value = nullptr;
//}
- const child_type = try irb.genNode(prefix_op.rhs, scope, .None);
+ const child_type = try irb.genNodeRecursive(prefix_op.rhs, scope, .None);
//uint32_t bit_offset_start = 0;
//if (node->data.pointer_type.bit_offset_start != nullptr) {
@@ -1307,9 +1315,9 @@ pub const Builder = struct {
var rest: []const u8 = undefined;
if (int_token.len >= 3 and int_token[0] == '0') {
base = switch (int_token[1]) {
- 'b' => u8(2),
- 'o' => u8(8),
- 'x' => u8(16),
+ 'b' => 2,
+ 'o' => 8,
+ 'x' => 16,
else => unreachable,
};
rest = int_token[2..];
@@ -1339,7 +1347,7 @@ pub const Builder = struct {
return inst;
}
- pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
+ pub fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
const str_token = irb.code.tree_scope.tree.tokenSlice(str_lit.token);
const src_span = Span.token(str_lit.token);
@@ -1389,7 +1397,7 @@ pub const Builder = struct {
}
}
- pub async fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst {
+ pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst {
const block_scope = try Scope.Block.create(irb.comp, parent_scope);
const outer_block_scope = &block_scope.base;
@@ -1437,7 +1445,7 @@ pub const Builder = struct {
child_scope = &defer_child_scope.base;
continue;
}
- const statement_value = try irb.genNode(statement_node, child_scope, .None);
+ const statement_value = try irb.genNodeRecursive(statement_node, child_scope, .None);
is_continuation_unreachable = statement_value.isNoReturn();
if (is_continuation_unreachable) {
@@ -1499,7 +1507,7 @@ pub const Builder = struct {
return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true);
}
- pub async fn genControlFlowExpr(
+ pub fn genControlFlowExpr(
irb: *Builder,
control_flow_expr: *ast.Node.ControlFlowExpression,
scope: *Scope,
@@ -1533,7 +1541,7 @@ pub const Builder = struct {
const outer_scope = irb.begin_scope.?;
const return_value = if (control_flow_expr.rhs) |rhs| blk: {
- break :blk try irb.genNode(rhs, scope, .None);
+ break :blk try irb.genNodeRecursive(rhs, scope, .None);
} else blk: {
break :blk try irb.buildConstVoid(scope, src_span, true);
};
@@ -1596,7 +1604,7 @@ pub const Builder = struct {
}
}
- pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
+ pub fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
const src_span = Span.token(identifier.token);
const name = irb.code.tree_scope.tree.tokenSlice(identifier.token);
@@ -1694,7 +1702,7 @@ pub const Builder = struct {
return result;
}
- async fn genDefersForBlock(
+ fn genDefersForBlock(
irb: *Builder,
inner_scope: *Scope,
outer_scope: *Scope,
@@ -1712,7 +1720,7 @@ pub const Builder = struct {
};
if (generate) {
const defer_expr_scope = defer_scope.defer_expr_scope;
- const instruction = try irb.genNode(
+ const instruction = try irb.genNodeRecursive(
defer_expr_scope.expr_node,
&defer_expr_scope.base,
.None,
@@ -1797,7 +1805,7 @@ pub const Builder = struct {
// Look at the params and ref() other instructions
comptime var i = 0;
inline while (i < @memberCount(I.Params)) : (i += 1) {
- const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i)));
+ const FieldType = comptime @typeOf(@field(@as(I.Params, undefined), @memberName(I.Params, i)));
switch (FieldType) {
*Inst => @field(inst.params, @memberName(I.Params, i)).ref(self),
*BasicBlock => @field(inst.params, @memberName(I.Params, i)).ref(self),
@@ -1909,7 +1917,7 @@ pub const Builder = struct {
VarScope: *Scope.Var,
};
- async fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident {
+ fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident {
var s = scope;
while (true) {
switch (s.id) {
@@ -2519,7 +2527,7 @@ const Analyze = struct {
}
};
-pub async fn gen(
+pub fn gen(
comp: *Compilation,
body_node: *ast.Node,
tree_scope: *Scope.AstTree,
@@ -2541,7 +2549,7 @@ pub async fn gen(
return irb.finish();
}
-pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
+pub fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
const old_entry_bb = old_code.basic_block_list.at(0);
var ira = try Analyze.init(comp, old_code.tree_scope, expected_type);
diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig
@@ -143,7 +143,7 @@ pub const LibCInstallation = struct {
}
/// Finds the default, native libc.
- pub async fn findNative(self: *LibCInstallation, allocator: *Allocator) !void {
+ pub fn findNative(self: *LibCInstallation, allocator: *Allocator) !void {
self.initEmpty();
var group = event.Group(FindError!void).init(allocator);
errdefer group.wait() catch {};
@@ -393,14 +393,14 @@ pub const LibCInstallation = struct {
};
/// caller owns returned memory
-async fn ccPrintFileName(allocator: *Allocator, o_file: []const u8, want_dirname: bool) ![]u8 {
+fn ccPrintFileName(allocator: *Allocator, o_file: []const u8, want_dirname: bool) ![]u8 {
const cc_exe = std.os.getenv("CC") orelse "cc";
const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", o_file);
defer allocator.free(arg1);
const argv = [_][]const u8{ cc_exe, arg1 };
// TODO This simulates evented I/O for the child process exec
- std.event.Loop.instance.?.yield();
+ event.Loop.startCpuBoundOperation();
const errorable_result = std.ChildProcess.exec(allocator, &argv, null, null, 1024 * 1024);
const exec_result = if (std.debug.runtime_safety) blk: {
break :blk errorable_result catch unreachable;
diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig
@@ -11,7 +11,7 @@ const util = @import("util.zig");
const Context = struct {
comp: *Compilation,
arena: std.heap.ArenaAllocator,
- args: std.ArrayList([*]const u8),
+ args: std.ArrayList([*:0]const u8),
link_in_crt: bool,
link_err: error{OutOfMemory}!void,
@@ -21,7 +21,7 @@ const Context = struct {
out_file_path: std.Buffer,
};
-pub async fn link(comp: *Compilation) !void {
+pub fn link(comp: *Compilation) !void {
var ctx = Context{
.comp = comp,
.arena = std.heap.ArenaAllocator.init(comp.gpa()),
@@ -33,7 +33,7 @@ pub async fn link(comp: *Compilation) !void {
.out_file_path = undefined,
};
defer ctx.arena.deinit();
- ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator);
+ ctx.args = std.ArrayList([*:0]const u8).init(&ctx.arena.allocator);
ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator);
if (comp.link_out_file) |out_file| {
@@ -58,7 +58,8 @@ pub async fn link(comp: *Compilation) !void {
try ctx.args.append("lld");
if (comp.haveLibC()) {
- ctx.libc = ctx.comp.override_libc orelse blk: {
+ // TODO https://github.com/ziglang/zig/issues/3190
+ var libc = ctx.comp.override_libc orelse blk: {
switch (comp.target) {
Target.Native => {
break :blk comp.zig_compiler.getNativeLibC() catch return error.LibCRequiredButNotProvidedOrFound;
@@ -66,6 +67,7 @@ pub async fn link(comp: *Compilation) !void {
else => return error.LibCRequiredButNotProvidedOrFound,
}
};
+ ctx.libc = libc;
}
try constructLinkerArgs(&ctx);
@@ -171,7 +173,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
//}
try ctx.args.append("-o");
- try ctx.args.append(ctx.out_file_path.ptr());
+ try ctx.args.append(ctx.out_file_path.toSliceConst());
if (ctx.link_in_crt) {
const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o";
@@ -214,10 +216,11 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
if (ctx.comp.haveLibC()) {
try ctx.args.append("-L");
- try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr);
+ // TODO addNullByte should probably return [:0]u8
+ try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr));
try ctx.args.append("-L");
- try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr);
+ try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr));
if (!ctx.comp.is_static) {
const dl = blk: {
@@ -226,7 +229,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
return error.LibCMissingDynamicLinker;
};
try ctx.args.append("-dynamic-linker");
- try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr);
+ try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr));
}
}
@@ -238,7 +241,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
// .o files
for (ctx.comp.link_objects) |link_object| {
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
- try ctx.args.append(link_obj_with_null.ptr);
+ try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));
}
try addFnObjects(ctx);
@@ -313,7 +316,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
const full_path = try std.fs.path.join(&ctx.arena.allocator, [_][]const u8{ dirname, basename });
const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);
- try ctx.args.append(full_path_with_null.ptr);
+ try ctx.args.append(@ptrCast([*:0]const u8, full_path_with_null.ptr));
}
fn constructLinkerArgsCoff(ctx: *Context) !void {
@@ -339,12 +342,12 @@ fn constructLinkerArgsCoff(ctx: *Context) !void {
const is_library = ctx.comp.kind == .Lib;
const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", ctx.out_file_path.toSliceConst());
- try ctx.args.append(out_arg.ptr);
+ try ctx.args.append(@ptrCast([*:0]const u8, out_arg.ptr));
if (ctx.comp.haveLibC()) {
- try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr);
- try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr);
- try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr);
+ try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr));
+ try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr));
+ try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr));
}
if (ctx.link_in_crt) {
@@ -353,17 +356,17 @@ fn constructLinkerArgsCoff(ctx: *Context) !void {
if (ctx.comp.is_static) {
const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", d_str);
- try ctx.args.append(cmt_lib_name.ptr);
+ try ctx.args.append(@ptrCast([*:0]const u8, cmt_lib_name.ptr));
} else {
const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", d_str);
- try ctx.args.append(msvcrt_lib_name.ptr);
+ try ctx.args.append(@ptrCast([*:0]const u8, msvcrt_lib_name.ptr));
}
const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", lib_str, d_str);
- try ctx.args.append(vcruntime_lib_name.ptr);
+ try ctx.args.append(@ptrCast([*:0]const u8, vcruntime_lib_name.ptr));
const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", lib_str, d_str);
- try ctx.args.append(crt_lib_name.ptr);
+ try ctx.args.append(@ptrCast([*:0]const u8, crt_lib_name.ptr));
// Visual C++ 2015 Conformance Changes
// https://msdn.microsoft.com/en-us/library/bb531344.aspx
@@ -395,7 +398,7 @@ fn constructLinkerArgsCoff(ctx: *Context) !void {
for (ctx.comp.link_objects) |link_object| {
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
- try ctx.args.append(link_obj_with_null.ptr);
+ try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));
}
try addFnObjects(ctx);
@@ -504,11 +507,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
//}
try ctx.args.append("-arch");
- const darwin_arch_str = try std.cstr.addNullByte(
- &ctx.arena.allocator,
- ctx.comp.target.getDarwinArchString(),
- );
- try ctx.args.append(darwin_arch_str.ptr);
+ try ctx.args.append(util.getDarwinArchString(ctx.comp.target));
const platform = try DarwinPlatform.get(ctx.comp);
switch (platform.kind) {
@@ -517,7 +516,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
.IPhoneOSSimulator => try ctx.args.append("-ios_simulator_version_min"),
}
const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", platform.major, platform.minor, platform.micro);
- try ctx.args.append(ver_str.ptr);
+ try ctx.args.append(@ptrCast([*:0]const u8, ver_str.ptr));
if (ctx.comp.kind == .Exe) {
if (ctx.comp.is_static) {
@@ -528,7 +527,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
}
try ctx.args.append("-o");
- try ctx.args.append(ctx.out_file_path.ptr());
+ try ctx.args.append(ctx.out_file_path.toSliceConst());
//for (size_t i = 0; i < g->rpath_list.length; i += 1) {
// Buf *rpath = g->rpath_list.at(i);
@@ -572,7 +571,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
for (ctx.comp.link_objects) |link_object| {
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
- try ctx.args.append(link_obj_with_null.ptr);
+ try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));
}
try addFnObjects(ctx);
@@ -593,10 +592,10 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
} else {
if (mem.indexOfScalar(u8, lib.name, '/') == null) {
const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", lib.name);
- try ctx.args.append(arg.ptr);
+ try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
} else {
const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name);
- try ctx.args.append(arg.ptr);
+ try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
}
}
}
@@ -626,20 +625,19 @@ fn constructLinkerArgsWasm(ctx: *Context) void {
}
fn addFnObjects(ctx: *Context) !void {
- // at this point it's guaranteed nobody else has this lock, so we circumvent it
- // and avoid having to be an async function
- const fn_link_set = &ctx.comp.fn_link_set.private_data;
+ const held = ctx.comp.fn_link_set.acquire();
+ defer held.release();
- var it = fn_link_set.first;
+ var it = held.value.first;
while (it) |node| {
const fn_val = node.data orelse {
// handle the tombstone. See Value.Fn.destroy.
it = node.next;
- fn_link_set.remove(node);
+ held.value.remove(node);
ctx.comp.gpa().destroy(node);
continue;
};
- try ctx.args.append(fn_val.containing_object.ptr());
+ try ctx.args.append(fn_val.containing_object.toSliceConst());
it = node.next;
}
}
diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig
@@ -86,7 +86,7 @@ pub const AddGlobal = LLVMAddGlobal;
extern fn LLVMAddGlobal(M: *Module, Ty: *Type, Name: [*:0]const u8) ?*Value;
pub const ConstStringInContext = LLVMConstStringInContext;
-extern fn LLVMConstStringInContext(C: *Context, Str: [*:0]const u8, Length: c_uint, DontNullTerminate: Bool) ?*Value;
+extern fn LLVMConstStringInContext(C: *Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) ?*Value;
pub const ConstInt = LLVMConstInt;
extern fn LLVMConstInt(IntTy: *Type, N: c_ulonglong, SignExtend: Bool) ?*Value;
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
@@ -49,14 +49,15 @@ const usage =
const Command = struct {
name: []const u8,
- exec: fn (*Allocator, []const []const u8) anyerror!void,
+ exec: async fn (*Allocator, []const []const u8) anyerror!void,
};
pub fn main() !void {
// This allocator needs to be thread-safe because we use it for the event.Loop
// which multiplexes async functions onto kernel threads.
// libc allocator is guaranteed to have this property.
- const allocator = std.heap.c_allocator;
+ // TODO https://github.com/ziglang/zig/issues/3783
+ const allocator = std.heap.page_allocator;
stdout = &std.io.getStdOut().outStream().stream;
@@ -118,14 +119,18 @@ pub fn main() !void {
},
};
- for (commands) |command| {
+ inline for (commands) |command| {
if (mem.eql(u8, command.name, args[1])) {
- return command.exec(allocator, args[2..]);
+ var frame = try allocator.create(@Frame(command.exec));
+ defer allocator.destroy(frame);
+ frame.* = async command.exec(allocator, args[2..]);
+ return await frame;
}
}
try stderr.print("unknown command: {}\n\n", args[1]);
try stderr.write(usage);
+ process.argsFree(allocator, args);
process.exit(1);
}
@@ -461,13 +466,12 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
comp.link_objects = link_objects;
comp.start();
- const frame = async processBuildEvents(comp, color);
+ processBuildEvents(comp, color);
}
-async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
+fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
var count: usize = 0;
- while (true) {
- // TODO directly awaiting async should guarantee memory allocation elision
+ while (!comp.cancelled) {
const build_event = comp.events.get();
count += 1;
@@ -545,7 +549,7 @@ fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_fil
"Try running `zig libc` to see an example for the native target.\n",
libc_paths_file,
@errorName(err),
- ) catch process.exit(1);
+ ) catch {};
process.exit(1);
};
}
@@ -567,12 +571,8 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
var zig_compiler = try ZigCompiler.init(allocator);
defer zig_compiler.deinit();
- const frame = async findLibCAsync(&zig_compiler);
-}
-
-async fn findLibCAsync(zig_compiler: *ZigCompiler) void {
const libc = zig_compiler.getNativeLibC() catch |err| {
- stderr.print("unable to find libc: {}\n", @errorName(err)) catch process.exit(1);
+ stderr.print("unable to find libc: {}\n", @errorName(err)) catch {};
process.exit(1);
};
libc.render(stdout) catch process.exit(1);
@@ -644,11 +644,23 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
process.exit(1);
}
- return asyncFmtMain(
- allocator,
- &flags,
- color,
- );
+ var fmt = Fmt{
+ .allocator = allocator,
+ .seen = event.Locked(Fmt.SeenMap).init(Fmt.SeenMap.init(allocator)),
+ .any_error = false,
+ .color = color,
+ };
+
+ const check_mode = flags.present("check");
+
+ var group = event.Group(FmtError!void).init(allocator);
+ for (flags.positionals.toSliceConst()) |file_path| {
+ try group.call(fmtPath, &fmt, file_path, check_mode);
+ }
+ try group.wait();
+ if (fmt.any_error) {
+ process.exit(1);
+ }
}
const FmtError = error{
@@ -673,30 +685,6 @@ const FmtError = error{
CurrentWorkingDirectoryUnlinked,
} || fs.File.OpenError;
-async fn asyncFmtMain(
- allocator: *Allocator,
- flags: *const Args,
- color: errmsg.Color,
-) FmtError!void {
- var fmt = Fmt{
- .allocator = allocator,
- .seen = event.Locked(Fmt.SeenMap).init(Fmt.SeenMap.init(allocator)),
- .any_error = false,
- .color = color,
- };
-
- const check_mode = flags.present("check");
-
- var group = event.Group(FmtError!void).init(allocator);
- for (flags.positionals.toSliceConst()) |file_path| {
- try group.call(fmtPath, &fmt, file_path, check_mode);
- }
- try group.wait();
- if (fmt.any_error) {
- process.exit(1);
- }
-}
-
async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void {
const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref);
defer fmt.allocator.free(file_path);
@@ -708,33 +696,34 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
if (try held.value.put(file_path, {})) |_| return;
}
- const source_code = "";
- // const source_code = event.fs.readFile(
- // file_path,
- // max_src_size,
- // ) catch |err| switch (err) {
- // error.IsDir, error.AccessDenied => {
- // // TODO make event based (and dir.next())
- // var dir = try fs.Dir.cwd().openDirList(file_path);
- // defer dir.close();
-
- // var group = event.Group(FmtError!void).init(fmt.allocator);
- // while (try dir.next()) |entry| {
- // if (entry.kind == fs.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
- // const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name });
- // try group.call(fmtPath, fmt, full_path, check_mode);
- // }
- // }
- // return group.wait();
- // },
- // else => {
- // // TODO lock stderr printing
- // try stderr.print("unable to open '{}': {}\n", file_path, err);
- // fmt.any_error = true;
- // return;
- // },
- // };
- // defer fmt.allocator.free(source_code);
+ const source_code = event.fs.readFile(
+ fmt.allocator,
+ file_path,
+ max_src_size,
+ ) catch |err| switch (err) {
+ error.IsDir, error.AccessDenied => {
+ var dir = try fs.cwd().openDirList(file_path);
+ defer dir.close();
+
+ var group = event.Group(FmtError!void).init(fmt.allocator);
+ var it = dir.iterate();
+ while (try it.next()) |entry| {
+ if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
+ const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name });
+ @panic("TODO https://github.com/ziglang/zig/issues/3777");
+ // try group.call(fmtPath, fmt, full_path, check_mode);
+ }
+ }
+ return group.wait();
+ },
+ else => {
+ // TODO lock stderr printing
+ try stderr.print("unable to open '{}': {}\n", file_path, err);
+ fmt.any_error = true;
+ return;
+ },
+ };
+ defer fmt.allocator.free(source_code);
const tree = std.zig.parse(fmt.allocator, source_code) catch |err| {
try stderr.print("error parsing file '{}': {}\n", file_path, err);
@@ -867,10 +856,12 @@ fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void {
.exec = cmdInternalBuildInfo,
}};
- for (sub_commands) |sub_command| {
+ inline for (sub_commands) |sub_command| {
if (mem.eql(u8, sub_command.name, args[0])) {
- try sub_command.exec(allocator, args[1..]);
- return;
+ var frame = try allocator.create(@Frame(sub_command.exec));
+ defer allocator.destroy(frame);
+ frame.* = async sub_command.exec(allocator, args[1..]);
+ return await frame;
}
}
diff --git a/src-self-hosted/stage1.zig b/src-self-hosted/stage1.zig
@@ -279,7 +279,7 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void
const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) {
error.IsDir, error.AccessDenied => {
// TODO make event based (and dir.next())
- var dir = try fs.Dir.cwd().openDirList(file_path);
+ var dir = try fs.cwd().openDirList(file_path);
defer dir.close();
var dir_it = dir.iterate();
@@ -427,11 +427,11 @@ export fn stage2_DepTokenizer_next(self: *stage2_DepTokenizer) stage2_DepNextRes
};
}
-export const stage2_DepTokenizer = extern struct {
+const stage2_DepTokenizer = extern struct {
handle: *DepTokenizer,
};
-export const stage2_DepNextResult = extern struct {
+const stage2_DepNextResult = extern struct {
type_id: TypeId,
// when type_id == error --> error text
@@ -440,7 +440,7 @@ export const stage2_DepNextResult = extern struct {
// when type_id == prereq --> prereq pathname
textz: [*]const u8,
- export const TypeId = extern enum {
+ const TypeId = extern enum {
error_,
null_,
target,
diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig
@@ -26,7 +26,8 @@ test "stage2" {
}
const file1 = "1.zig";
-const allocator = std.heap.c_allocator;
+// TODO https://github.com/ziglang/zig/issues/3783
+const allocator = std.heap.page_allocator;
pub const TestContext = struct {
zig_compiler: ZigCompiler,
@@ -94,8 +95,8 @@ pub const TestContext = struct {
&self.zig_compiler,
"test",
file1_path,
- Target.Native,
- Compilation.Kind.Obj,
+ .Native,
+ .Obj,
.Debug,
true, // is_static
self.zig_lib_dir,
@@ -116,7 +117,7 @@ pub const TestContext = struct {
const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 });
- const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, (Target{.Native = {}}).exeFileExt());
+ const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, (Target{ .Native = {} }).exeFileExt());
if (std.fs.path.dirname(file1_path)) |dirname| {
try std.fs.makePath(allocator, dirname);
}
@@ -128,8 +129,8 @@ pub const TestContext = struct {
&self.zig_compiler,
"test",
file1_path,
- Target.Native,
- Compilation.Kind.Exe,
+ .Native,
+ .Exe,
.Debug,
false,
self.zig_lib_dir,
@@ -148,15 +149,12 @@ pub const TestContext = struct {
exe_file: []const u8,
expected_output: []const u8,
) anyerror!void {
- // TODO this should not be necessary
- const exe_file_2 = try std.mem.dupe(allocator, u8, exe_file);
-
defer comp.destroy();
const build_event = comp.events.get();
switch (build_event) {
.Ok => {
- const argv = [_][]const u8{exe_file_2};
+ const argv = [_][]const u8{exe_file};
// TODO use event loop
const child = try std.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024);
switch (child.term) {
@@ -173,13 +171,13 @@ pub const TestContext = struct {
return error.OutputMismatch;
}
},
- Compilation.Event.Error => |err| return err,
- Compilation.Event.Fail => |msgs| {
+ .Error => @panic("Cannot return error: https://github.com/ziglang/zig/issues/3190"), // |err| return err,
+ .Fail => |msgs| {
const stderr = std.io.getStdErr();
try stderr.write("build incorrectly failed:\n");
for (msgs) |msg| {
defer msg.destroy();
- try msg.printToFile(stderr, errmsg.Color.Auto);
+ try msg.printToFile(stderr, .Auto);
}
},
}
diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig
@@ -53,7 +53,7 @@ pub const Type = struct {
base: *Type,
allocator: *Allocator,
llvm_context: *llvm.Context,
- ) (error{OutOfMemory}!*llvm.Type) {
+ ) error{OutOfMemory}!*llvm.Type {
switch (base.id) {
.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context),
.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context),
@@ -184,7 +184,7 @@ pub const Type = struct {
/// If you happen to have an llvm context handy, use getAbiAlignmentInContext instead.
/// Otherwise, this one will grab one from the pool and then release it.
- pub async fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 {
+ pub fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 {
if (base.abi_alignment.start()) |ptr| return ptr.*;
{
@@ -200,7 +200,7 @@ pub const Type = struct {
}
/// If you have an llvm conext handy, you can use it here.
- pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
+ pub fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
if (base.abi_alignment.start()) |ptr| return ptr.*;
base.abi_alignment.data = base.resolveAbiAlignment(comp, llvm_context);
@@ -209,7 +209,7 @@ pub const Type = struct {
}
/// Lower level function that does the work. See getAbiAlignment.
- async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
+ fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context);
return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type));
}
@@ -367,7 +367,7 @@ pub const Type = struct {
}
/// takes ownership of key.Normal.params on success
- pub async fn get(comp: *Compilation, key: Key) !*Fn {
+ pub fn get(comp: *Compilation, key: Key) !*Fn {
{
const held = comp.fn_type_table.acquire();
defer held.release();
@@ -564,7 +564,7 @@ pub const Type = struct {
return comp.u8_type;
}
- pub async fn get(comp: *Compilation, key: Key) !*Int {
+ pub fn get(comp: *Compilation, key: Key) !*Int {
{
const held = comp.int_type_table.acquire();
defer held.release();
@@ -606,7 +606,7 @@ pub const Type = struct {
comp.registerGarbage(Int, &self.garbage_node);
}
- pub async fn gcDestroy(self: *Int, comp: *Compilation) void {
+ pub fn gcDestroy(self: *Int, comp: *Compilation) void {
{
const held = comp.int_type_table.acquire();
defer held.release();
@@ -700,7 +700,7 @@ pub const Type = struct {
comp.registerGarbage(Pointer, &self.garbage_node);
}
- pub async fn gcDestroy(self: *Pointer, comp: *Compilation) void {
+ pub fn gcDestroy(self: *Pointer, comp: *Compilation) void {
{
const held = comp.ptr_type_table.acquire();
defer held.release();
@@ -711,14 +711,14 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub async fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 {
+ pub fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 {
switch (self.key.alignment) {
.Abi => return self.key.child_type.getAbiAlignment(comp),
.Override => |alignment| return alignment,
}
}
- pub async fn get(
+ pub fn get(
comp: *Compilation,
key: Key,
) !*Pointer {
@@ -726,8 +726,10 @@ pub const Type = struct {
switch (key.alignment) {
.Abi => {},
.Override => |alignment| {
+ // TODO https://github.com/ziglang/zig/issues/3190
+ var align_spill = alignment;
const abi_align = try key.child_type.getAbiAlignment(comp);
- if (abi_align == alignment) {
+ if (abi_align == align_spill) {
normal_key.alignment = .Abi;
}
},
@@ -828,7 +830,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
- pub async fn get(comp: *Compilation, key: Key) !*Array {
+ pub fn get(comp: *Compilation, key: Key) !*Array {
key.elem_type.base.ref();
errdefer key.elem_type.base.deref(comp);
diff --git a/src-self-hosted/util.zig b/src-self-hosted/util.zig
@@ -32,21 +32,21 @@ pub fn getFloatAbi(self: Target) FloatAbi {
};
}
-pub fn getObjectFormat(self: Target) Target.ObjectFormat {
- return switch (self) {
- .Native => @import("builtin").object_format,
- .Cross => {
+pub fn getObjectFormat(target: Target) Target.ObjectFormat {
+ switch (target) {
+ .Native => return @import("builtin").object_format,
+ .Cross => blk: {
if (target.isWindows() or target.isUefi()) {
- break .coff;
+ return .coff;
} else if (target.isDarwin()) {
- break .macho;
+ return .macho;
}
if (target.isWasm()) {
- break .wasm;
+ return .wasm;
}
- break .elf;
+ return .elf;
},
- };
+ }
}
pub fn getDynamicLinkerPath(self: Target) ?[]const u8 {
@@ -156,7 +156,7 @@ pub fn getDynamicLinkerPath(self: Target) ?[]const u8 {
}
}
-pub fn getDarwinArchString(self: Target) []const u8 {
+pub fn getDarwinArchString(self: Target) [:0]const u8 {
const arch = self.getArch();
switch (arch) {
.aarch64 => return "arm64",
@@ -166,7 +166,8 @@ pub fn getDarwinArchString(self: Target) []const u8 {
.powerpc => return "ppc",
.powerpc64 => return "ppc64",
.powerpc64le => return "ppc64le",
- else => return @tagName(arch),
+ // @tagName should be able to return sentinel terminated slice
+ else => @panic("TODO https://github.com/ziglang/zig/issues/3779"), //return @tagName(arch),
}
}
diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig
@@ -156,7 +156,7 @@ pub const Value = struct {
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
- self.symbol_name.ptr(),
+ self.symbol_name.toSliceConst(),
llvm_fn_type,
) orelse return error.OutOfMemory;
@@ -241,7 +241,7 @@ pub const Value = struct {
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
- self.symbol_name.ptr(),
+ self.symbol_name.toSliceConst(),
llvm_fn_type,
) orelse return error.OutOfMemory;
@@ -334,7 +334,7 @@ pub const Value = struct {
field_index: usize,
};
- pub async fn createArrayElemPtr(
+ pub fn createArrayElemPtr(
comp: *Compilation,
array_val: *Array,
mut: Type.Pointer.Mut,
@@ -350,7 +350,7 @@ pub const Value = struct {
.mut = mut,
.vol = Type.Pointer.Vol.Non,
.size = size,
- .alignment = Type.Pointer.Align.Abi,
+ .alignment = .Abi,
});
var ptr_type_consumed = false;
errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp);
@@ -390,13 +390,13 @@ pub const Value = struct {
const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?;
const ptr_bit_count = ofile.comp.target_ptr_bits;
const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory;
- const indices = [_]*llvm.Value{
+ var indices = [_]*llvm.Value{
llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory,
llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory,
};
return llvm.ConstInBoundsGEP(
array_llvm_value,
- &indices,
+ @ptrCast([*]*llvm.Value, &indices),
@intCast(c_uint, indices.len),
) orelse return error.OutOfMemory;
},
@@ -423,7 +423,7 @@ pub const Value = struct {
};
/// Takes ownership of buffer
- pub async fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {
+ pub fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {
const u8_type = Type.Int.get_u8(comp);
defer u8_type.base.base.deref(comp);
diff --git a/src/all_types.hpp b/src/all_types.hpp
@@ -1565,7 +1565,7 @@ struct ZigFn {
// in the case of async functions this is the implicit return type according to the
// zig source code, not according to zig ir
ZigType *src_implicit_return_type;
- IrExecutable ir_executable;
+ IrExecutable *ir_executable;
IrExecutable analyzed_executable;
size_t prealloc_bbc;
size_t prealloc_backward_branch_quota;
@@ -2204,6 +2204,8 @@ struct ZigVar {
bool src_is_const;
bool gen_is_const;
bool is_thread_local;
+ bool is_comptime_memoized;
+ bool is_comptime_memoized_value;
};
struct ErrorTableEntry {
diff --git a/src/analyze.cpp b/src/analyze.cpp
@@ -3275,14 +3275,15 @@ static void get_fully_qualified_decl_name(CodeGen *g, Buf *buf, Tld *tld, bool i
}
ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value) {
- ZigFn *fn_entry = allocate<ZigFn>(1);
+ ZigFn *fn_entry = allocate<ZigFn>(1, "ZigFn");
+ fn_entry->ir_executable = allocate<IrExecutable>(1, "IrExecutablePass1");
fn_entry->prealloc_backward_branch_quota = default_backward_branch_quota;
fn_entry->analyzed_executable.backward_branch_count = &fn_entry->prealloc_bbc;
fn_entry->analyzed_executable.backward_branch_quota = &fn_entry->prealloc_backward_branch_quota;
fn_entry->analyzed_executable.fn_entry = fn_entry;
- fn_entry->ir_executable.fn_entry = fn_entry;
+ fn_entry->ir_executable->fn_entry = fn_entry;
fn_entry->fn_inline = inline_value;
return fn_entry;
@@ -3792,6 +3793,16 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf
return variable_entry;
}
+static void validate_export_var_type(CodeGen *g, ZigType* type, AstNode *source_node) {
+ switch (type->id) {
+ case ZigTypeIdMetaType:
+ add_node_error(g, source_node, buf_sprintf("cannot export variable of type 'type'"));
+ break;
+ default:
+ break;
+ }
+}
+
static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {
AstNode *source_node = tld_var->base.source_node;
AstNodeVariableDeclaration *var_decl = &source_node->data.variable_declaration;
@@ -3881,6 +3892,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {
}
if (is_export) {
+ validate_export_var_type(g, type, source_node);
add_var_export(g, tld_var->var, tld_var->var->name, GlobalLinkageIdStrong);
}
@@ -4599,7 +4611,7 @@ static void analyze_fn_ir(CodeGen *g, ZigFn *fn, AstNode *return_type_node) {
assert(!fn_type->data.fn.is_generic);
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
- ZigType *block_return_type = ir_analyze(g, &fn->ir_executable,
+ ZigType *block_return_type = ir_analyze(g, fn->ir_executable,
&fn->analyzed_executable, fn_type_id->return_type, return_type_node);
fn->src_implicit_return_type = block_return_type;
@@ -4695,7 +4707,7 @@ static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) {
assert(!fn_type->data.fn.is_generic);
ir_gen_fn(g, fn_table_entry);
- if (fn_table_entry->ir_executable.first_err_trace_msg != nullptr) {
+ if (fn_table_entry->ir_executable->first_err_trace_msg != nullptr) {
fn_table_entry->anal_state = FnAnalStateInvalid;
return;
}
@@ -4703,7 +4715,7 @@ static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) {
fprintf(stderr, "\n");
ast_render(stderr, fn_table_entry->body_node, 4);
fprintf(stderr, "\nfn %s() { // (IR)\n", buf_ptr(&fn_table_entry->symbol_name));
- ir_print(g, stderr, &fn_table_entry->ir_executable, 4, IrPassSrc);
+ ir_print(g, stderr, fn_table_entry->ir_executable, 4, IrPassSrc);
fprintf(stderr, "}\n");
}
@@ -6442,20 +6454,31 @@ Error type_resolve(CodeGen *g, ZigType *ty, ResolveStatus status) {
}
bool ir_get_var_is_comptime(ZigVar *var) {
+ if (var->is_comptime_memoized)
+ return var->is_comptime_memoized_value;
+
+ var->is_comptime_memoized = true;
+
// The is_comptime field can be left null, which means not comptime.
- if (var->is_comptime == nullptr)
- return false;
+ if (var->is_comptime == nullptr) {
+ var->is_comptime_memoized_value = false;
+ return var->is_comptime_memoized_value;
+ }
// When the is_comptime field references an instruction that has to get analyzed, this
// is the value.
if (var->is_comptime->child != nullptr) {
assert(var->is_comptime->child->value->type->id == ZigTypeIdBool);
- return var->is_comptime->child->value->data.x_bool;
+ var->is_comptime_memoized_value = var->is_comptime->child->value->data.x_bool;
+ var->is_comptime = nullptr;
+ return var->is_comptime_memoized_value;
}
// As an optimization, is_comptime values which are constant are allowed
// to be omitted from analysis. In this case, there is no child instruction
// and we simply look at the unanalyzed const parent instruction.
assert(var->is_comptime->value->type->id == ZigTypeIdBool);
- return var->is_comptime->value->data.x_bool;
+ var->is_comptime_memoized_value = var->is_comptime->value->data.x_bool;
+ var->is_comptime = nullptr;
+ return var->is_comptime_memoized_value;
}
bool const_values_equal_ptr(ZigValue *a, ZigValue *b) {
diff --git a/src/codegen.cpp b/src/codegen.cpp
@@ -9563,7 +9563,11 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e
case ZigTypeIdVoid:
case ZigTypeIdUnreachable:
case ZigTypeIdBool:
+ g->c_want_stdbool = true;
+ return;
case ZigTypeIdInt:
+ g->c_want_stdint = true;
+ return;
case ZigTypeIdFloat:
return;
case ZigTypeIdOpaque:
@@ -9644,7 +9648,6 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu
break;
case ZigTypeIdBool:
buf_init_from_str(out_buf, "bool");
- g->c_want_stdbool = true;
break;
case ZigTypeIdUnreachable:
buf_init_from_str(out_buf, "__attribute__((__noreturn__)) void");
@@ -9668,7 +9671,6 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu
}
break;
case ZigTypeIdInt:
- g->c_want_stdint = true;
buf_resize(out_buf, 0);
buf_appendf(out_buf, "%sint%" PRIu32 "_t",
type_entry->data.integral.is_signed ? "" : "u",
@@ -9780,113 +9782,7 @@ static Buf *preprocessor_mangle(Buf *src) {
return result;
}
-static void gen_h_file(CodeGen *g) {
- GenH gen_h_data = {0};
- GenH *gen_h = &gen_h_data;
-
- assert(!g->is_test_build);
- assert(!g->disable_gen_h);
-
- Buf *out_h_path = buf_sprintf("%s" OS_SEP "%s.h", buf_ptr(g->output_dir), buf_ptr(g->root_out_name));
-
- FILE *out_h = fopen(buf_ptr(out_h_path), "wb");
- if (!out_h)
- zig_panic("unable to open %s: %s\n", buf_ptr(out_h_path), strerror(errno));
-
- Buf *export_macro = nullptr;
- if (g->is_dynamic) {
- export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name)));
- buf_upcase(export_macro);
- }
-
- Buf *extern_c_macro = preprocessor_mangle(buf_sprintf("%s_EXTERN_C", buf_ptr(g->root_out_name)));
- buf_upcase(extern_c_macro);
-
- Buf h_buf = BUF_INIT;
- buf_resize(&h_buf, 0);
- for (size_t fn_def_i = 0; fn_def_i < g->fn_defs.length; fn_def_i += 1) {
- ZigFn *fn_table_entry = g->fn_defs.at(fn_def_i);
-
- if (fn_table_entry->export_list.length == 0)
- continue;
-
- FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id;
-
- Buf return_type_c = BUF_INIT;
- get_c_type(g, gen_h, fn_type_id->return_type, &return_type_c);
-
- Buf *symbol_name;
- if (fn_table_entry->export_list.length == 0) {
- symbol_name = &fn_table_entry->symbol_name;
- } else {
- GlobalExport *fn_export = &fn_table_entry->export_list.items[0];
- symbol_name = &fn_export->name;
- }
-
- buf_appendf(&h_buf, "%s %s %s(",
- buf_ptr(g->is_dynamic ? export_macro : extern_c_macro),
- buf_ptr(&return_type_c),
- buf_ptr(symbol_name));
-
- Buf param_type_c = BUF_INIT;
- if (fn_type_id->param_count > 0) {
- for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
- FnTypeParamInfo *param_info = &fn_type_id->param_info[param_i];
- AstNode *param_decl_node = get_param_decl_node(fn_table_entry, param_i);
- Buf *param_name = param_decl_node->data.param_decl.name;
-
- const char *comma_str = (param_i == 0) ? "" : ", ";
- const char *restrict_str = param_info->is_noalias ? "restrict" : "";
- get_c_type(g, gen_h, param_info->type, ¶m_type_c);
-
- if (param_info->type->id == ZigTypeIdArray) {
- // Arrays decay to pointers
- buf_appendf(&h_buf, "%s%s%s %s[]", comma_str, buf_ptr(¶m_type_c),
- restrict_str, buf_ptr(param_name));
- } else {
- buf_appendf(&h_buf, "%s%s%s %s", comma_str, buf_ptr(¶m_type_c),
- restrict_str, buf_ptr(param_name));
- }
- }
- buf_appendf(&h_buf, ")");
- } else {
- buf_appendf(&h_buf, "void)");
- }
-
- buf_appendf(&h_buf, ";\n");
-
- }
-
- Buf *ifdef_dance_name = preprocessor_mangle(buf_sprintf("%s_H", buf_ptr(g->root_out_name)));
- buf_upcase(ifdef_dance_name);
-
- fprintf(out_h, "#ifndef %s\n", buf_ptr(ifdef_dance_name));
- fprintf(out_h, "#define %s\n\n", buf_ptr(ifdef_dance_name));
-
- if (g->c_want_stdbool)
- fprintf(out_h, "#include <stdbool.h>\n");
- if (g->c_want_stdint)
- fprintf(out_h, "#include <stdint.h>\n");
-
- fprintf(out_h, "\n");
-
- fprintf(out_h, "#ifdef __cplusplus\n");
- fprintf(out_h, "#define %s extern \"C\"\n", buf_ptr(extern_c_macro));
- fprintf(out_h, "#else\n");
- fprintf(out_h, "#define %s\n", buf_ptr(extern_c_macro));
- fprintf(out_h, "#endif\n");
- fprintf(out_h, "\n");
-
- if (g->is_dynamic) {
- fprintf(out_h, "#if defined(_WIN32)\n");
- fprintf(out_h, "#define %s %s __declspec(dllimport)\n", buf_ptr(export_macro), buf_ptr(extern_c_macro));
- fprintf(out_h, "#else\n");
- fprintf(out_h, "#define %s %s __attribute__((visibility (\"default\")))\n",
- buf_ptr(export_macro), buf_ptr(extern_c_macro));
- fprintf(out_h, "#endif\n");
- fprintf(out_h, "\n");
- }
-
+static void gen_h_file_types(CodeGen* g, GenH* gen_h, Buf* out_buf) {
for (size_t type_i = 0; type_i < gen_h->types_to_declare.length; type_i += 1) {
ZigType *type_entry = gen_h->types_to_declare.at(type_i);
switch (type_entry->id) {
@@ -9917,25 +9813,25 @@ static void gen_h_file(CodeGen *g) {
case ZigTypeIdEnum:
if (type_entry->data.enumeration.layout == ContainerLayoutExtern) {
- fprintf(out_h, "enum %s {\n", buf_ptr(type_h_name(type_entry)));
+ buf_appendf(out_buf, "enum %s {\n", buf_ptr(type_h_name(type_entry)));
for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) {
TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i];
Buf *value_buf = buf_alloc();
bigint_append_buf(value_buf, &enum_field->value, 10);
- fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf));
+ buf_appendf(out_buf, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf));
if (field_i != type_entry->data.enumeration.src_field_count - 1) {
- fprintf(out_h, ",");
+ buf_appendf(out_buf, ",");
}
- fprintf(out_h, "\n");
+ buf_appendf(out_buf, "\n");
}
- fprintf(out_h, "};\n\n");
+ buf_appendf(out_buf, "};\n\n");
} else {
- fprintf(out_h, "enum %s;\n", buf_ptr(type_h_name(type_entry)));
+ buf_appendf(out_buf, "enum %s;\n\n", buf_ptr(type_h_name(type_entry)));
}
break;
case ZigTypeIdStruct:
if (type_entry->data.structure.layout == ContainerLayoutExtern) {
- fprintf(out_h, "struct %s {\n", buf_ptr(type_h_name(type_entry)));
+ buf_appendf(out_buf, "struct %s {\n", buf_ptr(type_h_name(type_entry)));
for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) {
TypeStructField *struct_field = type_entry->data.structure.fields[field_i];
@@ -9943,43 +9839,194 @@ static void gen_h_file(CodeGen *g) {
get_c_type(g, gen_h, struct_field->type_entry, type_name_buf);
if (struct_field->type_entry->id == ZigTypeIdArray) {
- fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf),
+ buf_appendf(out_buf, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf),
buf_ptr(struct_field->name),
struct_field->type_entry->data.array.len);
} else {
- fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name));
+ buf_appendf(out_buf, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name));
}
}
- fprintf(out_h, "};\n\n");
+ buf_appendf(out_buf, "};\n\n");
} else {
- fprintf(out_h, "struct %s;\n", buf_ptr(type_h_name(type_entry)));
+ buf_appendf(out_buf, "struct %s;\n\n", buf_ptr(type_h_name(type_entry)));
}
break;
case ZigTypeIdUnion:
if (type_entry->data.unionation.layout == ContainerLayoutExtern) {
- fprintf(out_h, "union %s {\n", buf_ptr(type_h_name(type_entry)));
+ buf_appendf(out_buf, "union %s {\n", buf_ptr(type_h_name(type_entry)));
for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) {
TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i];
Buf *type_name_buf = buf_alloc();
get_c_type(g, gen_h, union_field->type_entry, type_name_buf);
- fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name));
+ buf_appendf(out_buf, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name));
}
- fprintf(out_h, "};\n\n");
+ buf_appendf(out_buf, "};\n\n");
} else {
- fprintf(out_h, "union %s;\n", buf_ptr(type_h_name(type_entry)));
+ buf_appendf(out_buf, "union %s;\n\n", buf_ptr(type_h_name(type_entry)));
}
break;
case ZigTypeIdOpaque:
- fprintf(out_h, "struct %s;\n\n", buf_ptr(type_h_name(type_entry)));
+ buf_appendf(out_buf, "struct %s;\n\n", buf_ptr(type_h_name(type_entry)));
break;
}
}
+}
+
+static void gen_h_file_functions(CodeGen* g, GenH* gen_h, Buf* out_buf, Buf* export_macro) {
+ for (size_t fn_def_i = 0; fn_def_i < g->fn_defs.length; fn_def_i += 1) {
+ ZigFn *fn_table_entry = g->fn_defs.at(fn_def_i);
+
+ if (fn_table_entry->export_list.length == 0)
+ continue;
+
+ FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id;
+
+ Buf return_type_c = BUF_INIT;
+ get_c_type(g, gen_h, fn_type_id->return_type, &return_type_c);
+
+ Buf *symbol_name;
+ if (fn_table_entry->export_list.length == 0) {
+ symbol_name = &fn_table_entry->symbol_name;
+ } else {
+ GlobalExport *fn_export = &fn_table_entry->export_list.items[0];
+ symbol_name = &fn_export->name;
+ }
+
+ if (export_macro != nullptr) {
+ buf_appendf(out_buf, "%s %s %s(",
+ buf_ptr(export_macro),
+ buf_ptr(&return_type_c),
+ buf_ptr(symbol_name));
+ } else {
+ buf_appendf(out_buf, "%s %s(",
+ buf_ptr(&return_type_c),
+ buf_ptr(symbol_name));
+ }
+
+ Buf param_type_c = BUF_INIT;
+ if (fn_type_id->param_count > 0) {
+ for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
+ FnTypeParamInfo *param_info = &fn_type_id->param_info[param_i];
+ AstNode *param_decl_node = get_param_decl_node(fn_table_entry, param_i);
+ Buf *param_name = param_decl_node->data.param_decl.name;
+
+ const char *comma_str = (param_i == 0) ? "" : ", ";
+ const char *restrict_str = param_info->is_noalias ? "restrict" : "";
+ get_c_type(g, gen_h, param_info->type, ¶m_type_c);
+
+ if (param_info->type->id == ZigTypeIdArray) {
+ // Arrays decay to pointers
+ buf_appendf(out_buf, "%s%s%s %s[]", comma_str, buf_ptr(¶m_type_c),
+ restrict_str, buf_ptr(param_name));
+ } else {
+ buf_appendf(out_buf, "%s%s%s %s", comma_str, buf_ptr(¶m_type_c),
+ restrict_str, buf_ptr(param_name));
+ }
+ }
+ buf_appendf(out_buf, ")");
+ } else {
+ buf_appendf(out_buf, "void)");
+ }
+
+ buf_appendf(out_buf, ";\n");
+ }
+}
+
+static void gen_h_file_variables(CodeGen* g, GenH* gen_h, Buf* h_buf, Buf* export_macro) {
+ for (size_t exp_var_i = 0; exp_var_i < g->global_vars.length; exp_var_i += 1) {
+ ZigVar* var = g->global_vars.at(exp_var_i)->var;
+ if (var->export_list.length == 0)
+ continue;
+
+ Buf var_type_c = BUF_INIT;
+ get_c_type(g, gen_h, var->var_type, &var_type_c);
+
+ if (export_macro != nullptr) {
+ buf_appendf(h_buf, "extern %s %s %s;\n",
+ buf_ptr(export_macro),
+ buf_ptr(&var_type_c),
+ var->name);
+ } else {
+ buf_appendf(h_buf, "extern %s %s;\n",
+ buf_ptr(&var_type_c),
+ var->name);
+ }
+ }
+}
+
+static void gen_h_file(CodeGen *g) {
+ GenH gen_h_data = {0};
+ GenH *gen_h = &gen_h_data;
+
+ assert(!g->is_test_build);
+ assert(!g->disable_gen_h);
+
+ Buf *out_h_path = buf_sprintf("%s" OS_SEP "%s.h", buf_ptr(g->output_dir), buf_ptr(g->root_out_name));
+
+ FILE *out_h = fopen(buf_ptr(out_h_path), "wb");
+ if (!out_h)
+ zig_panic("unable to open %s: %s\n", buf_ptr(out_h_path), strerror(errno));
+
+ Buf *export_macro = nullptr;
+ if (g->is_dynamic) {
+ export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name)));
+ buf_upcase(export_macro);
+ }
+
+ Buf fns_buf = BUF_INIT;
+ buf_resize(&fns_buf, 0);
+ gen_h_file_functions(g, gen_h, &fns_buf, export_macro);
+
+ Buf vars_buf = BUF_INIT;
+ buf_resize(&vars_buf, 0);
+ gen_h_file_variables(g, gen_h, &vars_buf, export_macro);
+
+ // Types will be populated by exported functions and variables so it has to run last.
+ Buf types_buf = BUF_INIT;
+ buf_resize(&types_buf, 0);
+ gen_h_file_types(g, gen_h, &types_buf);
+
+ Buf *ifdef_dance_name = preprocessor_mangle(buf_sprintf("%s_H", buf_ptr(g->root_out_name)));
+ buf_upcase(ifdef_dance_name);
+
+ fprintf(out_h, "#ifndef %s\n", buf_ptr(ifdef_dance_name));
+ fprintf(out_h, "#define %s\n\n", buf_ptr(ifdef_dance_name));
+
+ if (g->c_want_stdbool)
+ fprintf(out_h, "#include <stdbool.h>\n");
+ if (g->c_want_stdint)
+ fprintf(out_h, "#include <stdint.h>\n");
+
+ fprintf(out_h, "\n");
+
+ if (g->is_dynamic) {
+ fprintf(out_h, "#if defined(_WIN32)\n");
+ fprintf(out_h, "#define %s __declspec(dllimport)\n", buf_ptr(export_macro));
+ fprintf(out_h, "#else\n");
+ fprintf(out_h, "#define %s __attribute__((visibility (\"default\")))\n",
+ buf_ptr(export_macro));
+ fprintf(out_h, "#endif\n");
+ fprintf(out_h, "\n");
+ }
+
+ fprintf(out_h, "%s", buf_ptr(&types_buf));
+
+ fprintf(out_h, "#ifdef __cplusplus\n");
+ fprintf(out_h, "extern \"C\" {\n");
+ fprintf(out_h, "#endif\n");
+ fprintf(out_h, "\n");
+
+ fprintf(out_h, "%s\n", buf_ptr(&fns_buf));
+
+ fprintf(out_h, "#ifdef __cplusplus\n");
+ fprintf(out_h, "} // extern \"C\"\n");
+ fprintf(out_h, "#endif\n\n");
- fprintf(out_h, "%s", buf_ptr(&h_buf));
+ fprintf(out_h, "%s\n", buf_ptr(&vars_buf));
- fprintf(out_h, "\n#endif\n");
+ fprintf(out_h, "#endif // %s\n", buf_ptr(ifdef_dance_name));
if (fclose(out_h))
zig_panic("unable to close h file: %s", strerror(errno));
diff --git a/src/ir.cpp b/src/ir.cpp
@@ -41,6 +41,7 @@ struct IrAnalyze {
ZigList<IrInstruction *> src_implicit_return_type_list;
ZigList<IrSuspendPosition> resume_stack;
IrBasicBlock *const_predecessor_bb;
+ size_t ref_count;
// For the purpose of using in a debugger
void dump();
@@ -74,6 +75,7 @@ enum ConstCastResultId {
ConstCastResultIdPtrLens,
ConstCastResultIdCV,
ConstCastResultIdPtrSentinel,
+ ConstCastResultIdIntShorten,
};
struct ConstCastOnly;
@@ -100,6 +102,7 @@ struct ConstCastBadAllowsZero;
struct ConstCastBadNullTermArrays;
struct ConstCastBadCV;
struct ConstCastPtrSentinel;
+struct ConstCastIntShorten;
struct ConstCastOnly {
ConstCastResultId id;
@@ -120,6 +123,7 @@ struct ConstCastOnly {
ConstCastBadNullTermArrays *sentinel_arrays;
ConstCastBadCV *bad_cv;
ConstCastPtrSentinel *bad_ptr_sentinel;
+ ConstCastIntShorten *int_shorten;
} data;
};
@@ -189,6 +193,11 @@ struct ConstCastPtrSentinel {
ZigType *actual_type;
};
+struct ConstCastIntShorten {
+ ZigType *wanted_type;
+ ZigType *actual_type;
+};
+
static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope);
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval,
ResultLoc *result_loc);
@@ -248,6 +257,381 @@ static IrInstruction *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_n
IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type);
static ResultLoc *no_result_loc(void);
+static void destroy_instruction(IrInstruction *inst) {
+#ifdef ZIG_ENABLE_MEM_PROFILE
+ const char *name = ir_instruction_type_str(inst->id);
+#else
+ const char *name = nullptr;
+#endif
+ switch (inst->id) {
+ case IrInstructionIdInvalid:
+ zig_unreachable();
+ case IrInstructionIdReturn:
+ return destroy(reinterpret_cast<IrInstructionReturn *>(inst), name);
+ case IrInstructionIdConst:
+ return destroy(reinterpret_cast<IrInstructionConst *>(inst), name);
+ case IrInstructionIdBinOp:
+ return destroy(reinterpret_cast<IrInstructionBinOp *>(inst), name);
+ case IrInstructionIdMergeErrSets:
+ return destroy(reinterpret_cast<IrInstructionMergeErrSets *>(inst), name);
+ case IrInstructionIdDeclVarSrc:
+ return destroy(reinterpret_cast<IrInstructionDeclVarSrc *>(inst), name);
+ case IrInstructionIdCast:
+ return destroy(reinterpret_cast<IrInstructionCast *>(inst), name);
+ case IrInstructionIdCallSrc:
+ return destroy(reinterpret_cast<IrInstructionCallSrc *>(inst), name);
+ case IrInstructionIdCallGen:
+ return destroy(reinterpret_cast<IrInstructionCallGen *>(inst), name);
+ case IrInstructionIdUnOp:
+ return destroy(reinterpret_cast<IrInstructionUnOp *>(inst), name);
+ case IrInstructionIdCondBr:
+ return destroy(reinterpret_cast<IrInstructionCondBr *>(inst), name);
+ case IrInstructionIdBr:
+ return destroy(reinterpret_cast<IrInstructionBr *>(inst), name);
+ case IrInstructionIdPhi:
+ return destroy(reinterpret_cast<IrInstructionPhi *>(inst), name);
+ case IrInstructionIdContainerInitList:
+ return destroy(reinterpret_cast<IrInstructionContainerInitList *>(inst), name);
+ case IrInstructionIdContainerInitFields:
+ return destroy(reinterpret_cast<IrInstructionContainerInitFields *>(inst), name);
+ case IrInstructionIdUnreachable:
+ return destroy(reinterpret_cast<IrInstructionUnreachable *>(inst), name);
+ case IrInstructionIdElemPtr:
+ return destroy(reinterpret_cast<IrInstructionElemPtr *>(inst), name);
+ case IrInstructionIdVarPtr:
+ return destroy(reinterpret_cast<IrInstructionVarPtr *>(inst), name);
+ case IrInstructionIdReturnPtr:
+ return destroy(reinterpret_cast<IrInstructionReturnPtr *>(inst), name);
+ case IrInstructionIdLoadPtr:
+ return destroy(reinterpret_cast<IrInstructionLoadPtr *>(inst), name);
+ case IrInstructionIdLoadPtrGen:
+ return destroy(reinterpret_cast<IrInstructionLoadPtrGen *>(inst), name);
+ case IrInstructionIdStorePtr:
+ return destroy(reinterpret_cast<IrInstructionStorePtr *>(inst), name);
+ case IrInstructionIdVectorStoreElem:
+ return destroy(reinterpret_cast<IrInstructionVectorStoreElem *>(inst), name);
+ case IrInstructionIdTypeOf:
+ return destroy(reinterpret_cast<IrInstructionTypeOf *>(inst), name);
+ case IrInstructionIdFieldPtr:
+ return destroy(reinterpret_cast<IrInstructionFieldPtr *>(inst), name);
+ case IrInstructionIdStructFieldPtr:
+ return destroy(reinterpret_cast<IrInstructionStructFieldPtr *>(inst), name);
+ case IrInstructionIdUnionFieldPtr:
+ return destroy(reinterpret_cast<IrInstructionUnionFieldPtr *>(inst), name);
+ case IrInstructionIdSetCold:
+ return destroy(reinterpret_cast<IrInstructionSetCold *>(inst), name);
+ case IrInstructionIdSetRuntimeSafety:
+ return destroy(reinterpret_cast<IrInstructionSetRuntimeSafety *>(inst), name);
+ case IrInstructionIdSetFloatMode:
+ return destroy(reinterpret_cast<IrInstructionSetFloatMode *>(inst), name);
+ case IrInstructionIdArrayType:
+ return destroy(reinterpret_cast<IrInstructionArrayType *>(inst), name);
+ case IrInstructionIdSliceType:
+ return destroy(reinterpret_cast<IrInstructionSliceType *>(inst), name);
+ case IrInstructionIdAnyFrameType:
+ return destroy(reinterpret_cast<IrInstructionAnyFrameType *>(inst), name);
+ case IrInstructionIdGlobalAsm:
+ return destroy(reinterpret_cast<IrInstructionGlobalAsm *>(inst), name);
+ case IrInstructionIdAsm:
+ return destroy(reinterpret_cast<IrInstructionAsm *>(inst), name);
+ case IrInstructionIdSizeOf:
+ return destroy(reinterpret_cast<IrInstructionSizeOf *>(inst), name);
+ case IrInstructionIdTestNonNull:
+ return destroy(reinterpret_cast<IrInstructionTestNonNull *>(inst), name);
+ case IrInstructionIdOptionalUnwrapPtr:
+ return destroy(reinterpret_cast<IrInstructionOptionalUnwrapPtr *>(inst), name);
+ case IrInstructionIdPopCount:
+ return destroy(reinterpret_cast<IrInstructionPopCount *>(inst), name);
+ case IrInstructionIdClz:
+ return destroy(reinterpret_cast<IrInstructionClz *>(inst), name);
+ case IrInstructionIdCtz:
+ return destroy(reinterpret_cast<IrInstructionCtz *>(inst), name);
+ case IrInstructionIdBswap:
+ return destroy(reinterpret_cast<IrInstructionBswap *>(inst), name);
+ case IrInstructionIdBitReverse:
+ return destroy(reinterpret_cast<IrInstructionBitReverse *>(inst), name);
+ case IrInstructionIdSwitchBr:
+ return destroy(reinterpret_cast<IrInstructionSwitchBr *>(inst), name);
+ case IrInstructionIdSwitchVar:
+ return destroy(reinterpret_cast<IrInstructionSwitchVar *>(inst), name);
+ case IrInstructionIdSwitchElseVar:
+ return destroy(reinterpret_cast<IrInstructionSwitchElseVar *>(inst), name);
+ case IrInstructionIdSwitchTarget:
+ return destroy(reinterpret_cast<IrInstructionSwitchTarget *>(inst), name);
+ case IrInstructionIdUnionTag:
+ return destroy(reinterpret_cast<IrInstructionUnionTag *>(inst), name);
+ case IrInstructionIdImport:
+ return destroy(reinterpret_cast<IrInstructionImport *>(inst), name);
+ case IrInstructionIdRef:
+ return destroy(reinterpret_cast<IrInstructionRef *>(inst), name);
+ case IrInstructionIdRefGen:
+ return destroy(reinterpret_cast<IrInstructionRefGen *>(inst), name);
+ case IrInstructionIdCompileErr:
+ return destroy(reinterpret_cast<IrInstructionCompileErr *>(inst), name);
+ case IrInstructionIdCompileLog:
+ return destroy(reinterpret_cast<IrInstructionCompileLog *>(inst), name);
+ case IrInstructionIdErrName:
+ return destroy(reinterpret_cast<IrInstructionErrName *>(inst), name);
+ case IrInstructionIdCImport:
+ return destroy(reinterpret_cast<IrInstructionCImport *>(inst), name);
+ case IrInstructionIdCInclude:
+ return destroy(reinterpret_cast<IrInstructionCInclude *>(inst), name);
+ case IrInstructionIdCDefine:
+ return destroy(reinterpret_cast<IrInstructionCDefine *>(inst), name);
+ case IrInstructionIdCUndef:
+ return destroy(reinterpret_cast<IrInstructionCUndef *>(inst), name);
+ case IrInstructionIdEmbedFile:
+ return destroy(reinterpret_cast<IrInstructionEmbedFile *>(inst), name);
+ case IrInstructionIdCmpxchgSrc:
+ return destroy(reinterpret_cast<IrInstructionCmpxchgSrc *>(inst), name);
+ case IrInstructionIdCmpxchgGen:
+ return destroy(reinterpret_cast<IrInstructionCmpxchgGen *>(inst), name);
+ case IrInstructionIdFence:
+ return destroy(reinterpret_cast<IrInstructionFence *>(inst), name);
+ case IrInstructionIdTruncate:
+ return destroy(reinterpret_cast<IrInstructionTruncate *>(inst), name);
+ case IrInstructionIdIntCast:
+ return destroy(reinterpret_cast<IrInstructionIntCast *>(inst), name);
+ case IrInstructionIdFloatCast:
+ return destroy(reinterpret_cast<IrInstructionFloatCast *>(inst), name);
+ case IrInstructionIdErrSetCast:
+ return destroy(reinterpret_cast<IrInstructionErrSetCast *>(inst), name);
+ case IrInstructionIdFromBytes:
+ return destroy(reinterpret_cast<IrInstructionFromBytes *>(inst), name);
+ case IrInstructionIdToBytes:
+ return destroy(reinterpret_cast<IrInstructionToBytes *>(inst), name);
+ case IrInstructionIdIntToFloat:
+ return destroy(reinterpret_cast<IrInstructionIntToFloat *>(inst), name);
+ case IrInstructionIdFloatToInt:
+ return destroy(reinterpret_cast<IrInstructionFloatToInt *>(inst), name);
+ case IrInstructionIdBoolToInt:
+ return destroy(reinterpret_cast<IrInstructionBoolToInt *>(inst), name);
+ case IrInstructionIdIntType:
+ return destroy(reinterpret_cast<IrInstructionIntType *>(inst), name);
+ case IrInstructionIdVectorType:
+ return destroy(reinterpret_cast<IrInstructionVectorType *>(inst), name);
+ case IrInstructionIdShuffleVector:
+ return destroy(reinterpret_cast<IrInstructionShuffleVector *>(inst), name);
+ case IrInstructionIdSplatSrc:
+ return destroy(reinterpret_cast<IrInstructionSplatSrc *>(inst), name);
+ case IrInstructionIdSplatGen:
+ return destroy(reinterpret_cast<IrInstructionSplatGen *>(inst), name);
+ case IrInstructionIdBoolNot:
+ return destroy(reinterpret_cast<IrInstructionBoolNot *>(inst), name);
+ case IrInstructionIdMemset:
+ return destroy(reinterpret_cast<IrInstructionMemset *>(inst), name);
+ case IrInstructionIdMemcpy:
+ return destroy(reinterpret_cast<IrInstructionMemcpy *>(inst), name);
+ case IrInstructionIdSliceSrc:
+ return destroy(reinterpret_cast<IrInstructionSliceSrc *>(inst), name);
+ case IrInstructionIdSliceGen:
+ return destroy(reinterpret_cast<IrInstructionSliceGen *>(inst), name);
+ case IrInstructionIdMemberCount:
+ return destroy(reinterpret_cast<IrInstructionMemberCount *>(inst), name);
+ case IrInstructionIdMemberType:
+ return destroy(reinterpret_cast<IrInstructionMemberType *>(inst), name);
+ case IrInstructionIdMemberName:
+ return destroy(reinterpret_cast<IrInstructionMemberName *>(inst), name);
+ case IrInstructionIdBreakpoint:
+ return destroy(reinterpret_cast<IrInstructionBreakpoint *>(inst), name);
+ case IrInstructionIdReturnAddress:
+ return destroy(reinterpret_cast<IrInstructionReturnAddress *>(inst), name);
+ case IrInstructionIdFrameAddress:
+ return destroy(reinterpret_cast<IrInstructionFrameAddress *>(inst), name);
+ case IrInstructionIdFrameHandle:
+ return destroy(reinterpret_cast<IrInstructionFrameHandle *>(inst), name);
+ case IrInstructionIdFrameType:
+ return destroy(reinterpret_cast<IrInstructionFrameType *>(inst), name);
+ case IrInstructionIdFrameSizeSrc:
+ return destroy(reinterpret_cast<IrInstructionFrameSizeSrc *>(inst), name);
+ case IrInstructionIdFrameSizeGen:
+ return destroy(reinterpret_cast<IrInstructionFrameSizeGen *>(inst), name);
+ case IrInstructionIdAlignOf:
+ return destroy(reinterpret_cast<IrInstructionAlignOf *>(inst), name);
+ case IrInstructionIdOverflowOp:
+ return destroy(reinterpret_cast<IrInstructionOverflowOp *>(inst), name);
+ case IrInstructionIdTestErrSrc:
+ return destroy(reinterpret_cast<IrInstructionTestErrSrc *>(inst), name);
+ case IrInstructionIdTestErrGen:
+ return destroy(reinterpret_cast<IrInstructionTestErrGen *>(inst), name);
+ case IrInstructionIdUnwrapErrCode:
+ return destroy(reinterpret_cast<IrInstructionUnwrapErrCode *>(inst), name);
+ case IrInstructionIdUnwrapErrPayload:
+ return destroy(reinterpret_cast<IrInstructionUnwrapErrPayload *>(inst), name);
+ case IrInstructionIdOptionalWrap:
+ return destroy(reinterpret_cast<IrInstructionOptionalWrap *>(inst), name);
+ case IrInstructionIdErrWrapCode:
+ return destroy(reinterpret_cast<IrInstructionErrWrapCode *>(inst), name);
+ case IrInstructionIdErrWrapPayload:
+ return destroy(reinterpret_cast<IrInstructionErrWrapPayload *>(inst), name);
+ case IrInstructionIdFnProto:
+ return destroy(reinterpret_cast<IrInstructionFnProto *>(inst), name);
+ case IrInstructionIdTestComptime:
+ return destroy(reinterpret_cast<IrInstructionTestComptime *>(inst), name);
+ case IrInstructionIdPtrCastSrc:
+ return destroy(reinterpret_cast<IrInstructionPtrCastSrc *>(inst), name);
+ case IrInstructionIdPtrCastGen:
+ return destroy(reinterpret_cast<IrInstructionPtrCastGen *>(inst), name);
+ case IrInstructionIdBitCastSrc:
+ return destroy(reinterpret_cast<IrInstructionBitCastSrc *>(inst), name);
+ case IrInstructionIdBitCastGen:
+ return destroy(reinterpret_cast<IrInstructionBitCastGen *>(inst), name);
+ case IrInstructionIdWidenOrShorten:
+ return destroy(reinterpret_cast<IrInstructionWidenOrShorten *>(inst), name);
+ case IrInstructionIdPtrToInt:
+ return destroy(reinterpret_cast<IrInstructionPtrToInt *>(inst), name);
+ case IrInstructionIdIntToPtr:
+ return destroy(reinterpret_cast<IrInstructionIntToPtr *>(inst), name);
+ case IrInstructionIdIntToEnum:
+ return destroy(reinterpret_cast<IrInstructionIntToEnum *>(inst), name);
+ case IrInstructionIdIntToErr:
+ return destroy(reinterpret_cast<IrInstructionIntToErr *>(inst), name);
+ case IrInstructionIdErrToInt:
+ return destroy(reinterpret_cast<IrInstructionErrToInt *>(inst), name);
+ case IrInstructionIdCheckSwitchProngs:
+ return destroy(reinterpret_cast<IrInstructionCheckSwitchProngs *>(inst), name);
+ case IrInstructionIdCheckStatementIsVoid:
+ return destroy(reinterpret_cast<IrInstructionCheckStatementIsVoid *>(inst), name);
+ case IrInstructionIdTypeName:
+ return destroy(reinterpret_cast<IrInstructionTypeName *>(inst), name);
+ case IrInstructionIdTagName:
+ return destroy(reinterpret_cast<IrInstructionTagName *>(inst), name);
+ case IrInstructionIdPtrType:
+ return destroy(reinterpret_cast<IrInstructionPtrType *>(inst), name);
+ case IrInstructionIdDeclRef:
+ return destroy(reinterpret_cast<IrInstructionDeclRef *>(inst), name);
+ case IrInstructionIdPanic:
+ return destroy(reinterpret_cast<IrInstructionPanic *>(inst), name);
+ case IrInstructionIdFieldParentPtr:
+ return destroy(reinterpret_cast<IrInstructionFieldParentPtr *>(inst), name);
+ case IrInstructionIdByteOffsetOf:
+ return destroy(reinterpret_cast<IrInstructionByteOffsetOf *>(inst), name);
+ case IrInstructionIdBitOffsetOf:
+ return destroy(reinterpret_cast<IrInstructionBitOffsetOf *>(inst), name);
+ case IrInstructionIdTypeInfo:
+ return destroy(reinterpret_cast<IrInstructionTypeInfo *>(inst), name);
+ case IrInstructionIdType:
+ return destroy(reinterpret_cast<IrInstructionType *>(inst), name);
+ case IrInstructionIdHasField:
+ return destroy(reinterpret_cast<IrInstructionHasField *>(inst), name);
+ case IrInstructionIdTypeId:
+ return destroy(reinterpret_cast<IrInstructionTypeId *>(inst), name);
+ case IrInstructionIdSetEvalBranchQuota:
+ return destroy(reinterpret_cast<IrInstructionSetEvalBranchQuota *>(inst), name);
+ case IrInstructionIdAlignCast:
+ return destroy(reinterpret_cast<IrInstructionAlignCast *>(inst), name);
+ case IrInstructionIdImplicitCast:
+ return destroy(reinterpret_cast<IrInstructionImplicitCast *>(inst), name);
+ case IrInstructionIdResolveResult:
+ return destroy(reinterpret_cast<IrInstructionResolveResult *>(inst), name);
+ case IrInstructionIdResetResult:
+ return destroy(reinterpret_cast<IrInstructionResetResult *>(inst), name);
+ case IrInstructionIdOpaqueType:
+ return destroy(reinterpret_cast<IrInstructionOpaqueType *>(inst), name);
+ case IrInstructionIdSetAlignStack:
+ return destroy(reinterpret_cast<IrInstructionSetAlignStack *>(inst), name);
+ case IrInstructionIdArgType:
+ return destroy(reinterpret_cast<IrInstructionArgType *>(inst), name);
+ case IrInstructionIdTagType:
+ return destroy(reinterpret_cast<IrInstructionTagType *>(inst), name);
+ case IrInstructionIdExport:
+ return destroy(reinterpret_cast<IrInstructionExport *>(inst), name);
+ case IrInstructionIdErrorReturnTrace:
+ return destroy(reinterpret_cast<IrInstructionErrorReturnTrace *>(inst), name);
+ case IrInstructionIdErrorUnion:
+ return destroy(reinterpret_cast<IrInstructionErrorUnion *>(inst), name);
+ case IrInstructionIdAtomicRmw:
+ return destroy(reinterpret_cast<IrInstructionAtomicRmw *>(inst), name);
+ case IrInstructionIdSaveErrRetAddr:
+ return destroy(reinterpret_cast<IrInstructionSaveErrRetAddr *>(inst), name);
+ case IrInstructionIdAddImplicitReturnType:
+ return destroy(reinterpret_cast<IrInstructionAddImplicitReturnType *>(inst), name);
+ case IrInstructionIdFloatOp:
+ return destroy(reinterpret_cast<IrInstructionFloatOp *>(inst), name);
+ case IrInstructionIdMulAdd:
+ return destroy(reinterpret_cast<IrInstructionMulAdd *>(inst), name);
+ case IrInstructionIdAtomicLoad:
+ return destroy(reinterpret_cast<IrInstructionAtomicLoad *>(inst), name);
+ case IrInstructionIdAtomicStore:
+ return destroy(reinterpret_cast<IrInstructionAtomicStore *>(inst), name);
+ case IrInstructionIdEnumToInt:
+ return destroy(reinterpret_cast<IrInstructionEnumToInt *>(inst), name);
+ case IrInstructionIdCheckRuntimeScope:
+ return destroy(reinterpret_cast<IrInstructionCheckRuntimeScope *>(inst), name);
+ case IrInstructionIdDeclVarGen:
+ return destroy(reinterpret_cast<IrInstructionDeclVarGen *>(inst), name);
+ case IrInstructionIdArrayToVector:
+ return destroy(reinterpret_cast<IrInstructionArrayToVector *>(inst), name);
+ case IrInstructionIdVectorToArray:
+ return destroy(reinterpret_cast<IrInstructionVectorToArray *>(inst), name);
+ case IrInstructionIdPtrOfArrayToSlice:
+ return destroy(reinterpret_cast<IrInstructionPtrOfArrayToSlice *>(inst), name);
+ case IrInstructionIdAssertZero:
+ return destroy(reinterpret_cast<IrInstructionAssertZero *>(inst), name);
+ case IrInstructionIdAssertNonNull:
+ return destroy(reinterpret_cast<IrInstructionAssertNonNull *>(inst), name);
+ case IrInstructionIdResizeSlice:
+ return destroy(reinterpret_cast<IrInstructionResizeSlice *>(inst), name);
+ case IrInstructionIdHasDecl:
+ return destroy(reinterpret_cast<IrInstructionHasDecl *>(inst), name);
+ case IrInstructionIdUndeclaredIdent:
+ return destroy(reinterpret_cast<IrInstructionUndeclaredIdent *>(inst), name);
+ case IrInstructionIdAllocaSrc:
+ return destroy(reinterpret_cast<IrInstructionAllocaSrc *>(inst), name);
+ case IrInstructionIdAllocaGen:
+ return destroy(reinterpret_cast<IrInstructionAllocaGen *>(inst), name);
+ case IrInstructionIdEndExpr:
+ return destroy(reinterpret_cast<IrInstructionEndExpr *>(inst), name);
+ case IrInstructionIdUnionInitNamedField:
+ return destroy(reinterpret_cast<IrInstructionUnionInitNamedField *>(inst), name);
+ case IrInstructionIdSuspendBegin:
+ return destroy(reinterpret_cast<IrInstructionSuspendBegin *>(inst), name);
+ case IrInstructionIdSuspendFinish:
+ return destroy(reinterpret_cast<IrInstructionSuspendFinish *>(inst), name);
+ case IrInstructionIdResume:
+ return destroy(reinterpret_cast<IrInstructionResume *>(inst), name);
+ case IrInstructionIdAwaitSrc:
+ return destroy(reinterpret_cast<IrInstructionAwaitSrc *>(inst), name);
+ case IrInstructionIdAwaitGen:
+ return destroy(reinterpret_cast<IrInstructionAwaitGen *>(inst), name);
+ case IrInstructionIdSpillBegin:
+ return destroy(reinterpret_cast<IrInstructionSpillBegin *>(inst), name);
+ case IrInstructionIdSpillEnd:
+ return destroy(reinterpret_cast<IrInstructionSpillEnd *>(inst), name);
+ case IrInstructionIdVectorExtractElem:
+ return destroy(reinterpret_cast<IrInstructionVectorExtractElem *>(inst), name);
+ }
+ zig_unreachable();
+}
+
+static void ira_ref(IrAnalyze *ira) {
+ ira->ref_count += 1;
+}
+static void ira_deref(IrAnalyze *ira) {
+ if (ira->ref_count > 1) {
+ ira->ref_count -= 1;
+ return;
+ }
+ assert(ira->ref_count != 0);
+
+ for (size_t bb_i = 0; bb_i < ira->old_irb.exec->basic_block_list.length; bb_i += 1) {
+ IrBasicBlock *pass1_bb = ira->old_irb.exec->basic_block_list.items[bb_i];
+ for (size_t inst_i = 0; inst_i < pass1_bb->instruction_list.length; inst_i += 1) {
+ IrInstruction *pass1_inst = pass1_bb->instruction_list.items[inst_i];
+ destroy_instruction(pass1_inst);
+ }
+ destroy(pass1_bb, "IrBasicBlock");
+ }
+ ira->old_irb.exec->basic_block_list.deinit();
+ ira->old_irb.exec->tld_list.deinit();
+ // cannot destroy here because of var->owner_exec
+ //destroy(ira->old_irb.exec, "IrExecutablePass1");
+ ira->src_implicit_return_type_list.deinit();
+ ira->resume_stack.deinit();
+ ira->exec_context.mem_slot_list.deinit();
+ destroy(ira, "IrAnalyze");
+}
+
static ZigValue *const_ptr_pointee_unchecked(CodeGen *g, ZigValue *const_val) {
assert(get_src_ptr_type(const_val->type) != nullptr);
assert(const_val->special == ConstValSpecialStatic);
@@ -4186,7 +4570,7 @@ static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *nod
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
incoming_values[0] = val1;
incoming_values[1] = val2;
- IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
+ IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
incoming_blocks[0] = post_val1_block;
incoming_blocks[1] = post_val2_block;
@@ -4277,7 +4661,7 @@ static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
incoming_values[0] = null_result;
incoming_values[1] = unwrapped_payload;
- IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
+ IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
incoming_blocks[0] = after_null_block;
incoming_blocks[1] = after_ok_block;
IrInstruction *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
@@ -6044,7 +6428,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
incoming_values[0] = then_expr_result;
incoming_values[1] = else_expr_result;
- IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
+ IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
incoming_blocks[0] = after_then_block;
incoming_blocks[1] = after_else_block;
@@ -7398,7 +7782,7 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
incoming_values[0] = then_expr_result;
incoming_values[1] = else_expr_result;
- IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
+ IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
incoming_blocks[0] = after_then_block;
incoming_blocks[1] = after_else_block;
@@ -7495,7 +7879,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
incoming_values[0] = then_expr_result;
incoming_values[1] = else_expr_result;
- IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
+ IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
incoming_blocks[0] = after_then_block;
incoming_blocks[1] = after_else_block;
@@ -8092,7 +8476,7 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
incoming_values[0] = err_result;
incoming_values[1] = unwrapped_payload;
- IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
+ IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
incoming_blocks[0] = after_err_block;
incoming_blocks[1] = after_ok_block;
IrInstruction *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
@@ -8680,7 +9064,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
bool ir_gen_fn(CodeGen *codegen, ZigFn *fn_entry) {
assert(fn_entry);
- IrExecutable *ir_executable = &fn_entry->ir_executable;
+ IrExecutable *ir_executable = fn_entry->ir_executable;
AstNode *body_node = fn_entry->body_node;
assert(fn_entry->child_scope);
@@ -10224,6 +10608,14 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
return result;
}
+ if (wanted_type->id == ZigTypeIdInt && actual_type->id == ZigTypeIdInt) {
+ result.id = ConstCastResultIdIntShorten;
+ result.data.int_shorten = allocate_nonzero<ConstCastIntShorten>(1);
+ result.data.int_shorten->wanted_type = wanted_type;
+ result.data.int_shorten->actual_type = actual_type;
+ return result;
+ }
+
result.id = ConstCastResultIdType;
result.data.type_mismatch = allocate_nonzero<ConstCastTypeMismatch>(1);
result.data.type_mismatch->wanted_type = wanted_type;
@@ -11490,7 +11882,7 @@ ZigValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
if (expected_type != nullptr && type_is_invalid(expected_type))
return codegen->invalid_instruction->value;
- IrExecutable *ir_executable = allocate<IrExecutable>(1);
+ IrExecutable *ir_executable = allocate<IrExecutable>(1, "IrExecutablePass1");
ir_executable->source_node = source_node;
ir_executable->parent_exec = parent_exec;
ir_executable->name = exec_name;
@@ -11512,7 +11904,7 @@ ZigValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
ir_print(codegen, stderr, ir_executable, 2, IrPassSrc);
fprintf(stderr, "}\n");
}
- IrExecutable *analyzed_executable = allocate<IrExecutable>(1);
+ IrExecutable *analyzed_executable = allocate<IrExecutable>(1, "IrExecutablePass2");
analyzed_executable->source_node = source_node;
analyzed_executable->parent_exec = parent_exec;
analyzed_executable->source_exec = ir_executable;
@@ -12641,6 +13033,17 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
add_error_note(ira->codegen, parent_msg, source_node,
buf_sprintf("calling convention mismatch"));
break;
+ case ConstCastResultIdIntShorten: {
+ ZigType *wanted_type = cast_result->data.int_shorten->wanted_type;
+ ZigType *actual_type = cast_result->data.int_shorten->actual_type;
+ const char *wanted_signed = wanted_type->data.integral.is_signed ? "signed" : "unsigned";
+ const char *actual_signed = actual_type->data.integral.is_signed ? "signed" : "unsigned";
+ add_error_note(ira->codegen, parent_msg, source_node,
+ buf_sprintf("%s %" PRIu32 "-bit int cannot represent all possible %s %" PRIu32 "-bit values",
+ wanted_signed, wanted_type->data.integral.bit_count,
+ actual_signed, actual_type->data.integral.bit_count));
+ break;
+ }
case ConstCastResultIdFnAlign: // TODO
case ConstCastResultIdFnVarArgs: // TODO
case ConstCastResultIdFnReturnType: // TODO
@@ -15597,6 +16000,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira,
assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length);
ZigValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index);
copy_const_val(mem_slot, init_val, !is_comptime_var || var->gen_is_const);
+ ira_ref(var->owner_exec->analysis);
if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) {
return ir_const_void(ira, &decl_var_instruction->base);
@@ -15869,8 +16273,8 @@ static IrInstruction *ir_analyze_instruction_error_union(IrAnalyze *ira,
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type);
result->value->special = ConstValSpecialLazy;
- LazyValueErrUnionType *lazy_err_union_type = allocate<LazyValueErrUnionType>(1);
- lazy_err_union_type->ira = ira;
+ LazyValueErrUnionType *lazy_err_union_type = allocate<LazyValueErrUnionType>(1, "LazyValueErrUnionType");
+ lazy_err_union_type->ira = ira; ira_ref(ira);
result->value->data.x_lazy = &lazy_err_union_type->base;
lazy_err_union_type->base.id = LazyValueIdErrUnionType;
@@ -17368,8 +17772,8 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
if (type_is_invalid(impl_fn->type_entry))
return ira->codegen->invalid_instruction;
- impl_fn->ir_executable.source_node = call_instruction->base.source_node;
- impl_fn->ir_executable.parent_exec = ira->new_irb.exec;
+ impl_fn->ir_executable->source_node = call_instruction->base.source_node;
+ impl_fn->ir_executable->parent_exec = ira->new_irb.exec;
impl_fn->analyzed_executable.source_node = call_instruction->base.source_node;
impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec;
impl_fn->analyzed_executable.backward_branch_quota = ira->new_irb.exec->backward_branch_quota;
@@ -17722,8 +18126,8 @@ static IrInstruction *ir_analyze_optional_type(IrAnalyze *ira, IrInstructionUnOp
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type);
result->value->special = ConstValSpecialLazy;
- LazyValueOptType *lazy_opt_type = allocate<LazyValueOptType>(1);
- lazy_opt_type->ira = ira;
+ LazyValueOptType *lazy_opt_type = allocate<LazyValueOptType>(1, "LazyValueOptType");
+ lazy_opt_type->ira = ira; ira_ref(ira);
result->value->data.x_lazy = &lazy_opt_type->base;
lazy_opt_type->base.id = LazyValueIdOptType;
@@ -19668,8 +20072,8 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
IrInstruction *result = ir_const(ira, &slice_type_instruction->base, ira->codegen->builtin_types.entry_type);
result->value->special = ConstValSpecialLazy;
- LazyValueSliceType *lazy_slice_type = allocate<LazyValueSliceType>(1);
- lazy_slice_type->ira = ira;
+ LazyValueSliceType *lazy_slice_type = allocate<LazyValueSliceType>(1, "LazyValueSliceType");
+ lazy_slice_type->ira = ira; ira_ref(ira);
result->value->data.x_lazy = &lazy_slice_type->base;
lazy_slice_type->base.id = LazyValueIdSliceType;
@@ -19828,8 +20232,8 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstructi
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int);
result->value->special = ConstValSpecialLazy;
- LazyValueSizeOf *lazy_size_of = allocate<LazyValueSizeOf>(1);
- lazy_size_of->ira = ira;
+ LazyValueSizeOf *lazy_size_of = allocate<LazyValueSizeOf>(1, "LazyValueSizeOf");
+ lazy_size_of->ira = ira; ira_ref(ira);
result->value->data.x_lazy = &lazy_size_of->base;
lazy_size_of->base.id = LazyValueIdSizeOf;
@@ -24556,8 +24960,8 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int);
result->value->special = ConstValSpecialLazy;
- LazyValueAlignOf *lazy_align_of = allocate<LazyValueAlignOf>(1);
- lazy_align_of->ira = ira;
+ LazyValueAlignOf *lazy_align_of = allocate<LazyValueAlignOf>(1, "LazyValueAlignOf");
+ lazy_align_of->ira = ira; ira_ref(ira);
result->value->data.x_lazy = &lazy_align_of->base;
lazy_align_of->base.id = LazyValueIdAlignOf;
@@ -25040,8 +25444,8 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type);
result->value->special = ConstValSpecialLazy;
- LazyValueFnType *lazy_fn_type = allocate<LazyValueFnType>(1);
- lazy_fn_type->ira = ira;
+ LazyValueFnType *lazy_fn_type = allocate<LazyValueFnType>(1, "LazyValueFnType");
+ lazy_fn_type->ira = ira; ira_ref(ira);
result->value->data.x_lazy = &lazy_fn_type->base;
lazy_fn_type->base.id = LazyValueIdFnType;
@@ -26081,8 +26485,8 @@ static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruct
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type);
result->value->special = ConstValSpecialLazy;
- LazyValuePtrType *lazy_ptr_type = allocate<LazyValuePtrType>(1);
- lazy_ptr_type->ira = ira;
+ LazyValuePtrType *lazy_ptr_type = allocate<LazyValuePtrType>(1, "LazyValuePtrType");
+ lazy_ptr_type->ira = ira; ira_ref(ira);
result->value->data.x_lazy = &lazy_ptr_type->base;
lazy_ptr_type->base.id = LazyValueIdPtrType;
@@ -27551,7 +27955,8 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_
assert(old_exec->first_err_trace_msg == nullptr);
assert(expected_type == nullptr || !type_is_invalid(expected_type));
- IrAnalyze *ira = allocate<IrAnalyze>(1);
+ IrAnalyze *ira = allocate<IrAnalyze>(1, "IrAnalyze");
+ ira->ref_count = 1;
old_exec->analysis = ira;
ira->codegen = codegen;
@@ -27618,6 +28023,7 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_
ira->instruction_index += 1;
}
+ ZigType *res_type;
if (new_exec->first_err_trace_msg != nullptr) {
codegen->trace_err = new_exec->first_err_trace_msg;
if (codegen->trace_err != nullptr && new_exec->source_node != nullptr &&
@@ -27627,13 +28033,18 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_
codegen->trace_err = add_error_note(codegen, codegen->trace_err,
new_exec->source_node, buf_create_from_str("referenced here"));
}
- return ira->codegen->builtin_types.entry_invalid;
+ res_type = ira->codegen->builtin_types.entry_invalid;
} else if (ira->src_implicit_return_type_list.length == 0) {
- return codegen->builtin_types.entry_unreachable;
+ res_type = codegen->builtin_types.entry_unreachable;
} else {
- return ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items,
+ res_type = ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items,
ira->src_implicit_return_type_list.length);
}
+
+ // It is now safe to free Pass 1 IR instructions.
+ ira_deref(ira);
+
+ return res_type;
}
bool ir_has_side_effects(IrInstruction *instruction) {
@@ -27969,6 +28380,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
val->special = ConstValSpecialStatic;
assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt);
bigint_init_unsigned(&val->data.x_bigint, align_in_bytes);
+
+ // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
return ErrorNone;
}
case LazyValueIdSizeOf: {
@@ -28024,6 +28437,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
val->special = ConstValSpecialStatic;
assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt);
bigint_init_unsigned(&val->data.x_bigint, abi_size);
+
+ // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
return ErrorNone;
}
case LazyValueIdSliceType: {
@@ -28102,6 +28517,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
val->special = ConstValSpecialStatic;
assert(val->type->id == ZigTypeIdMetaType);
val->data.x_type = get_slice_type(ira->codegen, slice_ptr_type);
+
+ // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
return ErrorNone;
}
case LazyValueIdPtrType: {
@@ -28173,6 +28590,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
lazy_ptr_type->bit_offset_in_host, lazy_ptr_type->host_int_bytes,
allow_zero, VECTOR_INDEX_NONE, nullptr, sentinel_val);
val->special = ConstValSpecialStatic;
+
+ // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
return ErrorNone;
}
case LazyValueIdOptType: {
@@ -28195,16 +28614,21 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
assert(val->type->id == ZigTypeIdMetaType);
val->data.x_type = get_optional_type(ira->codegen, payload_type);
val->special = ConstValSpecialStatic;
+
+ // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
return ErrorNone;
}
case LazyValueIdFnType: {
LazyValueFnType *lazy_fn_type = reinterpret_cast<LazyValueFnType *>(val->data.x_lazy);
- ZigType *fn_type = ir_resolve_lazy_fn_type(lazy_fn_type->ira, source_node, lazy_fn_type);
+ IrAnalyze *ira = lazy_fn_type->ira;
+ ZigType *fn_type = ir_resolve_lazy_fn_type(ira, source_node, lazy_fn_type);
if (fn_type == nullptr)
return ErrorSemanticAnalyzeFail;
val->special = ConstValSpecialStatic;
assert(val->type->id == ZigTypeIdMetaType);
val->data.x_type = fn_type;
+
+ // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
return ErrorNone;
}
case LazyValueIdErrUnionType: {
@@ -28233,6 +28657,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
assert(val->type->id == ZigTypeIdMetaType);
val->data.x_type = get_error_union_type(ira->codegen, err_set_type, payload_type);
val->special = ConstValSpecialStatic;
+
+ // We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
return ErrorNone;
}
}
diff --git a/src/libc_installation.cpp b/src/libc_installation.cpp
@@ -389,7 +389,7 @@ static Error zig_libc_find_native_msvc_include_dir(ZigLibCInstallation *self, Zi
}
Buf search_path = BUF_INIT;
buf_init_from_mem(&search_path, sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len);
- buf_append_str(&search_path, "\\..\\..\\include");
+ buf_append_str(&search_path, "..\\..\\include");
Buf *vcruntime_path = buf_sprintf("%s\\vcruntime.h", buf_ptr(&search_path));
bool exists;
diff --git a/src/list.hpp b/src/list.hpp
@@ -13,7 +13,7 @@
template<typename T>
struct ZigList {
void deinit() {
- free(items);
+ deallocate(items, capacity);
}
void append(const T& item) {
ensure_capacity(length + 1);
diff --git a/src/memory_profiling.cpp b/src/memory_profiling.cpp
@@ -35,7 +35,9 @@ static const char *get_default_name(const char *name_or_null, size_t type_size)
if (name_or_null != nullptr) return name_or_null;
if (type_size >= unknown_names.length) {
table_active = false;
- unknown_names.resize(type_size + 1);
+ while (type_size >= unknown_names.length) {
+ unknown_names.append(nullptr);
+ }
table_active = true;
}
if (unknown_names.at(type_size) == nullptr) {
@@ -66,7 +68,8 @@ void memprof_dealloc(const char *name, size_t count, size_t type_size) {
name = get_default_name(name, type_size);
auto existing_entry = usage_table.maybe_get(name);
if (existing_entry == nullptr) {
- zig_panic("deallocated more than allocated; compromised memory usage stats");
+ zig_panic("deallocated name '%s' (size %zu) not found in allocated table; compromised memory usage stats",
+ name, type_size);
}
if (existing_entry->value.type_size != type_size) {
zig_panic("deallocated name '%s' does not match expected type size %zu", name, type_size);
diff --git a/src/os.cpp b/src/os.cpp
@@ -1554,7 +1554,7 @@ void os_stderr_set_color(TermColor color) {
Error os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) {
#if defined(ZIG_OS_WINDOWS)
buf_resize(output_buf, 0);
- buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr);
+ buf_appendf(output_buf, "%sLib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr);
switch (platform_type) {
case ZigLLVM_x86:
buf_append_str(output_buf, "x86\\");
@@ -1586,7 +1586,7 @@ Error os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Ar
Error os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) {
#if defined(ZIG_OS_WINDOWS)
buf_resize(output_buf, 0);
- buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr);
+ buf_appendf(output_buf, "%sInclude\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr);
if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) {
return ErrorNone;
}
@@ -1603,7 +1603,7 @@ Error os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch
#if defined(ZIG_OS_WINDOWS)
{
buf_resize(output_buf, 0);
- buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr);
+ buf_appendf(output_buf, "%sLib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr);
switch (platform_type) {
case ZigLLVM_x86:
buf_append_str(output_buf, "x86\\");
@@ -1626,7 +1626,7 @@ Error os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch
}
{
buf_resize(output_buf, 0);
- buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr);
+ buf_appendf(output_buf, "%sLib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr);
switch (platform_type) {
case ZigLLVM_x86:
buf_append_str(output_buf, "x86\\");
diff --git a/src/util.hpp b/src/util.hpp
@@ -165,7 +165,7 @@ static inline void deallocate(T *old, size_t count, const char *name = nullptr)
template<typename T>
static inline void destroy(T *old, const char *name = nullptr) {
- return deallocate(old, 1);
+ return deallocate(old, 1, name);
}
template <typename T, size_t n>
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
@@ -1670,10 +1670,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ var spartan_count: u16 = 300;
\\ var byte: u8 = spartan_count;
\\}
+ \\export fn entry4() void {
+ \\ var signed: i8 = -1;
+ \\ var unsigned: u64 = signed;
+ \\}
,
"tmp.zig:3:31: error: integer value 300 cannot be coerced to type 'u8'",
"tmp.zig:7:22: error: integer value 300 cannot be coerced to type 'u8'",
"tmp.zig:11:20: error: expected type 'u8', found 'u16'",
+ "tmp.zig:11:20: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values",
+ "tmp.zig:15:25: error: expected type 'u64', found 'i8'",
+ "tmp.zig:15:25: note: unsigned 64-bit int cannot represent all possible signed 8-bit values",
);
cases.add(
diff --git a/test/gen_h.zig b/test/gen_h.zig
@@ -10,9 +10,8 @@ pub fn addCases(cases: *tests.GenHContext) void {
\\ B = 1,
\\ C = 2
\\};
- \\
- \\TEST_EXTERN_C void entry(enum Foo foo);
- \\
+ ,
+ \\void entry(enum Foo foo);
);
cases.add("declare struct",
@@ -34,8 +33,8 @@ pub fn addCases(cases: *tests.GenHContext) void {
\\ uint64_t E;
\\ uint64_t F;
\\};
- \\
- \\TEST_EXTERN_C void entry(struct Foo foo);
+ ,
+ \\void entry(struct Foo foo);
\\
);
@@ -69,19 +68,19 @@ pub fn addCases(cases: *tests.GenHContext) void {
\\ bool C;
\\ struct Big D;
\\};
- \\
- \\TEST_EXTERN_C void entry(union Foo foo);
+ ,
+ \\void entry(union Foo foo);
\\
);
cases.add("declare opaque type",
- \\export const Foo = @OpaqueType();
+ \\const Foo = @OpaqueType();
\\
\\export fn entry(foo: ?*Foo) void { }
,
\\struct Foo;
- \\
- \\TEST_EXTERN_C void entry(struct Foo * foo);
+ ,
+ \\void entry(struct Foo * foo);
);
cases.add("array field-type",
@@ -95,8 +94,8 @@ pub fn addCases(cases: *tests.GenHContext) void {
\\ int32_t A[2];
\\ uint32_t * B[4];
\\};
- \\
- \\TEST_EXTERN_C void entry(struct Foo foo, uint8_t bar[]);
+ ,
+ \\void entry(struct Foo foo, uint8_t bar[]);
\\
);
@@ -110,7 +109,8 @@ pub fn addCases(cases: *tests.GenHContext) void {
\\}
,
\\struct S;
- \\TEST_EXTERN_C uint8_t a(struct S * s);
+ ,
+ \\uint8_t a(struct S * s);
\\
);
@@ -125,7 +125,8 @@ pub fn addCases(cases: *tests.GenHContext) void {
\\}
,
\\union U;
- \\TEST_EXTERN_C uint8_t a(union U * s);
+ ,
+ \\uint8_t a(union U * s);
\\
);
@@ -140,7 +141,8 @@ pub fn addCases(cases: *tests.GenHContext) void {
\\}
,
\\enum E;
- \\TEST_EXTERN_C uint8_t a(enum E * s);
+ ,
+ \\uint8_t a(enum E * s);
\\
);
}
diff --git a/test/standalone/cat/main.zig b/test/standalone/cat/main.zig
@@ -1,7 +1,7 @@
const std = @import("std");
const io = std.io;
const process = std.process;
-const File = std.fs.File;
+const fs = std.fs;
const mem = std.mem;
const warn = std.debug.warn;
const allocator = std.debug.global_allocator;
@@ -12,6 +12,8 @@ pub fn main() !void {
var catted_anything = false;
const stdout_file = io.getStdOut();
+ const cwd = fs.cwd();
+
while (args_it.next(allocator)) |arg_or_err| {
const arg = try unwrapArg(arg_or_err);
if (mem.eql(u8, arg, "-")) {
@@ -20,7 +22,7 @@ pub fn main() !void {
} else if (arg[0] == '-') {
return usage(exe);
} else {
- const file = File.openRead(arg) catch |err| {
+ const file = cwd.openFile(arg, .{}) catch |err| {
warn("Unable to open file: {}\n", @errorName(err));
return err;
};
@@ -40,7 +42,7 @@ fn usage(exe: []const u8) !void {
return error.Invalid;
}
-fn cat_file(stdout: File, file: File) !void {
+fn cat_file(stdout: fs.File, file: fs.File) !void {
var buf: [1024 * 4]u8 = undefined;
while (true) {
diff --git a/test/standalone/static_c_lib/foo.c b/test/standalone/static_c_lib/foo.c
@@ -2,3 +2,5 @@
uint32_t add(uint32_t a, uint32_t b) {
return a + b;
}
+
+uint32_t foo = 12345;
diff --git a/test/standalone/static_c_lib/foo.h b/test/standalone/static_c_lib/foo.h
@@ -1,2 +1,3 @@
#include <stdint.h>
uint32_t add(uint32_t a, uint32_t b);
+extern uint32_t foo;
diff --git a/test/standalone/static_c_lib/foo.zig b/test/standalone/static_c_lib/foo.zig
@@ -6,3 +6,7 @@ test "C add" {
const result = c.add(1, 2);
expect(result == 3);
}
+
+test "C extern variable" {
+ expect(c.foo == 12345);
+}
diff --git a/test/tests.zig b/test/tests.zig
@@ -74,6 +74,26 @@ const test_targets = [_]TestTarget{
.target = Target{
.Cross = CrossTarget{
.os = .linux,
+ .arch = .i386,
+ .abi = .none,
+ },
+ },
+ },
+ TestTarget{
+ .target = Target{
+ .Cross = CrossTarget{
+ .os = .linux,
+ .arch = .i386,
+ .abi = .musl,
+ },
+ },
+ .link_libc = true,
+ },
+
+ TestTarget{
+ .target = Target{
+ .Cross = CrossTarget{
+ .os = .linux,
.arch = builtin.Arch{ .aarch64 = builtin.Arch.Arm64.v8_5a },
.abi = .none,
},