Merge remote-tracking branch 'origin/master' into remove-array-type-coercion

This commit is contained in:
Andrew Kelley 2019-12-01 09:56:01 -05:00
commit b36c07a95a
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
65 changed files with 3021 additions and 995 deletions

View File

@ -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#}

View File

@ -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;

View File

@ -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"));
}

View File

@ -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());

View File

@ -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;

View File

@ -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]);

View File

@ -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);

View File

@ -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,
}

View File

@ -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 };
}
pub fn openReadW(self: Dir, sub_path_w: [*:0]const u16) File.OpenError!File {
/// 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);
}
/// 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" {

View File

@ -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{

View File

@ -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);

View File

@ -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 {

View File

@ -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

View File

@ -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;

View File

@ -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 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;
}
// 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) {
// 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);
// 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;
}
// 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);
}
// 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;
};
}
}
};

View File

@ -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,

View File

@ -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),
}
}

View File

@ -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"),

View File

@ -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";

View File

@ -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";

View File

@ -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;

View File

@ -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";

View File

@ -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";

View File

@ -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);
}

119
lib/std/os/linux/i386.zig Normal file
View File

@ -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"
);
}

View File

@ -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;

View File

@ -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)),
};
}

View File

@ -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" {

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

433
lib/std/reset_event.zig Normal file
View File

@ -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();
}

View File

@ -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

View File

@ -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");

View File

@ -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.

View File

@ -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,

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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: {

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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();
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);
// 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);
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;
}
}

View File

@ -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,

View File

@ -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);
}
},
}

View File

@ -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);

View File

@ -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),
}
}

View File

@ -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);

View File

@ -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 {

View File

@ -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) {

View File

@ -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, &param_type_c);
if (param_info->type->id == ZigTypeIdArray) {
// Arrays decay to pointers
buf_appendf(&h_buf, "%s%s%s %s[]", comma_str, buf_ptr(&param_type_c),
restrict_str, buf_ptr(param_name));
} else {
buf_appendf(&h_buf, "%s%s%s %s", comma_str, buf_ptr(&param_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;
}
}
}
fprintf(out_h, "%s", buf_ptr(&h_buf));
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);
fprintf(out_h, "\n#endif\n");
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, &param_type_c);
if (param_info->type->id == ZigTypeIdArray) {
// Arrays decay to pointers
buf_appendf(out_buf, "%s%s%s %s[]", comma_str, buf_ptr(&param_type_c),
restrict_str, buf_ptr(param_name));
} else {
buf_appendf(out_buf, "%s%s%s %s", comma_str, buf_ptr(&param_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\n", buf_ptr(&vars_buf));
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));

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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\\");

View File

@ -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>

View File

@ -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(

View File

@ -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);
\\
);
}

View File

@ -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) {

View File

@ -2,3 +2,5 @@
uint32_t add(uint32_t a, uint32_t b) {
return a + b;
}
uint32_t foo = 12345;

View File

@ -1,2 +1,3 @@
#include <stdint.h>
uint32_t add(uint32_t a, uint32_t b);
extern uint32_t foo;

View File

@ -6,3 +6,7 @@ test "C add" {
const result = c.add(1, 2);
expect(result == 3);
}
test "C extern variable" {
expect(c.foo == 12345);
}

View File

@ -70,6 +70,26 @@ const test_targets = [_]TestTarget{
.link_libc = true,
},
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{