srctree

Andrew Kelley parent 72443fb8 75ff34db df4cfc2e
Merge remote-tracking branch 'origin/master' into llvm16

inlinesplit
lib/build_runner.zig added: 1877, removed: 639, total 1238
@@ -362,6 +362,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi
\\ --cache-dir [path] Override path to local Zig cache directory
\\ --global-cache-dir [path] Override path to global Zig cache directory
\\ --zig-lib-dir [arg] Override path to Zig lib directory
\\ --build-runner [file] Override path to build runner
\\ --debug-log [scope] Enable debugging the compiler
\\ --verbose-link Enable compiler debug output for linking
\\ --verbose-air Enable compiler debug output for Zig AIR
 
lib/std/Build/Cache.zig added: 1877, removed: 639, total 1238
@@ -956,11 +956,16 @@ fn hashFile(file: fs.File, bin_digest: *[Hasher.mac_length]u8) !void {
 
// Create/Write a file, close it, then grab its stat.mtime timestamp.
fn testGetCurrentFileTimestamp() !i128 {
var file = try fs.cwd().createFile("test-filetimestamp.tmp", .{
const test_out_file = "test-filetimestamp.tmp";
 
var file = try fs.cwd().createFile(test_out_file, .{
.read = true,
.truncate = true,
});
defer file.close();
defer {
file.close();
fs.cwd().deleteFile(test_out_file) catch {};
}
 
return (try file.stat()).mtime;
}
 
lib/std/c.zig added: 1877, removed: 639, total 1238
@@ -171,7 +171,9 @@ pub extern "c" fn dup(fd: c.fd_t) c_int;
pub extern "c" fn dup2(old_fd: c.fd_t, new_fd: c.fd_t) c_int;
pub extern "c" fn readlink(noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize;
pub extern "c" fn readlinkat(dirfd: c.fd_t, noalias path: [*:0]const u8, noalias buf: [*]u8, bufsize: usize) isize;
pub extern "c" fn chmod(path: [*:0]const u8, mode: c.mode_t) c_int;
pub extern "c" fn fchmod(fd: c.fd_t, mode: c.mode_t) c_int;
pub extern "c" fn fchmodat(fd: c.fd_t, path: [*:0]const u8, mode: c.mode_t, flags: c_uint) c_int;
pub extern "c" fn fchown(fd: c.fd_t, owner: c.uid_t, group: c.gid_t) c_int;
pub extern "c" fn umask(mode: c.mode_t) c.mode_t;
 
 
lib/std/child_process.zig added: 1877, removed: 639, total 1238
@@ -19,8 +19,15 @@ const maxInt = std.math.maxInt;
const assert = std.debug.assert;
 
pub const ChildProcess = struct {
pid: if (builtin.os.tag == .windows) void else i32,
handle: if (builtin.os.tag == .windows) windows.HANDLE else void,
pub const Id = switch (builtin.os.tag) {
.windows => windows.HANDLE,
else => os.pid_t,
};
 
/// Available after calling `spawn()`. This becomes `undefined` after calling `wait()`.
/// On Windows this is the hProcess.
/// On POSIX this is the pid.
id: Id,
thread_handle: if (builtin.os.tag == .windows) windows.HANDLE else void,
 
allocator: mem.Allocator,
@@ -105,8 +112,7 @@ pub const ChildProcess = struct {
return .{
.allocator = allocator,
.argv = argv,
.pid = undefined,
.handle = undefined,
.id = undefined,
.thread_handle = undefined,
.err_pipe = null,
.term = null,
@@ -131,6 +137,7 @@ pub const ChildProcess = struct {
}
 
/// On success must call `kill` or `wait`.
/// After spawning the `id` is available.
pub fn spawn(self: *ChildProcess) SpawnError!void {
if (!std.process.can_spawn) {
@compileError("the target operating system cannot spawn processes");
@@ -167,7 +174,7 @@ pub const ChildProcess = struct {
return term;
}
 
try windows.TerminateProcess(self.handle, exit_code);
try windows.TerminateProcess(self.id, exit_code);
try self.waitUnwrappedWindows();
return self.term.?;
}
@@ -177,18 +184,21 @@ pub const ChildProcess = struct {
self.cleanupStreams();
return term;
}
try os.kill(self.pid, os.SIG.TERM);
try os.kill(self.id, os.SIG.TERM);
try self.waitUnwrapped();
return self.term.?;
}
 
/// Blocks until child process terminates and then cleans up all resources.
pub fn wait(self: *ChildProcess) !Term {
if (builtin.os.tag == .windows) {
return self.waitWindows();
} else {
return self.waitPosix();
}
const term = if (builtin.os.tag == .windows)
try self.waitWindows()
else
try self.waitPosix();
 
self.id = undefined;
 
return term;
}
 
pub const ExecResult = struct {
@@ -197,6 +207,19 @@ pub const ChildProcess = struct {
stderr: []u8,
};
 
fn fifoToOwnedArrayList(fifo: *std.io.PollFifo) std.ArrayList(u8) {
if (fifo.head > 0) {
std.mem.copy(u8, fifo.buf[0..fifo.count], fifo.buf[fifo.head .. fifo.head + fifo.count]);
}
const result = std.ArrayList(u8){
.items = fifo.buf[0..fifo.count],
.capacity = fifo.buf.len,
.allocator = fifo.allocator,
};
fifo.* = std.io.PollFifo.init(fifo.allocator);
return result;
}
 
/// Collect the output from the process's stdout and stderr. Will return once all output
/// has been collected. This does not mean that the process has ended. `wait` should still
/// be called to wait for and clean up the process.
@@ -210,197 +233,34 @@ pub const ChildProcess = struct {
) !void {
debug.assert(child.stdout_behavior == .Pipe);
debug.assert(child.stderr_behavior == .Pipe);
if (builtin.os.tag == .haiku) {
const stdout_in = child.stdout.?.reader();
const stderr_in = child.stderr.?.reader();
 
try stdout_in.readAllArrayList(stdout, max_output_bytes);
try stderr_in.readAllArrayList(stderr, max_output_bytes);
} else if (builtin.os.tag == .windows) {
try collectOutputWindows(child, stdout, stderr, max_output_bytes);
} else {
try collectOutputPosix(child, stdout, stderr, max_output_bytes);
// we could make this work with multiple allocators but YAGNI
if (stdout.allocator.ptr != stderr.allocator.ptr or
stdout.allocator.vtable != stderr.allocator.vtable)
@panic("ChildProcess.collectOutput only supports 1 allocator");
 
var poller = std.io.poll(stdout.allocator, enum { stdout, stderr }, .{
.stdout = child.stdout.?,
.stderr = child.stderr.?,
});
defer poller.deinit();
 
while (try poller.poll()) {
if (poller.fifo(.stdout).count > max_output_bytes)
return error.StdoutStreamTooLong;
if (poller.fifo(.stderr).count > max_output_bytes)
return error.StderrStreamTooLong;
}
 
stdout.* = fifoToOwnedArrayList(poller.fifo(.stdout));
stderr.* = fifoToOwnedArrayList(poller.fifo(.stderr));
}
 
fn collectOutputPosix(
child: ChildProcess,
stdout: *std.ArrayList(u8),
stderr: *std.ArrayList(u8),
max_output_bytes: usize,
) !void {
var poll_fds = [_]os.pollfd{
.{ .fd = child.stdout.?.handle, .events = os.POLL.IN, .revents = undefined },
.{ .fd = child.stderr.?.handle, .events = os.POLL.IN, .revents = undefined },
};
 
var dead_fds: usize = 0;
// We ask for ensureTotalCapacity with this much extra space. This has more of an
// effect on small reads because once the reads start to get larger the amount
// of space an ArrayList will allocate grows exponentially.
const bump_amt = 512;
 
const err_mask = os.POLL.ERR | os.POLL.NVAL | os.POLL.HUP;
 
while (dead_fds < poll_fds.len) {
const events = try os.poll(&poll_fds, std.math.maxInt(i32));
if (events == 0) continue;
 
var remove_stdout = false;
var remove_stderr = false;
// Try reading whatever is available before checking the error
// conditions.
// It's still possible to read after a POLL.HUP is received, always
// check if there's some data waiting to be read first.
if (poll_fds[0].revents & os.POLL.IN != 0) {
// stdout is ready.
const new_capacity = std.math.min(stdout.items.len + bump_amt, max_output_bytes);
try stdout.ensureTotalCapacity(new_capacity);
const buf = stdout.unusedCapacitySlice();
if (buf.len == 0) return error.StdoutStreamTooLong;
const nread = try os.read(poll_fds[0].fd, buf);
stdout.items.len += nread;
 
// Remove the fd when the EOF condition is met.
remove_stdout = nread == 0;
} else {
remove_stdout = poll_fds[0].revents & err_mask != 0;
}
 
if (poll_fds[1].revents & os.POLL.IN != 0) {
// stderr is ready.
const new_capacity = std.math.min(stderr.items.len + bump_amt, max_output_bytes);
try stderr.ensureTotalCapacity(new_capacity);
const buf = stderr.unusedCapacitySlice();
if (buf.len == 0) return error.StderrStreamTooLong;
const nread = try os.read(poll_fds[1].fd, buf);
stderr.items.len += nread;
 
// Remove the fd when the EOF condition is met.
remove_stderr = nread == 0;
} else {
remove_stderr = poll_fds[1].revents & err_mask != 0;
}
 
// Exclude the fds that signaled an error.
if (remove_stdout) {
poll_fds[0].fd = -1;
dead_fds += 1;
}
if (remove_stderr) {
poll_fds[1].fd = -1;
dead_fds += 1;
}
}
}
 
const WindowsAsyncReadResult = enum {
pending,
closed,
full,
pub const ExecError = os.GetCwdError || os.ReadError || SpawnError || os.PollError || error{
StdoutStreamTooLong,
StderrStreamTooLong,
};
 
fn windowsAsyncRead(
handle: windows.HANDLE,
overlapped: *windows.OVERLAPPED,
buf: *std.ArrayList(u8),
bump_amt: usize,
max_output_bytes: usize,
) !WindowsAsyncReadResult {
while (true) {
const new_capacity = std.math.min(buf.items.len + bump_amt, max_output_bytes);
try buf.ensureTotalCapacity(new_capacity);
const next_buf = buf.unusedCapacitySlice();
if (next_buf.len == 0) return .full;
var read_bytes: u32 = undefined;
const read_result = windows.kernel32.ReadFile(handle, next_buf.ptr, math.cast(u32, next_buf.len) orelse maxInt(u32), &read_bytes, overlapped);
if (read_result == 0) return switch (windows.kernel32.GetLastError()) {
.IO_PENDING => .pending,
.BROKEN_PIPE => .closed,
else => |err| windows.unexpectedError(err),
};
buf.items.len += read_bytes;
}
}
 
fn collectOutputWindows(child: ChildProcess, stdout: *std.ArrayList(u8), stderr: *std.ArrayList(u8), max_output_bytes: usize) !void {
const bump_amt = 512;
const outs = [_]*std.ArrayList(u8){
stdout,
stderr,
};
const handles = [_]windows.HANDLE{
child.stdout.?.handle,
child.stderr.?.handle,
};
 
var overlapped = [_]windows.OVERLAPPED{
mem.zeroes(windows.OVERLAPPED),
mem.zeroes(windows.OVERLAPPED),
};
 
var wait_objects: [2]windows.HANDLE = undefined;
var wait_object_count: u2 = 0;
 
// we need to cancel all pending IO before returning so our OVERLAPPED values don't go out of scope
defer for (wait_objects[0..wait_object_count]) |o| {
_ = windows.kernel32.CancelIo(o);
};
 
// Windows Async IO requires an initial call to ReadFile before waiting on the handle
for ([_]u1{ 0, 1 }) |i| {
switch (try windowsAsyncRead(handles[i], &overlapped[i], outs[i], bump_amt, max_output_bytes)) {
.pending => {
wait_objects[wait_object_count] = handles[i];
wait_object_count += 1;
},
.closed => {}, // don't add to the wait_objects list
.full => return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong,
}
}
 
while (wait_object_count > 0) {
const status = windows.kernel32.WaitForMultipleObjects(wait_object_count, &wait_objects, 0, windows.INFINITE);
if (status == windows.WAIT_FAILED) {
switch (windows.kernel32.GetLastError()) {
else => |err| return windows.unexpectedError(err),
}
}
if (status < windows.WAIT_OBJECT_0 or status > windows.WAIT_OBJECT_0 + wait_object_count - 1)
unreachable;
 
const wait_idx = status - windows.WAIT_OBJECT_0;
 
// this extra `i` index is needed to map the wait handle back to the stdout or stderr
// values since the wait_idx can change which handle it corresponds with
const i: u1 = if (wait_objects[wait_idx] == handles[0]) 0 else 1;
 
// remove completed event from the wait list
wait_object_count -= 1;
if (wait_idx == 0)
wait_objects[0] = wait_objects[1];
 
var read_bytes: u32 = undefined;
if (windows.kernel32.GetOverlappedResult(handles[i], &overlapped[i], &read_bytes, 0) == 0) {
switch (windows.kernel32.GetLastError()) {
.BROKEN_PIPE => continue,
else => |err| return windows.unexpectedError(err),
}
}
 
outs[i].items.len += read_bytes;
 
switch (try windowsAsyncRead(handles[i], &overlapped[i], outs[i], bump_amt, max_output_bytes)) {
.pending => {
wait_objects[wait_object_count] = handles[i];
wait_object_count += 1;
},
.closed => {}, // don't add to the wait_objects list
.full => return if (i == 0) error.StdoutStreamTooLong else error.StderrStreamTooLong,
}
}
}
 
/// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
/// If it succeeds, the caller owns result.stdout and result.stderr memory.
pub fn exec(args: struct {
@@ -411,7 +271,7 @@ pub const ChildProcess = struct {
env_map: ?*const EnvMap = null,
max_output_bytes: usize = 50 * 1024,
expand_arg0: Arg0Expand = .no_expand,
}) !ExecResult {
}) ExecError!ExecResult {
var child = ChildProcess.init(args.argv, args.allocator);
child.stdin_behavior = .Ignore;
child.stdout_behavior = .Pipe;
@@ -459,18 +319,18 @@ pub const ChildProcess = struct {
}
 
fn waitUnwrappedWindows(self: *ChildProcess) !void {
const result = windows.WaitForSingleObjectEx(self.handle, windows.INFINITE, false);
const result = windows.WaitForSingleObjectEx(self.id, windows.INFINITE, false);
 
self.term = @as(SpawnError!Term, x: {
var exit_code: windows.DWORD = undefined;
if (windows.kernel32.GetExitCodeProcess(self.handle, &exit_code) == 0) {
if (windows.kernel32.GetExitCodeProcess(self.id, &exit_code) == 0) {
break :x Term{ .Unknown = 0 };
} else {
break :x Term{ .Exited = @truncate(u8, exit_code) };
}
});
 
os.close(self.handle);
os.close(self.id);
os.close(self.thread_handle);
self.cleanupStreams();
return result;
@@ -478,9 +338,9 @@ pub const ChildProcess = struct {
 
fn waitUnwrapped(self: *ChildProcess) !void {
const res: os.WaitPidResult = if (comptime builtin.target.isDarwin())
try os.posix_spawn.waitpid(self.pid, 0)
try os.posix_spawn.waitpid(self.id, 0)
else
os.waitpid(self.pid, 0);
os.waitpid(self.id, 0);
const status = res.status;
self.cleanupStreams();
self.handleWaitResult(status);
@@ -638,7 +498,7 @@ pub const ChildProcess = struct {
self.stderr = null;
}
 
self.pid = pid;
self.id = pid;
self.term = null;
 
if (self.stdin_behavior == StdIo.Pipe) {
@@ -812,7 +672,7 @@ pub const ChildProcess = struct {
self.stderr = null;
}
 
self.pid = pid;
self.id = pid;
self.err_pipe = err_pipe;
self.term = null;
 
@@ -1078,7 +938,7 @@ pub const ChildProcess = struct {
self.stderr = null;
}
 
self.handle = piProcInfo.hProcess;
self.id = piProcInfo.hProcess;
self.thread_handle = piProcInfo.hThread;
self.term = null;
 
 
lib/std/crypto.zig added: 1877, removed: 639, total 1238
@@ -47,6 +47,8 @@ pub const auth = struct {
/// Core functions, that should rarely be used directly by applications.
pub const core = struct {
pub const aes = @import("crypto/aes.zig");
pub const keccak = @import("crypto/keccak_p.zig");
 
pub const Ascon = @import("crypto/ascon.zig").State;
pub const Gimli = @import("crypto/gimli.zig").State;
pub const Xoodoo = @import("crypto/xoodoo.zig").State;
 
lib/std/crypto/25519/field.zig added: 1877, removed: 639, total 1238
@@ -287,7 +287,7 @@ pub const Fe = struct {
return _carry128(&r);
}
 
inline fn _sq(a: Fe, comptime double: bool) Fe {
fn _sq(a: Fe, comptime double: bool) Fe {
var ax: [5]u128 = undefined;
var r: [5]u128 = undefined;
comptime var i = 0;
 
lib/std/crypto/benchmark.zig added: 1877, removed: 639, total 1238
@@ -25,6 +25,8 @@ const hashes = [_]Crypto{
Crypto{ .ty = crypto.hash.sha2.Sha512, .name = "sha512" },
Crypto{ .ty = crypto.hash.sha3.Sha3_256, .name = "sha3-256" },
Crypto{ .ty = crypto.hash.sha3.Sha3_512, .name = "sha3-512" },
Crypto{ .ty = crypto.hash.sha3.Shake128, .name = "shake-128" },
Crypto{ .ty = crypto.hash.sha3.Shake256, .name = "shake-256" },
Crypto{ .ty = crypto.hash.Gimli, .name = "gimli-hash" },
Crypto{ .ty = crypto.hash.blake2.Blake2s256, .name = "blake2s" },
Crypto{ .ty = crypto.hash.blake2.Blake2b512, .name = "blake2b" },
 
filename was Deleted added: 1877, removed: 639, total 1238
@@ -0,0 +1,277 @@
const std = @import("std");
const assert = std.debug.assert;
const math = std.math;
const mem = std.mem;
 
/// The Keccak-f permutation.
pub fn KeccakF(comptime f: u11) type {
comptime assert(f > 200 and f <= 1600 and f % 200 == 0); // invalid bit size
const T = std.meta.Int(.unsigned, f / 25);
const Block = [25]T;
 
const PI = [_]u5{
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
};
 
return struct {
const Self = @This();
 
/// Number of bytes in the state.
pub const block_bytes = f / 8;
 
/// Maximum number of rounds for the given f parameter.
pub const max_rounds = 12 + 2 * math.log2(f / 25);
 
// Round constants
const RC = rc: {
const RC64 = [_]u64{
0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
};
var rc: [max_rounds]T = undefined;
for (&rc, RC64[0..max_rounds]) |*t, c| t.* = @truncate(T, c);
break :rc rc;
};
 
st: Block = [_]T{0} ** 25,
 
/// Initialize the state from a slice of bytes.
pub fn init(bytes: [block_bytes]u8) Self {
var self: Self = undefined;
inline for (&self.st, 0..) |*r, i| {
r.* = mem.readIntLittle(T, bytes[@sizeOf(T) * i ..][0..@sizeOf(T)]);
}
return self;
}
 
/// A representation of the state as bytes. The byte order is architecture-dependent.
pub fn asBytes(self: *Self) *[block_bytes]u8 {
return mem.asBytes(&self.st);
}
 
/// Byte-swap the entire state if the architecture doesn't match the required endianness.
pub fn endianSwap(self: *Self) void {
for (&self.st) |*w| {
w.* = mem.littleTooNative(T, w.*);
}
}
 
/// Set bytes starting at the beginning of the state.
pub fn setBytes(self: *Self, bytes: []const u8) void {
var i: usize = 0;
while (i + @sizeOf(T) <= bytes.len) : (i += @sizeOf(T)) {
self.st[i / @sizeOf(T)] = mem.readIntLittle(T, bytes[i..][0..@sizeOf(T)]);
}
if (i < bytes.len) {
var padded = [_]u8{0} ** @sizeOf(T);
mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]);
self.st[i / @sizeOf(T)] = mem.readIntLittle(T, padded[0..]);
}
}
 
/// XOR a byte into the state at a given offset.
pub fn addByte(self: *Self, byte: u8, offset: usize) void {
const z = @sizeOf(T) * @truncate(math.Log2Int(T), offset % @sizeOf(T));
self.st[offset / @sizeOf(T)] ^= @as(T, byte) << z;
}
 
/// XOR bytes into the beginning of the state.
pub fn addBytes(self: *Self, bytes: []const u8) void {
var i: usize = 0;
while (i + @sizeOf(T) <= bytes.len) : (i += @sizeOf(T)) {
self.st[i / @sizeOf(T)] ^= mem.readIntLittle(T, bytes[i..][0..@sizeOf(T)]);
}
if (i < bytes.len) {
var padded = [_]u8{0} ** @sizeOf(T);
mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]);
self.st[i / @sizeOf(T)] ^= mem.readIntLittle(T, padded[0..]);
}
}
 
/// Extract the first bytes of the state.
pub fn extractBytes(self: *Self, out: []u8) void {
var i: usize = 0;
while (i + @sizeOf(T) <= out.len) : (i += @sizeOf(T)) {
mem.writeIntLittle(T, out[i..][0..@sizeOf(T)], self.st[i / @sizeOf(T)]);
}
if (i < out.len) {
var padded = [_]u8{0} ** @sizeOf(T);
mem.writeIntLittle(T, padded[0..], self.st[i / @sizeOf(T)]);
mem.copy(u8, out[i..], padded[0 .. out.len - i]);
}
}
 
/// XOR the first bytes of the state into a slice of bytes.
pub fn xorBytes(self: *Self, out: []u8, in: []const u8) void {
assert(out.len == in.len);
 
var i: usize = 0;
while (i + @sizeOf(T) <= in.len) : (i += @sizeOf(T)) {
const x = mem.readIntNative(T, in[i..][0..@sizeOf(T)]) ^ mem.nativeToLittle(T, self.st[i / @sizeOf(T)]);
mem.writeIntNative(T, out[i..][0..@sizeOf(T)], x);
}
if (i < in.len) {
var padded = [_]u8{0} ** @sizeOf(T);
mem.copy(u8, padded[0 .. in.len - i], in[i..]);
const x = mem.readIntNative(T, &padded) ^ mem.nativeToLittle(T, self.st[i / @sizeOf(T)]);
mem.writeIntNative(T, &padded, x);
mem.copy(u8, out[i..], padded[0 .. in.len - i]);
}
}
 
/// Set the words storing the bytes of a given range to zero.
pub fn clear(self: *Self, from: usize, to: usize) void {
mem.set(T, self.st[from / @sizeOf(T) .. (to + @sizeOf(T) - 1) / @sizeOf(T)], 0);
}
 
/// Clear the entire state, disabling compiler optimizations.
pub fn secureZero(self: *Self) void {
std.crypto.utils.secureZero(T, &self.st);
}
 
inline fn round(self: *Self, rc: T) void {
const st = &self.st;
 
// theta
var t = [_]T{0} ** 5;
inline for (0..5) |i| {
inline for (0..5) |j| {
t[i] ^= st[j * 5 + i];
}
}
inline for (0..5) |i| {
inline for (0..5) |j| {
st[j * 5 + i] ^= t[(i + 4) % 5] ^ math.rotl(T, t[(i + 1) % 5], 1);
}
}
 
// rho+pi
var last = st[1];
comptime var rotc = 0;
inline for (0..24) |i| {
const x = PI[i];
const tmp = st[x];
rotc = (rotc + i + 1) % @bitSizeOf(T);
st[x] = math.rotl(T, last, rotc);
last = tmp;
}
inline for (0..5) |i| {
inline for (0..5) |j| {
t[j] = st[i * 5 + j];
}
inline for (0..5) |j| {
st[i * 5 + j] = t[j] ^ (~t[(j + 1) % 5] & t[(j + 2) % 5]);
}
}
 
// iota
st[0] ^= rc;
}
 
/// Apply a (possibly) reduced-round permutation to the state.
pub fn permuteR(self: *Self, comptime rounds: u5) void {
var i = RC.len - rounds;
while (i < rounds - rounds % 3) : (i += 3) {
self.round(RC[i]);
self.round(RC[i + 1]);
self.round(RC[i + 2]);
}
while (i < rounds) : (i += 1) {
self.round(RC[i]);
}
}
 
/// Apply a full-round permutation to the state.
pub fn permute(self: *Self) void {
self.permuteR(max_rounds);
}
};
}
 
/// A generic Keccak-P state.
pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, comptime rounds: u5) type {
comptime assert(f > 200 and f <= 1600 and f % 200 == 0); // invalid state size
comptime assert(capacity < f and capacity % 8 == 0); // invalid capacity size
 
return struct {
const Self = @This();
 
/// The block length, or rate, in bytes.
pub const rate = KeccakF(f).block_bytes - capacity / 8;
/// Keccak does not have any options.
pub const Options = struct {};
 
offset: usize = 0,
buf: [rate]u8 = undefined,
 
st: KeccakF(f) = .{},
 
/// Absorb a slice of bytes into the sponge.
pub fn absorb(self: *Self, bytes_: []const u8) void {
var bytes = bytes_;
if (self.offset > 0) {
const left = math.min(rate - self.offset, bytes.len);
mem.copy(u8, self.buf[self.offset..], bytes[0..left]);
self.offset += left;
if (self.offset == rate) {
self.offset = 0;
self.st.addBytes(self.buf[0..]);
self.st.permuteR(rounds);
}
if (left == bytes.len) return;
bytes = bytes[left..];
}
while (bytes.len >= rate) {
self.st.addBytes(bytes[0..rate]);
self.st.permuteR(rounds);
bytes = bytes[rate..];
}
if (bytes.len > 0) {
self.st.addBytes(bytes[0..]);
self.offset = bytes.len;
}
}
 
/// Mark the end of the input.
pub fn pad(self: *Self) void {
self.st.addBytes(self.buf[0..self.offset]);
self.st.addByte(delim, self.offset);
self.st.addByte(0x80, rate - 1);
self.st.permuteR(rounds);
self.offset = 0;
}
 
/// Squeeze a slice of bytes from the sponge.
pub fn squeeze(self: *Self, out: []u8) void {
var i: usize = 0;
while (i < out.len) : (i += rate) {
const left = math.min(rate, out.len - i);
self.st.extractBytes(out[i..][0..left]);
self.st.permuteR(rounds);
}
}
};
}
 
test "Keccak-f800" {
var st: KeccakF(800) = .{
.st = .{
0xE531D45D, 0xF404C6FB, 0x23A0BF99, 0xF1F8452F, 0x51FFD042, 0xE539F578, 0xF00B80A7,
0xAF973664, 0xBF5AF34C, 0x227A2424, 0x88172715, 0x9F685884, 0xB15CD054, 0x1BF4FC0E,
0x6166FA91, 0x1A9E599A, 0xA3970A1F, 0xAB659687, 0xAFAB8D68, 0xE74B1015, 0x34001A98,
0x4119EFF3, 0x930A0E76, 0x87B28070, 0x11EFE996,
},
};
st.permute();
const expected: [25]u32 = .{
0x75BF2D0D, 0x9B610E89, 0xC826AF40, 0x64CD84AB, 0xF905BDD6, 0xBC832835, 0x5F8001B9,
0x15662CCE, 0x8E38C95E, 0x701FE543, 0x1B544380, 0x89ACDEFF, 0x51EDB5DE, 0x0E9702D9,
0x6C19AA16, 0xA2913EEE, 0x60754E9A, 0x9819063C, 0xF4709254, 0xD09F9084, 0x772DA259,
0x1DB35DF7, 0x5AA60162, 0x358825D5, 0xB3783BAB,
};
try std.testing.expectEqualSlices(u32, &st.st, &expected);
}
 
lib/std/crypto/sha3.zig added: 1877, removed: 639, total 1238
@@ -1,84 +1,63 @@
const std = @import("../std.zig");
const mem = std.mem;
const std = @import("std");
const assert = std.debug.assert;
const math = std.math;
const debug = std.debug;
const htest = @import("test.zig");
const mem = std.mem;
 
pub const Sha3_224 = Keccak(224, 0x06);
pub const Sha3_256 = Keccak(256, 0x06);
pub const Sha3_384 = Keccak(384, 0x06);
pub const Sha3_512 = Keccak(512, 0x06);
pub const Keccak_256 = Keccak(256, 0x01);
pub const Keccak_512 = Keccak(512, 0x01);
const KeccakState = std.crypto.core.keccak.State;
 
pub const Sha3_224 = Keccak(1600, 224, 0x06, 24);
pub const Sha3_256 = Keccak(1600, 256, 0x06, 24);
pub const Sha3_384 = Keccak(1600, 384, 0x06, 24);
pub const Sha3_512 = Keccak(1600, 512, 0x06, 24);
 
pub const Keccak256 = Keccak(1600, 256, 0x01, 24);
pub const Keccak512 = Keccak(1600, 512, 0x01, 24);
pub const Keccak_256 = @compileError("Deprecated: use `Keccak256` instead");
pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead");
 
pub const Shake128 = Shake(128);
pub const Shake256 = Shake(256);
 
/// A generic Keccak hash function.
pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, comptime rounds: u5) type {
comptime assert(output_bits > 0 and output_bits * 2 < f and output_bits % 8 == 0); // invalid output length
 
const State = KeccakState(f, output_bits * 2, delim, rounds);
 
fn Keccak(comptime bits: usize, comptime delim: u8) type {
return struct {
const Self = @This();
 
st: State = .{},
 
/// The output length, in bytes.
pub const digest_length = bits / 8;
pub const digest_length = output_bits / 8;
/// The block length, or rate, in bytes.
pub const block_length = 200 - bits / 4;
pub const block_length = State.rate;
/// Keccak does not have any options.
pub const Options = struct {};
 
s: [200]u8,
offset: usize,
 
/// Initialize a Keccak hash function.
pub fn init(options: Options) Self {
_ = options;
return Self{ .s = [_]u8{0} ** 200, .offset = 0 };
return Self{};
}
 
pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {
var d = Self.init(options);
d.update(b);
d.final(out);
/// Hash a slice of bytes.
pub fn hash(bytes: []const u8, out: *[digest_length]u8, options: Options) void {
var st = Self.init(options);
st.update(bytes);
st.final(out);
}
 
pub fn update(d: *Self, b: []const u8) void {
var ip: usize = 0;
var len = b.len;
var rate = block_length - d.offset;
var offset = d.offset;
 
// absorb
while (len >= rate) {
for (d.s[offset .. offset + rate], 0..) |*r, i|
r.* ^= b[ip..][i];
 
keccakF(1600, &d.s);
 
ip += rate;
len -= rate;
rate = block_length;
offset = 0;
}
 
for (d.s[offset .. offset + len], 0..) |*r, i|
r.* ^= b[ip..][i];
 
d.offset = offset + len;
/// Absorb a slice of bytes into the state.
pub fn update(self: *Self, bytes: []const u8) void {
self.st.absorb(bytes);
}
 
pub fn final(d: *Self, out: *[digest_length]u8) void {
// padding
d.s[d.offset] ^= delim;
d.s[block_length - 1] ^= 0x80;
 
keccakF(1600, &d.s);
 
// squeeze
var op: usize = 0;
var len: usize = bits / 8;
 
while (len >= block_length) {
mem.copy(u8, out[op..], d.s[0..block_length]);
keccakF(1600, &d.s);
op += block_length;
len -= block_length;
}
 
mem.copy(u8, out[op..], d.s[0..len]);
/// Return the hash of the absorbed bytes.
pub fn final(self: *Self, out: *[digest_length]u8) void {
self.st.pad();
self.st.squeeze(out[0..]);
}
 
pub const Error = error{};
@@ -95,87 +74,101 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type {
};
}
 
const RC = [_]u64{
0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000,
0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003,
0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
};
/// The SHAKE extendable output hash function.
pub fn Shake(comptime security_level: u11) type {
const f = 1600;
const rounds = 24;
const State = KeccakState(f, security_level * 2, 0x1f, rounds);
 
const ROTC = [_]usize{
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
};
return struct {
const Self = @This();
 
const PIL = [_]usize{
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
};
st: State = .{},
buf: [State.rate]u8 = undefined,
offset: usize = 0,
padded: bool = false,
 
const M5 = [_]usize{
0, 1, 2, 3, 4, 0, 1, 2, 3, 4,
};
/// The recommended output length, in bytes.
pub const digest_length = security_level / 2;
/// The block length, or rate, in bytes.
pub const block_length = State.rate;
/// Keccak does not have any options.
pub const Options = struct {};
 
fn keccakF(comptime F: usize, d: *[F / 8]u8) void {
const B = F / 25;
const no_rounds = comptime x: {
break :x 12 + 2 * math.log2(B);
/// Initialize a SHAKE extensible hash function.
pub fn init(options: Options) Self {
_ = options;
return Self{};
}
 
/// Hash a slice of bytes.
/// `out` can be any length.
pub fn hash(bytes: []const u8, out: []u8, options: Options) void {
var st = Self.init(options);
st.update(bytes);
st.squeeze(out);
}
 
/// Absorb a slice of bytes into the state.
pub fn update(self: *Self, bytes: []const u8) void {
self.st.absorb(bytes);
}
 
/// Squeeze a slice of bytes from the state.
/// `out` can be any length, and the function can be called multiple times.
pub fn squeeze(self: *Self, out_: []u8) void {
if (!self.padded) {
self.st.pad();
self.padded = true;
}
var out = out_;
if (self.offset > 0) {
const left = self.buf.len - self.offset;
if (left > 0) {
const n = math.min(left, out.len);
mem.copy(u8, out[0..n], self.buf[self.offset..][0..n]);
out = out[n..];
self.offset += n;
if (out.len == 0) {
return;
}
}
}
const full_blocks = out[0 .. out.len - out.len % State.rate];
if (full_blocks.len > 0) {
self.st.squeeze(full_blocks);
out = out[full_blocks.len..];
}
if (out.len > 0) {
self.st.squeeze(self.buf[0..]);
mem.copy(u8, out[0..], self.buf[0..out.len]);
self.offset = out.len;
}
}
 
/// Return the hash of the absorbed bytes.
/// `out` can be of any length, but the function must not be called multiple times (use `squeeze` for that purpose instead).
pub fn final(self: *Self, out: []u8) void {
self.squeeze(out);
self.st.st.clear(0, State.rate);
}
 
pub const Error = error{};
pub const Writer = std.io.Writer(*Self, Error, write);
 
fn write(self: *Self, bytes: []const u8) Error!usize {
self.update(bytes);
return bytes.len;
}
 
pub fn writer(self: *Self) Writer {
return .{ .context = self };
}
};
 
var s = [_]u64{0} ** 25;
var t = [_]u64{0} ** 1;
var c = [_]u64{0} ** 5;
 
for (&s, 0..) |*r, i| {
r.* = mem.readIntLittle(u64, d[8 * i ..][0..8]);
}
 
for (RC[0..no_rounds]) |round| {
// theta
comptime var x: usize = 0;
inline while (x < 5) : (x += 1) {
c[x] = s[x] ^ s[x + 5] ^ s[x + 10] ^ s[x + 15] ^ s[x + 20];
}
x = 0;
inline while (x < 5) : (x += 1) {
t[0] = c[M5[x + 4]] ^ math.rotl(u64, c[M5[x + 1]], @as(usize, 1));
comptime var y: usize = 0;
inline while (y < 5) : (y += 1) {
s[x + y * 5] ^= t[0];
}
}
 
// rho+pi
t[0] = s[1];
x = 0;
inline while (x < 24) : (x += 1) {
c[0] = s[PIL[x]];
s[PIL[x]] = math.rotl(u64, t[0], ROTC[x]);
t[0] = c[0];
}
 
// chi
comptime var y: usize = 0;
inline while (y < 5) : (y += 1) {
x = 0;
inline while (x < 5) : (x += 1) {
c[x] = s[x + y * 5];
}
x = 0;
inline while (x < 5) : (x += 1) {
s[x + y * 5] = c[x] ^ (~c[M5[x + 1]] & c[M5[x + 2]]);
}
}
 
// iota
s[0] ^= round;
}
 
for (s, 0..) |r, i| {
mem.writeIntLittle(u64, d[8 * i ..][0..8], r);
}
}
 
const htest = @import("test.zig");
 
test "sha3-224 single" {
try htest.assertEqualHash(Sha3_224, "6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", "");
try htest.assertEqualHash(Sha3_224, "e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc");
@@ -309,13 +302,49 @@ test "sha3-512 aligned final" {
}
 
test "keccak-256 single" {
try htest.assertEqualHash(Keccak_256, "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "");
try htest.assertEqualHash(Keccak_256, "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc");
try htest.assertEqualHash(Keccak_256, "f519747ed599024f3882238e5ab43960132572b7345fbeb9a90769dafd21ad67", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
try htest.assertEqualHash(Keccak256, "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "");
try htest.assertEqualHash(Keccak256, "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc");
try htest.assertEqualHash(Keccak256, "f519747ed599024f3882238e5ab43960132572b7345fbeb9a90769dafd21ad67", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
}
 
test "keccak-512 single" {
try htest.assertEqualHash(Keccak_512, "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", "");
try htest.assertEqualHash(Keccak_512, "18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc");
try htest.assertEqualHash(Keccak_512, "ac2fb35251825d3aa48468a9948c0a91b8256f6d97d8fa4160faff2dd9dfcc24f3f1db7a983dad13d53439ccac0b37e24037e7b95f80f59f37a2f683c4ba4682", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
try htest.assertEqualHash(Keccak512, "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", "");
try htest.assertEqualHash(Keccak512, "18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc");
try htest.assertEqualHash(Keccak512, "ac2fb35251825d3aa48468a9948c0a91b8256f6d97d8fa4160faff2dd9dfcc24f3f1db7a983dad13d53439ccac0b37e24037e7b95f80f59f37a2f683c4ba4682", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
}
 
test "SHAKE-128 single" {
var out: [10]u8 = undefined;
Shake128.hash("hello123", &out, .{});
try htest.assertEqual("1b85861510bc4d8e467d", &out);
}
 
test "SHAKE-128 multisqueeze" {
var out: [10]u8 = undefined;
var h = Shake128.init(.{});
h.update("hello123");
h.squeeze(out[0..4]);
h.squeeze(out[4..]);
try htest.assertEqual("1b85861510bc4d8e467d", &out);
}
 
test "SHAKE-128 multisqueeze with multiple blocks" {
var out: [100]u8 = undefined;
var out2: [100]u8 = undefined;
 
var h = Shake128.init(.{});
h.update("hello123");
h.squeeze(out[0..50]);
h.squeeze(out[50..]);
 
var h2 = Shake128.init(.{});
h2.update("hello123");
h2.squeeze(&out2);
try std.testing.expectEqualSlices(u8, &out, &out2);
}
 
test "SHAKE-256 single" {
var out: [10]u8 = undefined;
Shake256.hash("hello123", &out, .{});
try htest.assertEqual("ade612ba265f92de4a37", &out);
}
 
lib/std/fs.zig added: 1877, removed: 639, total 1238
@@ -11,6 +11,11 @@ const math = std.math;
 
const is_darwin = builtin.os.tag.isDarwin();
 
pub const has_executable_bit = switch (builtin.os.tag) {
.windows, .wasi => false,
else => true,
};
 
pub const path = @import("fs/path.zig");
pub const File = @import("fs/file.zig").File;
pub const wasi = @import("fs/wasi.zig");
 
lib/std/fs/file.zig added: 1877, removed: 639, total 1238
@@ -48,6 +48,12 @@ pub const File = struct {
Unknown,
};
 
/// This is the default mode given to POSIX operating systems for creating
/// files. `0o666` is "-rw-rw-rw-" which is counter-intuitive at first,
/// since most people would expect "-rw-r--r--", for example, when using
/// the `touch` command, which would correspond to `0o644`. However, POSIX
/// libc implementations use `0o666` inside `fopen` and then rely on the
/// process-scoped "umask" setting to adjust this number for file creation.
pub const default_mode = switch (builtin.os.tag) {
.windows => 0,
.wasi => 0,
 
lib/std/hash_map.zig added: 1877, removed: 639, total 1238
@@ -1595,16 +1595,17 @@ pub fn HashMapUnmanaged(
self.available = 0;
}
 
/// This function is used in tools/zig-gdb.py to fetch the header type to facilitate
/// fancy debug printing for this type.
fn gdbHelper(self: *Self, hdr: *Header) void {
/// This function is used in the debugger pretty formatters in tools/ to fetch the
/// header type to facilitate fancy debug printing for this type.
fn dbHelper(self: *Self, hdr: *Header, entry: *Entry) void {
_ = self;
_ = hdr;
_ = entry;
}
 
comptime {
if (builtin.mode == .Debug) {
_ = gdbHelper;
_ = dbHelper;
}
}
};
 
lib/std/heap/general_purpose_allocator.zig added: 1877, removed: 639, total 1238
@@ -423,6 +423,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
}
} else struct {};
 
/// Returns true if there were leaks; false otherwise.
pub fn deinit(self: *Self) bool {
const leaks = if (config.safety) self.detectLeaks() else false;
if (config.retain_metadata) {
 
lib/std/io.zig added: 1877, removed: 639, total 1238
@@ -168,6 +168,256 @@ test "null_writer" {
null_writer.writeAll("yay" ** 10) catch |err| switch (err) {};
}
 
pub fn poll(
allocator: std.mem.Allocator,
comptime StreamEnum: type,
files: PollFiles(StreamEnum),
) Poller(StreamEnum) {
const enum_fields = @typeInfo(StreamEnum).Enum.fields;
var result: Poller(StreamEnum) = undefined;
 
if (builtin.os.tag == .windows) result.windows = .{
.first_read_done = false,
.overlapped = [1]os.windows.OVERLAPPED{
mem.zeroes(os.windows.OVERLAPPED),
} ** enum_fields.len,
.active = .{
.count = 0,
.handles_buf = undefined,
.stream_map = undefined,
},
};
 
inline for (0..enum_fields.len) |i| {
result.fifos[i] = .{
.allocator = allocator,
.buf = &.{},
.head = 0,
.count = 0,
};
if (builtin.os.tag == .windows) {
result.windows.active.handles_buf[i] = @field(files, enum_fields[i].name).handle;
} else {
result.poll_fds[i] = .{
.fd = @field(files, enum_fields[i].name).handle,
.events = os.POLL.IN,
.revents = undefined,
};
}
}
return result;
}
 
pub const PollFifo = std.fifo.LinearFifo(u8, .Dynamic);
 
pub fn Poller(comptime StreamEnum: type) type {
return struct {
const enum_fields = @typeInfo(StreamEnum).Enum.fields;
const PollFd = if (builtin.os.tag == .windows) void else std.os.pollfd;
 
fifos: [enum_fields.len]PollFifo,
poll_fds: [enum_fields.len]PollFd,
windows: if (builtin.os.tag == .windows) struct {
first_read_done: bool,
overlapped: [enum_fields.len]os.windows.OVERLAPPED,
active: struct {
count: math.IntFittingRange(0, enum_fields.len),
handles_buf: [enum_fields.len]os.windows.HANDLE,
stream_map: [enum_fields.len]StreamEnum,
 
pub fn removeAt(self: *@This(), index: u32) void {
std.debug.assert(index < self.count);
for (index + 1..self.count) |i| {
self.handles_buf[i - 1] = self.handles_buf[i];
self.stream_map[i - 1] = self.stream_map[i];
}
self.count -= 1;
}
},
} else void,
 
const Self = @This();
 
pub fn deinit(self: *Self) void {
if (builtin.os.tag == .windows) {
// cancel any pending IO to prevent clobbering OVERLAPPED value
for (self.windows.active.handles_buf[0..self.windows.active.count]) |h| {
_ = os.windows.kernel32.CancelIo(h);
}
}
inline for (&self.fifos) |*q| q.deinit();
self.* = undefined;
}
 
pub fn poll(self: *Self) !bool {
if (builtin.os.tag == .windows) {
return pollWindows(self);
} else {
return pollPosix(self);
}
}
 
pub inline fn fifo(self: *Self, comptime which: StreamEnum) *PollFifo {
return &self.fifos[@enumToInt(which)];
}
 
fn pollWindows(self: *Self) !bool {
const bump_amt = 512;
 
if (!self.windows.first_read_done) {
// Windows Async IO requires an initial call to ReadFile before waiting on the handle
for (0..enum_fields.len) |i| {
const handle = self.windows.active.handles_buf[i];
switch (try windowsAsyncRead(
handle,
&self.windows.overlapped[i],
&self.fifos[i],
bump_amt,
)) {
.pending => {
self.windows.active.handles_buf[self.windows.active.count] = handle;
self.windows.active.stream_map[self.windows.active.count] = @intToEnum(StreamEnum, i);
self.windows.active.count += 1;
},
.closed => {}, // don't add to the wait_objects list
}
}
self.windows.first_read_done = true;
}
 
while (true) {
if (self.windows.active.count == 0) return false;
 
const status = os.windows.kernel32.WaitForMultipleObjects(
self.windows.active.count,
&self.windows.active.handles_buf,
0,
os.windows.INFINITE,
);
if (status == os.windows.WAIT_FAILED)
return os.windows.unexpectedError(os.windows.kernel32.GetLastError());
 
if (status < os.windows.WAIT_OBJECT_0 or status > os.windows.WAIT_OBJECT_0 + enum_fields.len - 1)
unreachable;
 
const active_idx = status - os.windows.WAIT_OBJECT_0;
 
const handle = self.windows.active.handles_buf[active_idx];
const stream_idx = @enumToInt(self.windows.active.stream_map[active_idx]);
var read_bytes: u32 = undefined;
if (0 == os.windows.kernel32.GetOverlappedResult(
handle,
&self.windows.overlapped[stream_idx],
&read_bytes,
0,
)) switch (os.windows.kernel32.GetLastError()) {
.BROKEN_PIPE => {
self.windows.active.removeAt(active_idx);
continue;
},
else => |err| return os.windows.unexpectedError(err),
};
 
self.fifos[stream_idx].update(read_bytes);
 
switch (try windowsAsyncRead(
handle,
&self.windows.overlapped[stream_idx],
&self.fifos[stream_idx],
bump_amt,
)) {
.pending => {},
.closed => self.windows.active.removeAt(active_idx),
}
return true;
}
}
 
fn pollPosix(self: *Self) !bool {
// We ask for ensureUnusedCapacity with this much extra space. This
// has more of an effect on small reads because once the reads
// start to get larger the amount of space an ArrayList will
// allocate grows exponentially.
const bump_amt = 512;
 
const err_mask = os.POLL.ERR | os.POLL.NVAL | os.POLL.HUP;
 
const events_len = try os.poll(&self.poll_fds, std.math.maxInt(i32));
if (events_len == 0) {
for (self.poll_fds) |poll_fd| {
if (poll_fd.fd != -1) return true;
} else return false;
}
 
var keep_polling = false;
inline for (&self.poll_fds, &self.fifos) |*poll_fd, *q| {
// Try reading whatever is available before checking the error
// conditions.
// It's still possible to read after a POLL.HUP is received,
// always check if there's some data waiting to be read first.
if (poll_fd.revents & os.POLL.IN != 0) {
const buf = try q.writableWithSize(bump_amt);
const amt = try os.read(poll_fd.fd, buf);
q.update(amt);
if (amt == 0) {
// Remove the fd when the EOF condition is met.
poll_fd.fd = -1;
} else {
keep_polling = true;
}
} else if (poll_fd.revents & err_mask != 0) {
// Exclude the fds that signaled an error.
poll_fd.fd = -1;
} else if (poll_fd.fd != -1) {
keep_polling = true;
}
}
return keep_polling;
}
};
}
 
fn windowsAsyncRead(
handle: os.windows.HANDLE,
overlapped: *os.windows.OVERLAPPED,
fifo: *PollFifo,
bump_amt: usize,
) !enum { pending, closed } {
while (true) {
const buf = try fifo.writableWithSize(bump_amt);
var read_bytes: u32 = undefined;
const read_result = os.windows.kernel32.ReadFile(handle, buf.ptr, math.cast(u32, buf.len) orelse math.maxInt(u32), &read_bytes, overlapped);
if (read_result == 0) return switch (os.windows.kernel32.GetLastError()) {
.IO_PENDING => .pending,
.BROKEN_PIPE => .closed,
else => |err| os.windows.unexpectedError(err),
};
fifo.update(read_bytes);
}
}
 
/// Given an enum, returns a struct with fields of that enum, each field
/// representing an I/O stream for polling.
pub fn PollFiles(comptime StreamEnum: type) type {
const enum_fields = @typeInfo(StreamEnum).Enum.fields;
var struct_fields: [enum_fields.len]std.builtin.Type.StructField = undefined;
for (&struct_fields, enum_fields) |*struct_field, enum_field| {
struct_field.* = .{
.name = enum_field.name,
.type = fs.File,
.default_value = null,
.is_comptime = false,
.alignment = @alignOf(fs.File),
};
}
return @Type(.{ .Struct = .{
.layout = .Auto,
.fields = &struct_fields,
.decls = &.{},
.is_tuple = false,
} });
}
 
test {
_ = @import("io/bit_reader.zig");
_ = @import("io/bit_writer.zig");
 
lib/std/multi_array_list.zig added: 1877, removed: 639, total 1238
@@ -131,8 +131,8 @@ pub fn MultiArrayList(comptime S: type) type {
.capacity = self.capacity,
};
var ptr: [*]u8 = self.bytes;
for (sizes.bytes, 0..) |field_size, i| {
result.ptrs[sizes.fields[i]] = ptr;
for (sizes.bytes, sizes.fields) |field_size, i| {
result.ptrs[i] = ptr;
ptr += field_size * self.capacity;
}
return result;
@@ -446,16 +446,33 @@ pub fn MultiArrayList(comptime S: type) type {
return meta.fieldInfo(S, field).type;
}
 
/// This function is used in tools/zig-gdb.py to fetch the child type to facilitate
/// fancy debug printing for this type.
fn gdbHelper(self: *Self, child: *S) void {
const Entry = entry: {
var entry_fields: [fields.len]std.builtin.Type.StructField = undefined;
for (&entry_fields, sizes.fields) |*entry_field, i| entry_field.* = .{
.name = fields[i].name ++ "_ptr",
.type = *fields[i].type,
.default_value = null,
.is_comptime = fields[i].is_comptime,
.alignment = fields[i].alignment,
};
break :entry @Type(.{ .Struct = .{
.layout = .Extern,
.fields = &entry_fields,
.decls = &.{},
.is_tuple = false,
} });
};
/// This function is used in the debugger pretty formatters in tools/ to fetch the
/// child type to facilitate fancy debug printing for this type.
fn dbHelper(self: *Self, child: *S, entry: *Entry) void {
_ = self;
_ = child;
_ = entry;
}
 
comptime {
if (builtin.mode == .Debug) {
_ = gdbHelper;
_ = dbHelper;
}
}
};
 
lib/std/os.zig added: 1877, removed: 639, total 1238
@@ -302,8 +302,7 @@ pub const FChmodError = error{
/// successfully, or must have the effective user ID matching the owner
/// of the file.
pub fn fchmod(fd: fd_t, mode: mode_t) FChmodError!void {
if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
@compileError("Unsupported OS");
if (!std.fs.has_executable_bit) @compileError("fchmod unsupported by target OS");
 
while (true) {
const res = system.fchmod(fd, mode);
@@ -311,8 +310,38 @@ pub fn fchmod(fd: fd_t, mode: mode_t) FChmodError!void {
switch (system.getErrno(res)) {
.SUCCESS => return,
.INTR => continue,
.BADF => unreachable, // Can be reached if the fd refers to a non-iterable directory.
.BADF => unreachable,
.FAULT => unreachable,
.INVAL => unreachable,
.ACCES => return error.AccessDenied,
.IO => return error.InputOutput,
.LOOP => return error.SymLinkLoop,
.NOENT => return error.FileNotFound,
.NOMEM => return error.SystemResources,
.NOTDIR => return error.FileNotFound,
.PERM => return error.AccessDenied,
.ROFS => return error.ReadOnlyFileSystem,
else => |err| return unexpectedErrno(err),
}
}
}
 
const FChmodAtError = FChmodError || error{
NameTooLong,
};
 
pub fn fchmodat(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void {
if (!std.fs.has_executable_bit) @compileError("fchmodat unsupported by target OS");
 
const path_c = try toPosixPath(path);
 
while (true) {
const res = system.fchmodat(dirfd, &path_c, mode, flags);
 
switch (system.getErrno(res)) {
.SUCCESS => return,
.INTR => continue,
.BADF => unreachable,
.FAULT => unreachable,
.INVAL => unreachable,
.ACCES => return error.AccessDenied,
 
lib/std/os/linux.zig added: 1877, removed: 639, total 1238
@@ -769,6 +769,20 @@ pub fn fchmod(fd: i32, mode: mode_t) usize {
return syscall2(.fchmod, @bitCast(usize, @as(isize, fd)), mode);
}
 
pub fn chmod(path: [*:0]const u8, mode: mode_t) usize {
if (@hasField(SYS, "chmod")) {
return syscall2(.chmod, @ptrToInt(path), mode);
} else {
return syscall4(
.fchmodat,
@bitCast(usize, @as(isize, AT.FDCWD)),
@ptrToInt(path),
mode,
0,
);
}
}
 
pub fn fchown(fd: i32, owner: uid_t, group: gid_t) usize {
if (@hasField(SYS, "fchown32")) {
return syscall3(.fchown32, @bitCast(usize, @as(isize, fd)), owner, group);
@@ -777,6 +791,10 @@ pub fn fchown(fd: i32, owner: uid_t, group: gid_t) usize {
}
}
 
pub fn fchmodat(fd: i32, path: [*:0]const u8, mode: mode_t, flags: u32) usize {
return syscall4(.fchmodat, @bitCast(usize, @as(isize, fd)), @ptrToInt(path), mode, flags);
}
 
/// Can only be called on 32 bit systems. For 64 bit see `lseek`.
pub fn llseek(fd: i32, offset: u64, result: ?*u64, whence: usize) usize {
// NOTE: The offset parameter splitting is independent from the target
 
lib/std/os/test.zig added: 1877, removed: 639, total 1238
@@ -531,17 +531,17 @@ test "memfd_create" {
else => return error.SkipZigTest,
}
 
const fd = std.os.memfd_create("test", 0) catch |err| switch (err) {
const fd = os.memfd_create("test", 0) catch |err| switch (err) {
// Related: https://github.com/ziglang/zig/issues/4019
error.SystemOutdated => return error.SkipZigTest,
else => |e| return e,
};
defer std.os.close(fd);
try expect((try std.os.write(fd, "test")) == 4);
try std.os.lseek_SET(fd, 0);
defer os.close(fd);
try expect((try os.write(fd, "test")) == 4);
try os.lseek_SET(fd, 0);
 
var buf: [10]u8 = undefined;
const bytes_read = try std.os.read(fd, &buf);
const bytes_read = try os.read(fd, &buf);
try expect(bytes_read == 4);
try expect(mem.eql(u8, buf[0..4], "test"));
}
@@ -688,7 +688,7 @@ test "signalfd" {
.linux, .solaris => {},
else => return error.SkipZigTest,
}
_ = std.os.signalfd;
_ = os.signalfd;
}
 
test "sync" {
@@ -757,11 +757,11 @@ test "shutdown socket" {
if (native_os == .wasi)
return error.SkipZigTest;
if (native_os == .windows) {
_ = try std.os.windows.WSAStartup(2, 2);
_ = try os.windows.WSAStartup(2, 2);
}
defer {
if (native_os == .windows) {
std.os.windows.WSACleanup() catch unreachable;
os.windows.WSACleanup() catch unreachable;
}
}
const sock = try os.socket(os.AF.INET, os.SOCK.STREAM, 0);
@@ -855,13 +855,13 @@ test "dup & dup2" {
var file = try tmp.dir.createFile("os_dup_test", .{});
defer file.close();
 
var duped = std.fs.File{ .handle = try std.os.dup(file.handle) };
var duped = std.fs.File{ .handle = try os.dup(file.handle) };
defer duped.close();
try duped.writeAll("dup");
 
// Tests aren't run in parallel so using the next fd shouldn't be an issue.
const new_fd = duped.handle + 1;
try std.os.dup2(file.handle, new_fd);
try os.dup2(file.handle, new_fd);
var dup2ed = std.fs.File{ .handle = new_fd };
defer dup2ed.close();
try dup2ed.writeAll("dup2");
@@ -909,46 +909,46 @@ test "POSIX file locking with fcntl" {
const fd = file.handle;
 
// Place an exclusive lock on the first byte, and a shared lock on the second byte:
var struct_flock = std.mem.zeroInit(std.os.Flock, .{ .type = std.os.F.WRLCK });
_ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock));
var struct_flock = std.mem.zeroInit(os.Flock, .{ .type = os.F.WRLCK });
_ = try os.fcntl(fd, os.F.SETLK, @ptrToInt(&struct_flock));
struct_flock.start = 1;
struct_flock.type = std.os.F.RDLCK;
_ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock));
struct_flock.type = os.F.RDLCK;
_ = try os.fcntl(fd, os.F.SETLK, @ptrToInt(&struct_flock));
 
// Check the locks in a child process:
const pid = try std.os.fork();
const pid = try os.fork();
if (pid == 0) {
// child expects be denied the exclusive lock:
struct_flock.start = 0;
struct_flock.type = std.os.F.WRLCK;
try expectError(error.Locked, std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock)));
struct_flock.type = os.F.WRLCK;
try expectError(error.Locked, os.fcntl(fd, os.F.SETLK, @ptrToInt(&struct_flock)));
// child expects to get the shared lock:
struct_flock.start = 1;
struct_flock.type = std.os.F.RDLCK;
_ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock));
struct_flock.type = os.F.RDLCK;
_ = try os.fcntl(fd, os.F.SETLK, @ptrToInt(&struct_flock));
// child waits for the exclusive lock in order to test deadlock:
struct_flock.start = 0;
struct_flock.type = std.os.F.WRLCK;
_ = try std.os.fcntl(fd, std.os.F.SETLKW, @ptrToInt(&struct_flock));
struct_flock.type = os.F.WRLCK;
_ = try os.fcntl(fd, os.F.SETLKW, @ptrToInt(&struct_flock));
// child exits without continuing:
std.os.exit(0);
os.exit(0);
} else {
// parent waits for child to get shared lock:
std.time.sleep(1 * std.time.ns_per_ms);
// parent expects deadlock when attempting to upgrade the shared lock to exclusive:
struct_flock.start = 1;
struct_flock.type = std.os.F.WRLCK;
try expectError(error.DeadLock, std.os.fcntl(fd, std.os.F.SETLKW, @ptrToInt(&struct_flock)));
struct_flock.type = os.F.WRLCK;
try expectError(error.DeadLock, os.fcntl(fd, os.F.SETLKW, @ptrToInt(&struct_flock)));
// parent releases exclusive lock:
struct_flock.start = 0;
struct_flock.type = std.os.F.UNLCK;
_ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock));
struct_flock.type = os.F.UNLCK;
_ = try os.fcntl(fd, os.F.SETLK, @ptrToInt(&struct_flock));
// parent releases shared lock:
struct_flock.start = 1;
struct_flock.type = std.os.F.UNLCK;
_ = try std.os.fcntl(fd, std.os.F.SETLK, @ptrToInt(&struct_flock));
struct_flock.type = os.F.UNLCK;
_ = try os.fcntl(fd, os.F.SETLK, @ptrToInt(&struct_flock));
// parent waits for child:
const result = std.os.waitpid(pid, 0);
const result = os.waitpid(pid, 0);
try expect(result.status == 0 * 256);
}
}
@@ -1182,3 +1182,17 @@ test "pwrite with empty buffer" {
 
_ = try os.pwrite(file.handle, bytes, 0);
}
 
test "fchmodat smoke test" {
if (!std.fs.has_executable_bit) return error.SkipZigTest;
 
var tmp = tmpDir(.{});
defer tmp.cleanup();
 
try expectError(error.FileNotFound, os.fchmodat(tmp.dir.fd, "foo.txt", 0o666, 0));
const fd = try os.openat(tmp.dir.fd, "foo.txt", os.O.RDWR | os.O.CREAT | os.O.EXCL, 0o666);
os.close(fd);
try os.fchmodat(tmp.dir.fd, "foo.txt", 0o755, 0);
const st = try os.fstatat(tmp.dir.fd, "foo.txt", 0);
try expectEqual(@as(os.mode_t, 0o755), st.mode & 0b111_111_111);
}
 
lib/std/os/windows.zig added: 1877, removed: 639, total 1238
@@ -2068,7 +2068,7 @@ pub fn loadWinsockExtensionFunction(comptime T: type, sock: ws2_32.SOCKET, guid:
ws2_32.SIO_GET_EXTENSION_FUNCTION_POINTER,
@ptrCast(*const anyopaque, &guid),
@sizeOf(GUID),
@intToPtr(?*anyopaque, @ptrToInt(function)),
@intToPtr(?*anyopaque, @ptrToInt(&function)),
@sizeOf(T),
&num_bytes,
null,
 
lib/std/os/windows/test.zig added: 1877, removed: 639, total 1238
@@ -63,3 +63,28 @@ test "removeDotDirs" {
try testRemoveDotDirs("a\\b\\..\\", "a\\");
try testRemoveDotDirs("a\\b\\..\\c", "a\\c");
}
 
test "loadWinsockExtensionFunction" {
_ = try windows.WSAStartup(2, 2);
defer windows.WSACleanup() catch unreachable;
 
const LPFN_CONNECTEX = *const fn (
Socket: windows.ws2_32.SOCKET,
SockAddr: *const windows.ws2_32.sockaddr,
SockLen: std.os.socklen_t,
SendBuf: ?*const anyopaque,
SendBufLen: windows.DWORD,
BytesSent: *windows.DWORD,
Overlapped: *windows.OVERLAPPED,
) callconv(windows.WINAPI) windows.BOOL;
 
_ = windows.loadWinsockExtensionFunction(
LPFN_CONNECTEX,
try std.os.socket(std.os.AF.INET, std.os.SOCK.DGRAM, 0),
windows.ws2_32.WSAID_CONNECTEX,
) catch |err| switch (err) {
error.OperationNotSupported => unreachable,
error.ShortRead => unreachable,
else => |e| return e,
};
}
 
lib/std/process.zig added: 1877, removed: 639, total 1238
@@ -9,6 +9,7 @@ const assert = std.debug.assert;
const testing = std.testing;
const child_process = @import("child_process.zig");
 
pub const Child = child_process.ChildProcess;
pub const abort = os.abort;
pub const exit = os.exit;
pub const changeCurDir = os.chdir;
 
lib/std/std.zig added: 1877, removed: 639, total 1238
@@ -12,6 +12,7 @@ pub const BoundedArray = @import("bounded_array.zig").BoundedArray;
pub const Build = @import("Build.zig");
pub const BufMap = @import("buf_map.zig").BufMap;
pub const BufSet = @import("buf_set.zig").BufSet;
/// Deprecated: use `process.Child`.
pub const ChildProcess = @import("child_process.zig").ChildProcess;
pub const ComptimeStringMap = @import("comptime_string_map.zig").ComptimeStringMap;
pub const DynLib = @import("dynamic_library.zig").DynLib;
 
src/AstGen.zig added: 1877, removed: 639, total 1238
@@ -2342,10 +2342,10 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
 
.while_simple,
.while_cont,
.@"while", => _ = try whileExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.fullWhile(inner_node).?, true),
.@"while", => _ = try whileExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullWhile(inner_node).?, true),
 
.for_simple,
.@"for", => _ = try forExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.fullFor(inner_node).?, true),
.@"for", => _ = try forExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullFor(inner_node).?, true),
 
else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node),
// zig fmt: on
 
src/Package.zig added: 1877, removed: 639, total 1238
@@ -225,6 +225,7 @@ pub fn fetchAndAddDependencies(
build_roots_source: *std.ArrayList(u8),
name_prefix: []const u8,
color: main.Color,
all_modules: *AllModules,
) !void {
const max_bytes = 10 * 1024 * 1024;
const gpa = thread_pool.allocator;
@@ -291,6 +292,7 @@ pub fn fetchAndAddDependencies(
report,
build_roots_source,
fqn,
all_modules,
);
 
try pkg.fetchAndAddDependencies(
@@ -304,6 +306,7 @@ pub fn fetchAndAddDependencies(
build_roots_source,
sub_prefix,
color,
all_modules,
);
 
try add(pkg, gpa, fqn, sub_pkg);
@@ -402,6 +405,11 @@ const Report = struct {
}
};
 
const hex_multihash_len = 2 * Manifest.multihash_len;
const MultiHashHexDigest = [hex_multihash_len]u8;
/// This is to avoid creating multiple modules for the same build.zig file.
pub const AllModules = std.AutoHashMapUnmanaged(MultiHashHexDigest, *Package);
 
fn fetchAndUnpack(
thread_pool: *ThreadPool,
http_client: *std.http.Client,
@@ -410,6 +418,7 @@ fn fetchAndUnpack(
report: Report,
build_roots_source: *std.ArrayList(u8),
fqn: []const u8,
all_modules: *AllModules,
) !*Package {
const gpa = http_client.allocator;
const s = fs.path.sep_str;
@@ -417,9 +426,24 @@ fn fetchAndUnpack(
// Check if the expected_hash is already present in the global package
// cache, and thereby avoid both fetching and unpacking.
if (dep.hash) |h| cached: {
const hex_multihash_len = 2 * Manifest.multihash_len;
const hex_digest = h[0..hex_multihash_len];
const pkg_dir_sub_path = "p" ++ s ++ hex_digest;
 
const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
errdefer gpa.free(build_root);
 
try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{
std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root),
});
 
// The compiler has a rule that a file must not be included in multiple modules,
// so we must detect if a module has been created for this package and reuse it.
const gop = try all_modules.getOrPut(gpa, hex_digest.*);
if (gop.found_existing) {
gpa.free(build_root);
return gop.value_ptr.*;
}
 
var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) {
error.FileNotFound => break :cached,
else => |e| return e,
@@ -432,13 +456,6 @@ fn fetchAndUnpack(
const owned_src_path = try gpa.dupe(u8, build_zig_basename);
errdefer gpa.free(owned_src_path);
 
const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
errdefer gpa.free(build_root);
 
try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{
std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root),
});
 
ptr.* = .{
.root_src_directory = .{
.path = build_root,
@@ -448,6 +465,7 @@ fn fetchAndUnpack(
.root_src_path = owned_src_path,
};
 
gop.value_ptr.* = ptr;
return ptr;
}
 
 
src/Sema.zig added: 1877, removed: 639, total 1238
@@ -17328,11 +17328,11 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
break :blk abi_align;
} else 0;
 
const address_space = if (inst_data.flags.has_addrspace) blk: {
const address_space: std.builtin.AddressSpace = if (inst_data.flags.has_addrspace) blk: {
const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]);
extra_i += 1;
break :blk try sema.analyzeAddressSpace(block, addrspace_src, ref, .pointer);
} else .generic;
} else if (elem_ty.zigTypeTag() == .Fn and target.cpu.arch == .avr) .flash else .generic;
 
const bit_offset = if (inst_data.flags.has_bit_range) blk: {
const ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_i]);
 
src/arch/aarch64/CodeGen.zig added: 1877, removed: 639, total 1238
@@ -4177,8 +4177,10 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
}
 
fn airArg(self: *Self, inst: Air.Inst.Index) !void {
const arg_index = self.arg_index;
self.arg_index += 1;
// skip zero-bit arguments as they don't have a corresponding arg instruction
var arg_index = self.arg_index;
while (self.args[arg_index] == .none) arg_index += 1;
self.arg_index = arg_index + 1;
 
const ty = self.air.typeOfIndex(inst);
const tag = self.air.instructions.items(.tag)[inst];
 
src/arch/arm/CodeGen.zig added: 1877, removed: 639, total 1238
@@ -4125,8 +4125,10 @@ fn genInlineMemsetCode(
}
 
fn airArg(self: *Self, inst: Air.Inst.Index) !void {
const arg_index = self.arg_index;
self.arg_index += 1;
// skip zero-bit arguments as they don't have a corresponding arg instruction
var arg_index = self.arg_index;
while (self.args[arg_index] == .none) arg_index += 1;
self.arg_index = arg_index + 1;
 
const ty = self.air.typeOfIndex(inst);
const tag = self.air.instructions.items(.tag)[inst];
 
src/arch/x86_64/CodeGen.zig added: 1877, removed: 639, total 1238
@@ -3827,8 +3827,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
}
 
fn airArg(self: *Self, inst: Air.Inst.Index) !void {
const arg_index = self.arg_index;
self.arg_index += 1;
// skip zero-bit arguments as they don't have a corresponding arg instruction
var arg_index = self.arg_index;
while (self.args[arg_index] == .none) arg_index += 1;
self.arg_index = arg_index + 1;
 
const ty = self.air.typeOfIndex(inst);
const mcv = self.args[arg_index];
 
src/codegen/llvm.zig added: 1877, removed: 639, total 1238
@@ -2219,11 +2219,16 @@ pub const Object = struct {
));
}
 
const union_name = if (layout.tag_size == 0) name.ptr else "AnonUnion";
var union_name_buf: ?[:0]const u8 = null;
defer if (union_name_buf) |buf| gpa.free(buf);
const union_name = if (layout.tag_size == 0) name else name: {
union_name_buf = try std.fmt.allocPrintZ(gpa, "{s}:Payload", .{name});
break :name union_name_buf.?;
};
 
const union_di_ty = dib.createUnionType(
compile_unit_scope,
union_name,
union_name.ptr,
null, // file
0, // line
ty.abiSize(target) * 8, // size in bits
 
src/link/Wasm.zig added: 1877, removed: 639, total 1238
@@ -345,7 +345,17 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
}
 
// TODO: read the file and keep valid parts instead of truncating
const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true });
const file = try options.emit.?.directory.handle.createFile(sub_path, .{
.truncate = true,
.read = true,
.mode = if (fs.has_executable_bit)
if (options.target.os.tag == .wasi and options.output_mode == .Exe)
fs.File.default_mode | 0b001_000_000
else
fs.File.default_mode
else
0,
});
wasm_bin.base.file = file;
wasm_bin.name = sub_path;
 
@@ -3750,10 +3760,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
if (wasm.base.options.import_symbols) {
try argv.append("--allow-undefined");
}
try argv.appendSlice(&[_][]const u8{
"-o",
full_out_path,
});
try argv.appendSlice(&.{ "-o", full_out_path });
 
if (target.cpu.arch == .wasm64) {
try argv.append("-mwasm64");
@@ -3889,6 +3896,21 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
}
}
}
 
// Give +x to the .wasm file if it is an executable and the OS is WASI.
// Some systems may be configured to execute such binaries directly. Even if that
// is not the case, it means we will get "exec format error" when trying to run
// it, and then can react to that in the same way as trying to run an ELF file
// from a foreign CPU architecture.
if (fs.has_executable_bit and target.os.tag == .wasi and
wasm.base.options.output_mode == .Exe)
{
// TODO: what's our strategy for reporting linker errors from this function?
// report a nice error here with the file path if it fails instead of
// just returning the error code.
// chmod does not interact with umask, so we use a conservative -rwxr--r-- here.
try std.os.fchmodat(fs.cwd().fd, full_out_path, 0o744, 0);
}
}
 
if (!wasm.base.options.disable_lld_caching) {
 
src/main.zig added: 1877, removed: 639, total 1238
@@ -4013,6 +4013,7 @@ pub const usage_build =
\\ --cache-dir [path] Override path to local Zig cache directory
\\ --global-cache-dir [path] Override path to global Zig cache directory
\\ --zig-lib-dir [arg] Override path to Zig lib directory
\\ --build-runner [file] Override path to build runner
\\ --prominent-compile-errors Output compile errors formatted for a human to read
\\ -h, --help Print this help and exit
\\
@@ -4031,6 +4032,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR");
var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR");
var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR");
var override_build_runner: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_BUILD_RUNNER");
var child_argv = std.ArrayList([]const u8).init(arena);
var reference_trace: ?u32 = null;
var debug_compile_errors = false;
@@ -4065,6 +4067,11 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
override_lib_dir = args[i];
try child_argv.appendSlice(&[_][]const u8{ arg, args[i] });
continue;
} else if (mem.eql(u8, arg, "--build-runner")) {
if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
i += 1;
override_build_runner = args[i];
continue;
} else if (mem.eql(u8, arg, "--cache-dir")) {
if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg});
i += 1;
@@ -4197,10 +4204,29 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
try thread_pool.init(gpa);
defer thread_pool.deinit();
 
var main_pkg: Package = .{
.root_src_directory = zig_lib_directory,
.root_src_path = "build_runner.zig",
};
var cleanup_build_runner_dir: ?fs.Dir = null;
defer if (cleanup_build_runner_dir) |*dir| dir.close();
 
var main_pkg: Package = if (override_build_runner) |build_runner_path|
.{
.root_src_directory = blk: {
if (std.fs.path.dirname(build_runner_path)) |dirname| {
const dir = fs.cwd().openDir(dirname, .{}) catch |err| {
fatal("unable to open directory to build runner from argument 'build-runner', '{s}': {s}", .{ dirname, @errorName(err) });
};
cleanup_build_runner_dir = dir;
break :blk .{ .path = dirname, .handle = dir };
}
 
break :blk .{ .path = null, .handle = fs.cwd() };
},
.root_src_path = std.fs.path.basename(build_runner_path),
}
else
.{
.root_src_directory = zig_lib_directory,
.root_src_path = "build_runner.zig",
};
 
if (!build_options.omit_pkg_fetching_code) {
var http_client: std.http.Client = .{ .allocator = gpa };
@@ -4218,6 +4244,9 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
var build_roots_source = std.ArrayList(u8).init(gpa);
defer build_roots_source.deinit();
 
var all_modules: Package.AllModules = .{};
defer all_modules.deinit(gpa);
 
// Here we borrow main package's table and will replace it with a fresh
// one after this process completes.
main_pkg.fetchAndAddDependencies(
@@ -4231,6 +4260,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
&build_roots_source,
"",
color,
&all_modules,
) catch |err| switch (err) {
error.PackageFetchFailed => process.exit(1),
else => |e| return e,
 
src/target.zig added: 1877, removed: 639, total 1238
@@ -648,8 +648,9 @@ pub fn defaultAddressSpace(
function,
},
) AddressSpace {
_ = target;
_ = context;
// The default address space for functions on AVR is .flash to produce
// correct fixups into progmem.
if (context == .function and target.cpu.arch == .avr) return .flash;
return .generic;
}
 
 
src/type.zig added: 1877, removed: 639, total 1238
@@ -1,4 +1,5 @@
const std = @import("std");
const builtin = @import("builtin");
const Value = @import("value.zig").Value;
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
@@ -6694,4 +6695,33 @@ pub const Type = extern union {
/// This is only used for comptime asserts. Bump this number when you make a change
/// to packed struct layout to find out all the places in the codebase you need to edit!
pub const packed_struct_layout_version = 2;
 
/// This function is used in the debugger pretty formatters in tools/ to fetch the
/// Tag to Payload mapping to facilitate fancy debug printing for this type.
fn dbHelper(self: *Type, tag_to_payload_map: *map: {
const tags = @typeInfo(Tag).Enum.fields;
var fields: [tags.len]std.builtin.Type.StructField = undefined;
for (&fields, tags) |*field, t| field.* = .{
.name = t.name,
.type = *if (t.value < Tag.no_payload_count) void else @field(Tag, t.name).Type(),
.default_value = null,
.is_comptime = false,
.alignment = 0,
};
break :map @Type(.{ .Struct = .{
.layout = .Extern,
.fields = &fields,
.decls = &.{},
.is_tuple = false,
} });
}) void {
_ = self;
_ = tag_to_payload_map;
}
 
comptime {
if (builtin.mode == .Debug) {
_ = dbHelper;
}
}
};
 
src/value.zig added: 1877, removed: 639, total 1238
@@ -1,4 +1,5 @@
const std = @import("std");
const builtin = @import("builtin");
const Type = @import("type.zig").Type;
const log2 = std.math.log2;
const assert = std.debug.assert;
@@ -5584,6 +5585,35 @@ pub const Value = extern union {
ri.* = @intToEnum(RuntimeIndex, @enumToInt(ri.*) + 1);
}
};
 
/// This function is used in the debugger pretty formatters in tools/ to fetch the
/// Tag to Payload mapping to facilitate fancy debug printing for this type.
fn dbHelper(self: *Value, tag_to_payload_map: *map: {
const tags = @typeInfo(Tag).Enum.fields;
var fields: [tags.len]std.builtin.Type.StructField = undefined;
for (&fields, tags) |*field, t| field.* = .{
.name = t.name,
.type = *if (t.value < Tag.no_payload_count) void else @field(Tag, t.name).Type(),
.default_value = null,
.is_comptime = false,
.alignment = 0,
};
break :map @Type(.{ .Struct = .{
.layout = .Extern,
.fields = &fields,
.decls = &.{},
.is_tuple = false,
} });
}) void {
_ = self;
_ = tag_to_payload_map;
}
 
comptime {
if (builtin.mode == .Debug) {
_ = dbHelper;
}
}
};
 
var negative_one_payload: Value.Payload.I64 = .{
 
filename was Deleted added: 1877, removed: 639, total 1238
@@ -0,0 +1,15 @@
fn returns() usize {
return 2;
}
 
export fn f1() void {
for ("hello") |_| {
break returns();
}
}
 
// error
// backend=stage2
// target=native
//
// :6:5: error: incompatible types: 'usize' and 'void'
 
filename was Deleted added: 1877, removed: 639, total 1238
@@ -0,0 +1,26 @@
fn returns() usize {
return 2;
}
 
export fn f1() void {
var a: bool = true;
while (a) {
break returns();
}
}
 
export fn f2() void {
var x: bool = true;
outer: while (x) {
while (x) {
break :outer returns();
}
}
}
 
// error
// backend=stage2
// target=native
//
// :7:5: error: incompatible types: 'usize' and 'void'
// :14:12: error: incompatible types: 'usize' and 'void'
 
filename was Deleted added: 1877, removed: 639, total 1238
@@ -0,0 +1,575 @@
# pretty printing for the zig language, zig standard library, and zig stage 2 compiler.
# put commands in ~/.lldbinit to run them automatically when starting lldb
# `command script /path/to/stage2_lldb_pretty_printers.py` to import this file
# `type category enable zig` to enable pretty printing for the zig language
# `type category enable zig.std` to enable pretty printing for the zig standard library
# `type category enable zig.stage2` to enable pretty printing for the zig stage 2 compiler
import lldb
import re
 
page_size = 1 << 12
 
def log2_int(i): return i.bit_length() - 1
 
# Define Zig Language
 
zig_keywords = {
'addrspace',
'align',
'allowzero',
'and',
'anyframe',
'anytype',
'asm',
'async',
'await',
'break',
'callconv',
'catch',
'comptime',
'const',
'continue',
'defer',
'else',
'enum',
'errdefer',
'error',
'export',
'extern',
'fn',
'for',
'if',
'inline',
'noalias',
'noinline',
'nosuspend',
'opaque',
'or',
'orelse',
'packed',
'pub',
'resume',
'return',
'linksection',
'struct',
'suspend',
'switch',
'test',
'threadlocal',
'try',
'union',
'unreachable',
'usingnamespace',
'var',
'volatile',
'while',
}
zig_primitives = {
'anyerror',
'anyframe',
'anyopaque',
'bool',
'c_int',
'c_long',
'c_longdouble',
'c_longlong',
'c_short',
'c_uint',
'c_ulong',
'c_ulonglong',
'c_ushort',
'comptime_float',
'comptime_int',
'f128',
'f16',
'f32',
'f64',
'f80',
'false',
'isize',
'noreturn',
'null',
'true',
'type',
'undefined',
'usize',
'void',
}
zig_integer_type = re.compile('[iu][1-9][0-9]+')
zig_identifier_regex = re.compile('[A-Z_a-z][0-9A-Z_a-z]*')
def zig_IsVariableName(string): return string != '_' and string not in zig_keywords and string not in zig_primitives and not zig_integer_type.fullmatch(string) and zig_identifier_regex.fullmatch(string)
def zig_IsFieldName(string): return string not in zig_keywords and zig_identifier_regex.fullmatch(string)
 
class zig_Slice_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.ptr = self.value.GetChildMemberWithName('ptr')
self.len = self.value.GetChildMemberWithName('len').unsigned if self.ptr.unsigned > page_size else 0
self.elem_type = self.ptr.type.GetPointeeType()
self.elem_size = self.elem_type.size
except: pass
def has_children(self): return True
def num_children(self): return self.len or 0
def get_child_index(self, name):
try: return int(name.removeprefix('[').removesuffix(']'))
except: return -1
def get_child_at_index(self, index):
if index < 0 or index >= self.len: return None
try: return self.ptr.CreateChildAtOffset('[%d]' % index, index * self.elem_size, self.elem_type)
except: return None
 
def zig_String_decode(value, offset=0, length=None):
try:
value = value.GetNonSyntheticValue()
data = value.GetChildMemberWithName('ptr').GetPointeeData(offset, length if length is not None else value.GetChildMemberWithName('len').unsigned)
b = bytes(data.uint8)
b = b.replace(b'\\', b'\\\\')
b = b.replace(b'\n', b'\\n')
b = b.replace(b'\r', b'\\r')
b = b.replace(b'\t', b'\\t')
b = b.replace(b'"', b'\\"')
b = b.replace(b'\'', b'\\\'')
s = b.decode(encoding='ascii', errors='backslashreplace')
return s if s.isprintable() else ''.join((c if c.isprintable() else '\\x%02x' % ord(c) for c in s))
except: return None
def zig_String_SummaryProvider(value, _=None): return '"%s"' % zig_String_decode(value)
def zig_String_AsIdentifier(value, pred):
string = zig_String_decode(value)
return string if pred(string) else '@"%s"' % string
 
class zig_Optional_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.child = self.value.GetChildMemberWithName('some').unsigned == 1 and self.value.GetChildMemberWithName('data').Clone('child')
except: pass
def has_children(self): return bool(self.child)
def num_children(self): return int(self.child)
def get_child_index(self, name): return 0 if self.child and (name == 'child' or name == '?') else -1
def get_child_at_index(self, index): return self.child if self.child and index == 0 else None
def zig_Optional_SummaryProvider(value, _=None):
child = value.GetChildMemberWithName('child')
return child or 'null'
 
class zig_ErrorUnion_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.error_set = self.value.GetChildMemberWithName('tag').Clone('error_set')
self.payload = self.value.GetChildMemberWithName('value').Clone('payload') if self.error_set.unsigned == 0 else None
except: pass
def has_children(self): return True
def num_children(self): return 1
def get_child_index(self, name): return 0 if name == ('payload' if self.payload else 'error_set') else -1
def get_child_at_index(self, index): return self.payload or self.error_set if index == 0 else None
 
# Define Zig Standard Library
 
class std_SegmentedList_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.prealloc_segment = self.value.GetChildMemberWithName('prealloc_segment')
self.dynamic_segments = zig_Slice_SynthProvider(self.value.GetChildMemberWithName('dynamic_segments'))
self.dynamic_segments.update()
self.len = self.value.GetChildMemberWithName('len').unsigned
except: pass
def has_children(self): return True
def num_children(self): return self.len
def get_child_index(self, name):
try: return int(name.removeprefix('[').removesuffix(']'))
except: return -1
def get_child_at_index(self, index):
try:
if index < 0 or index >= self.len: return None
prealloc_item_count = len(self.prealloc_segment)
if index < prealloc_item_count: return self.prealloc_segment.child[index]
prealloc_exp = prealloc_item_count.bit_length() - 1
shelf_index = log2_int(index + 1) if prealloc_item_count == 0 else log2_int(index + prealloc_item_count) - prealloc_exp - 1
shelf = self.dynamic_segments.get_child_at_index(shelf_index)
box_index = (index + 1) - (1 << shelf_index) if prealloc_item_count == 0 else index + prealloc_item_count - (1 << ((prealloc_exp + 1) + shelf_index))
elem_type = shelf.type.GetPointeeType()
return shelf.CreateChildAtOffset('[%d]' % index, box_index * elem_type.size, elem_type)
except: return None
 
class std_MultiArrayList_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.len = 0
 
value_type = self.value.type
for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull):
ptr_self_type, ptr_child_type, ptr_entry_type = helper.function.type.GetFunctionArgumentTypes()
if ptr_self_type.GetPointeeType() == value_type: break
else: return
 
self.entry_type = ptr_entry_type.GetPointeeType()
self.bytes = self.value.GetChildMemberWithName('bytes')
self.len = self.value.GetChildMemberWithName('len').unsigned
self.capacity = self.value.GetChildMemberWithName('capacity').unsigned
except: pass
def has_children(self): return True
def num_children(self): return self.len
def get_child_index(self, name):
try: return int(name.removeprefix('[').removesuffix(']'))
except: return -1
def get_child_at_index(self, index):
try:
if index < 0 or index >= self.len: return None
offset = 0
data = lldb.SBData()
for field in self.entry_type.fields:
ptr_field_type = field.type
field_size = ptr_field_type.GetPointeeType().size
data.Append(self.bytes.CreateChildAtOffset(field.name, offset + index * field_size, ptr_field_type).address_of.data)
offset += self.capacity * field_size
return self.bytes.CreateValueFromData('[%d]' % index, data, self.entry_type)
except: return None
 
class std_HashMapUnmanaged_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.capacity = 0
self.indices = tuple()
 
self.metadata = self.value.GetChildMemberWithName('metadata')
if not self.metadata.unsigned: return
 
value_type = self.value.type
for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull):
ptr_self_type, ptr_hdr_type, ptr_entry_type = helper.function.type.GetFunctionArgumentTypes()
if ptr_self_type.GetPointeeType() == value_type: break
else: return
self.entry_type = ptr_entry_type.GetPointeeType()
 
hdr_type = ptr_hdr_type.GetPointeeType()
hdr = self.metadata.CreateValueFromAddress('header', self.metadata.deref.load_addr - hdr_type.size, hdr_type)
self.values = hdr.GetChildMemberWithName('values')
self.keys = hdr.GetChildMemberWithName('keys')
self.capacity = hdr.GetChildMemberWithName('capacity').unsigned
 
self.indices = tuple(i for i, value in enumerate(self.metadata.GetPointeeData(0, self.capacity).sint8) if value < 0)
except: pass
def has_children(self): return True
def num_children(self): return len(self.indices)
def get_capacity(self): return self.capacity
def get_child_index(self, name):
try: return int(name.removeprefix('[').removesuffix(']'))
except: return -1
def get_child_at_index(self, index):
try:
fields = {name: base.CreateChildAtOffset(name, self.indices[index] * pointee_type.size, pointee_type).address_of.data for name, base, pointee_type in ((name, base, base.type.GetPointeeType()) for name, base in (('key_ptr', self.keys), ('value_ptr', self.values)))}
data = lldb.SBData()
for field in self.entry_type.fields: data.Append(fields[field.name])
return self.metadata.CreateValueFromData('[%d]' % index, data, self.entry_type)
except: return None
def std_HashMapUnmanaged_SummaryProvider(value, _=None):
synth = std_HashMapUnmanaged_SynthProvider(value.GetNonSyntheticValue(), _)
synth.update()
return 'len=%d capacity=%d' % (synth.num_children(), synth.get_capacity())
 
# formats a struct of fields of the form `name_ptr: *Type` by auto dereferencing its fields
class std_Entry_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.children = tuple(child.Clone(child.name.removesuffix('_ptr')) for child in self.value.children if child.type.GetPointeeType().size != 0)
self.indices = {child.name: i for i, child in enumerate(self.children)}
except: pass
def has_children(self): return self.num_children() != 0
def num_children(self): return len(self.children)
def get_child_index(self, name): return self.indices.get(name)
def get_child_at_index(self, index): return self.children[index].deref if index >= 0 and index < len(self.children) else None
 
# Define Zig Stage2 Compiler
 
class TagAndPayload_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
self.tag = self.value.GetChildMemberWithName('tag') or self.value.GetChildMemberWithName('tag_ptr').deref.Clone('tag')
data = self.value.GetChildMemberWithName('data_ptr') or self.value.GetChildMemberWithName('data')
self.payload = data.GetChildMemberWithName('payload').GetChildMemberWithName(data.GetChildMemberWithName('tag').value)
except: pass
def has_children(self): return True
def num_children(self): return 2
def get_child_index(self, name):
try: return ('tag', 'payload').index(name)
except: return -1
def get_child_at_index(self, index): return (self.tag, self.payload)[index] if index >= 0 and index < 2 else None
 
def Inst_Ref_SummaryProvider(value, _=None):
members = value.type.enum_members
return value if any(value.unsigned == member.unsigned for member in members) else 'instructions[%d]' % (value.unsigned - len(members))
 
class Module_Decl__Module_Decl_Index_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
for frame in self.value.thread:
mod = frame.FindVariable('mod') or frame.FindVariable('module')
if mod: break
else: return
self.ptr = mod.GetChildMemberWithName('allocated_decls').GetChildAtIndex(self.value.unsigned).Clone('decl')
except: pass
def has_children(self): return True
def num_children(self): return 1
def get_child_index(self, name): return 0 if name == 'decl' else -1
def get_child_at_index(self, index): return self.ptr if index == 0 else None
 
class TagOrPayloadPtr_SynthProvider:
def __init__(self, value, _=None): self.value = value
def update(self):
try:
value_type = self.value.type
for helper in self.value.target.FindFunctions('%s.dbHelper' % value_type.name, lldb.eFunctionNameTypeFull):
ptr_self_type, ptr_tag_to_payload_map_type = helper.function.type.GetFunctionArgumentTypes()
self_type = ptr_self_type.GetPointeeType()
if self_type == value_type: break
else: return
tag_to_payload_map = {field.name: field.type for field in ptr_tag_to_payload_map_type.GetPointeeType().fields}
 
tag = self.value.GetChildMemberWithName('tag_if_small_enough')
if tag.unsigned < page_size:
self.tag = tag.Clone('tag')
self.payload = None
else:
ptr_otherwise = self.value.GetChildMemberWithName('ptr_otherwise')
self.tag = ptr_otherwise.GetChildMemberWithName('tag')
self.payload = ptr_otherwise.Cast(tag_to_payload_map[self.tag.value]).GetChildMemberWithName('data').Clone('payload')
except: pass
def has_children(self): return True
def num_children(self): return 1 + (self.payload is not None)
def get_child_index(self, name):
try: return ('tag', 'payload').index(name)
except: return -1
def get_child_at_index(self, index): return (self.tag, self.payload)[index] if index >= 0 and index < 2 else None
 
def Module_Decl_name(decl):
error = lldb.SBError()
return decl.process.ReadCStringFromMemory(decl.GetChildMemberWithName('name').deref.load_addr, 256, error)
 
def Module_Namespace_RenderFullyQualifiedName(namespace):
parent = namespace.GetChildMemberWithName('parent')
if parent.unsigned < page_size: return zig_String_decode(namespace.GetChildMemberWithName('file_scope').GetChildMemberWithName('sub_file_path')).removesuffix('.zig').replace('/', '.')
return '.'.join((Module_Namespace_RenderFullyQualifiedName(parent), Module_Decl_name(namespace.GetChildMemberWithName('ty').GetChildMemberWithName('payload').GetChildMemberWithName('owner_decl').GetChildMemberWithName('decl'))))
 
def Module_Decl_RenderFullyQualifiedName(decl): return '.'.join((Module_Namespace_RenderFullyQualifiedName(decl.GetChildMemberWithName('src_namespace')), Module_Decl_name(decl)))
 
def OwnerDecl_RenderFullyQualifiedName(payload): return Module_Decl_RenderFullyQualifiedName(payload.GetChildMemberWithName('owner_decl').GetChildMemberWithName('decl'))
 
def type_Type_pointer(payload):
pointee_type = payload.GetChildMemberWithName('pointee_type')
sentinel = payload.GetChildMemberWithName('sentinel').GetChildMemberWithName('child')
align = payload.GetChildMemberWithName('align').unsigned
addrspace = payload.GetChildMemberWithName('addrspace').value
bit_offset = payload.GetChildMemberWithName('bit_offset').unsigned
host_size = payload.GetChildMemberWithName('host_size').unsigned
vector_index = payload.GetChildMemberWithName('vector_index')
allowzero = payload.GetChildMemberWithName('allowzero').unsigned
const = not payload.GetChildMemberWithName('mutable').unsigned
volatile = payload.GetChildMemberWithName('volatile').unsigned
size = payload.GetChildMemberWithName('size').value
 
if size == 'One': summary = '*'
elif size == 'Many': summary = '[*'
elif size == 'Slice': summary = '['
elif size == 'C': summary = '[*c'
if sentinel: summary += ':%s' % value_Value_SummaryProvider(sentinel)
if size != 'One': summary += ']'
if allowzero: summary += 'allowzero '
if align != 0 or host_size != 0 or vector_index.value != 'none': summary += 'align(%d%s%s) ' % (align, ':%d:%d' % (bit_offset, host_size) if bit_offset != 0 or host_size != 0 else '', ':?' if vector_index.value == 'runtime' else ':%d' % vector_index.unsigned if vector_index.value != 'none' else '')
if addrspace != 'generic': summary += 'addrspace(.%s) ' % addrspace
if const: summary += 'const '
if volatile: summary += 'volatile '
summary += type_Type_SummaryProvider(pointee_type)
return summary
 
def type_Type_function(payload):
param_types = payload.GetChildMemberWithName('param_types').children
comptime_params = payload.GetChildMemberWithName('comptime_params').GetPointeeData(0, len(param_types)).uint8
return_type = payload.GetChildMemberWithName('return_type')
alignment = payload.GetChildMemberWithName('alignment').unsigned
noalias_bits = payload.GetChildMemberWithName('noalias_bits').unsigned
cc = payload.GetChildMemberWithName('cc').value
is_var_args = payload.GetChildMemberWithName('is_var_args').unsigned
 
return 'fn(%s)%s%s %s' % (', '.join(tuple(''.join(('comptime ' if comptime_param else '', 'noalias ' if noalias_bits & 1 << i else '', type_Type_SummaryProvider(param_type))) for i, (comptime_param, param_type) in enumerate(zip(comptime_params, param_types))) + (('...',) if is_var_args else ())), ' align(%d)' % alignment if alignment != 0 else '', ' callconv(.%s)' % cc if cc != 'Unspecified' else '', type_Type_SummaryProvider(return_type))
 
def type_Type_SummaryProvider(value, _=None):
tag = value.GetChildMemberWithName('tag').value
return type_tag_handlers.get(tag, lambda payload: tag)(value.GetChildMemberWithName('payload'))
 
type_tag_handlers = {
'atomic_order': lambda payload: 'std.builtin.AtomicOrder',
'atomic_rmw_op': lambda payload: 'std.builtin.AtomicRmwOp',
'calling_convention': lambda payload: 'std.builtin.CallingConvention',
'address_space': lambda payload: 'std.builtin.AddressSpace',
'float_mode': lambda payload: 'std.builtin.FloatMode',
'reduce_op': lambda payload: 'std.builtin.ReduceOp',
'modifier': lambda payload: 'std.builtin.CallModifier',
'prefetch_options': lambda payload: 'std.builtin.PrefetchOptions',
'export_options': lambda payload: 'std.builtin.ExportOptions',
'extern_options': lambda payload: 'std.builtin.ExternOptions',
'type_info': lambda payload: 'std.builtin.Type',
 
'enum_literal': lambda payload: '@TypeOf(.enum_literal)',
'null': lambda payload: '@TypeOf(null)',
'undefined': lambda payload: '@TypeOf(undefined)',
'empty_struct_literal': lambda payload: '@TypeOf(.{})',
 
'anyerror_void_error_union': lambda payload: 'anyerror!void',
'const_slice_u8': lambda payload: '[]const u8',
'const_slice_u8_sentinel_0': lambda payload: '[:0]const u8',
'fn_noreturn_no_args': lambda payload: 'fn() noreturn',
'fn_void_no_args': lambda payload: 'fn() void',
'fn_naked_noreturn_no_args': lambda payload: 'fn() callconv(.Naked) noreturn',
'fn_ccc_void_no_args': lambda payload: 'fn() callconv(.C) void',
'single_const_pointer_to_comptime_int': lambda payload: '*const comptime_int',
'manyptr_u8': lambda payload: '[*]u8',
'manyptr_const_u8': lambda payload: '[*]const u8',
'manyptr_const_u8_sentinel_0': lambda payload: '[*:0]const u8',
 
'function': type_Type_function,
'error_union': lambda payload: '%s!%s' % (type_Type_SummaryProvider(payload.GetChildMemberWithName('error_set')), type_Type_SummaryProvider(payload.GetChildMemberWithName('payload'))),
'array_u8': lambda payload: '[%d]u8' % payload.unsigned,
'array_u8_sentinel_0': lambda payload: '[%d:0]u8' % payload.unsigned,
'vector': lambda payload: '@Vector(%d, %s)' % (payload.GetChildMemberWithName('len').unsigned, type_Type_SummaryProvider(payload.GetChildMemberWithName('elem_type'))),
'array': lambda payload: '[%d]%s' % (payload.GetChildMemberWithName('len').unsigned, type_Type_SummaryProvider(payload.GetChildMemberWithName('elem_type'))),
'array_sentinel': lambda payload: '[%d:%s]%s' % (payload.GetChildMemberWithName('len').unsigned, value_Value_SummaryProvider(payload.GetChildMemberWithName('sentinel')), type_Type_SummaryProvider(payload.GetChildMemberWithName('elem_type'))),
'tuple': lambda payload: 'tuple{%s}' % ', '.join(('comptime %%s = %s' % value_Value_SummaryProvider(value) if value.GetChildMemberWithName('tag').value != 'unreachable_value' else '%s') % type_Type_SummaryProvider(type) for type, value in zip(payload.GetChildMemberWithName('types').children, payload.GetChildMemberWithName('values').children)),
'anon_struct': lambda payload: 'struct{%s}' % ', '.join(('comptime %%s: %%s = %s' % value_Value_SummaryProvider(value) if value.GetChildMemberWithName('tag').value != 'unreachable_value' else '%s: %s') % (zig_String_AsIdentifier(name, zig_IsFieldName), type_Type_SummaryProvider(type)) for name, type, value in zip(payload.GetChildMemberWithName('names').children, payload.GetChildMemberWithName('types').children, payload.GetChildMemberWithName('values').children)),
'pointer': type_Type_pointer,
'single_const_pointer': lambda payload: '*const %s' % type_Type_SummaryProvider(payload),
'single_mut_pointer': lambda payload: '*%s' % type_Type_SummaryProvider(payload),
'many_const_pointer': lambda payload: '[*]const %s' % type_Type_SummaryProvider(payload),
'many_mut_pointer': lambda payload: '[*]%s' % type_Type_SummaryProvider(payload),
'c_const_pointer': lambda payload: '[*c]const %s' % type_Type_SummaryProvider(payload),
'c_mut_pointer': lambda payload: '[*c]%s' % type_Type_SummaryProvider(payload),
'const_slice': lambda payload: '[]const %s' % type_Type_SummaryProvider(payload),
'mut_slice': lambda payload: '[]%s' % type_Type_SummaryProvider(payload),
'int_signed': lambda payload: 'i%d' % payload.unsigned,
'int_unsigned': lambda payload: 'u%d' % payload.unsigned,
'optional': lambda payload: '?%s' % type_Type_SummaryProvider(payload),
'optional_single_mut_pointer': lambda payload: '?*%s' % type_Type_SummaryProvider(payload),
'optional_single_const_pointer': lambda payload: '?*const %s' % type_Type_SummaryProvider(payload),
'anyframe_T': lambda payload: 'anyframe->%s' % type_Type_SummaryProvider(payload),
'error_set': lambda payload: type_tag_handlers['error_set_merged'](payload.GetChildMemberWithName('names')),
'error_set_single': lambda payload: 'error{%s}' % zig_String_AsIdentifier(payload, zig_IsFieldName),
'error_set_merged': lambda payload: 'error{%s}' % ','.join(zig_String_AsIdentifier(child.GetChildMemberWithName('key'), zig_IsFieldName) for child in payload.GetChildMemberWithName('entries').children),
'error_set_inferred': lambda payload: '@typeInfo(@typeInfo(@TypeOf(%s)).Fn.return_type.?).ErrorUnion.error_set' % OwnerDecl_RenderFullyQualifiedName(payload.GetChildMemberWithName('func')),
 
'enum_full': OwnerDecl_RenderFullyQualifiedName,
'enum_nonexhaustive': OwnerDecl_RenderFullyQualifiedName,
'enum_numbered': OwnerDecl_RenderFullyQualifiedName,
'enum_simple': OwnerDecl_RenderFullyQualifiedName,
'struct': OwnerDecl_RenderFullyQualifiedName,
'union': OwnerDecl_RenderFullyQualifiedName,
'union_safety_tagged': OwnerDecl_RenderFullyQualifiedName,
'union_tagged': OwnerDecl_RenderFullyQualifiedName,
'opaque': OwnerDecl_RenderFullyQualifiedName,
}
 
def value_Value_str_lit(payload):
for frame in payload.thread:
mod = frame.FindVariable('mod') or frame.FindVariable('module')
if mod: break
else: return
return '"%s"' % zig_String_decode(mod.GetChildMemberWithName('string_literal_bytes').GetChildMemberWithName('items'), payload.GetChildMemberWithName('index').unsigned, payload.GetChildMemberWithName('len').unsigned)
 
def value_Value_SummaryProvider(value, _=None):
tag = value.GetChildMemberWithName('tag').value
return value_tag_handlers.get(tag, lambda payload: tag.removesuffix('_type'))(value.GetChildMemberWithName('payload'))
 
value_tag_handlers = {
'undef': lambda payload: 'undefined',
'zero': lambda payload: '0',
'one': lambda payload: '1',
'void_value': lambda payload: '{}',
'unreachable_value': lambda payload: 'unreachable',
'null_value': lambda payload: 'null',
'bool_true': lambda payload: 'true',
'bool_false': lambda payload: 'false',
 
'empty_struct_value': lambda payload: '.{}',
'empty_array': lambda payload: '.{}',
 
'ty': type_Type_SummaryProvider,
'int_type': lambda payload: '%c%d' % (payload.GetChildMemberWithName('bits').unsigned, 's' if payload.GetChildMemberWithName('signed').unsigned == 1 else 'u'),
'int_u64': lambda payload: '%d' % payload.unsigned,
'int_i64': lambda payload: '%d' % payload.signed,
'int_big_positive': lambda payload: sum(child.unsigned << i * child.type.size * 8 for i, child in enumerate(payload.children)),
'int_big_negative': lambda payload: '-%s' % value_tag_handlers['int_big_positive'](payload),
'function': OwnerDecl_RenderFullyQualifiedName,
'extern_fn': OwnerDecl_RenderFullyQualifiedName,
'variable': lambda payload: value_Value_SummaryProvider(payload.GetChildMemberWithName('decl').GetChildMemberWithName('val')),
'runtime_value': value_Value_SummaryProvider,
'decl_ref': lambda payload: value_Value_SummaryProvider(payload.GetChildMemberWithName('decl').GetChildMemberWithName('val')),
'decl_ref_mut': lambda payload: value_Value_SummaryProvider(payload.GetChildMemberWithName('decl_index').GetChildMemberWithName('decl').GetChildMemberWithName('val')),
'comptime_field_ptr': lambda payload: '&%s' % value_Value_SummaryProvider(payload.GetChildMemberWithName('field_val')),
'elem_ptr': lambda payload: '(%s)[%d]' % (value_Value_SummaryProvider(payload.GetChildMemberWithName('array_ptr')), payload.GetChildMemberWithName('index').unsigned),
'field_ptr': lambda payload: '(%s).field[%d]' % (value_Value_SummaryProvider(payload.GetChildMemberWithName('container_ptr')), payload.GetChildMemberWithName('field_index').unsigned),
'bytes': lambda payload: '"%s"' % zig_String_decode(payload),
'str_lit': value_Value_str_lit,
'repeated': lambda payload: '.{%s} ** _' % value_Value_SummaryProvider(payload),
'empty_array_sentinel': lambda payload: '.{%s}' % value_Value_SummaryProvider(payload),
'slice': lambda payload: '(%s)[0..%s]' % tuple(value_Value_SummaryProvider(payload.GetChildMemberWithName(name)) for name in ('ptr', 'len')),
'float_16': lambda payload: payload.value,
'float_32': lambda payload: payload.value,
'float_64': lambda payload: payload.value,
'float_80': lambda payload: payload.value,
'float_128': lambda payload: payload.value,
'enum_literal': lambda payload: '.%s' % zig_String_AsIdentifier(payload, zig_IsFieldName),
'enum_field_index': lambda payload: 'field[%d]' % payload.unsigned,
'error': lambda payload: 'error.%s' % zig_String_AsIdentifier(payload.GetChildMemberWithName('name'), zig_IsFieldName),
'eu_payload': value_Value_SummaryProvider,
'eu_payload_ptr': lambda payload: '&((%s).* catch unreachable)' % value_Value_SummaryProvider(payload.GetChildMemberWithName('container_ptr')),
'opt_payload': value_Value_SummaryProvider,
'opt_payload_ptr': lambda payload: '&(%s).*.?' % value_Value_SummaryProvider(payload.GetChildMemberWithName('container_ptr')),
'aggregate': lambda payload: '.{%s}' % ', '.join(map(value_Value_SummaryProvider, payload.children)),
'union': lambda payload: '.{.%s = %s}' % tuple(value_Value_SummaryProvider(payload.GetChildMemberWithName(name)) for name in ('tag', 'val')),
 
'lazy_align': lambda payload: '@alignOf(%s)' % type_Type_SummaryProvider(payload),
'lazy_size': lambda payload: '@sizeOf(%s)' % type_Type_SummaryProvider(payload),
}
 
# Initialize
 
def add(debugger, *, category, regex=False, type, identifier=None, synth=False, inline_children=False, expand=False, summary=False):
prefix = '.'.join((__name__, (identifier or type).replace('.', '_').replace(':', '_')))
if summary: debugger.HandleCommand('type summary add --category %s%s%s "%s"' % (category, ' --inline-children' if inline_children else ''.join((' --expand' if expand else '', ' --python-function %s_SummaryProvider' % prefix if summary == True else ' --summary-string "%s"' % summary)), ' --regex' if regex else '', type))
if synth: debugger.HandleCommand('type synthetic add --category %s%s --python-class %s_SynthProvider "%s"' % (category, ' --regex' if regex else '', prefix, type))
 
def MultiArrayList_Entry(type): return '^multi_array_list\\.MultiArrayList\\(%s\\)\\.Entry__struct_[1-9][0-9]*$' % type
 
def __lldb_init_module(debugger, _=None):
# Initialize Zig Language
add(debugger, category='zig', regex=True, type='^\\[\\]', identifier='zig_Slice', synth=True, expand=True, summary='len=${svar%#}')
add(debugger, category='zig', type='[]u8', identifier='zig_String', summary=True)
add(debugger, category='zig', regex=True, type='^\\?', identifier='zig_Optional', synth=True, summary=True)
add(debugger, category='zig', regex=True, type='^(error{.*}|anyerror)!', identifier='zig_ErrorUnion', synth=True, inline_children=True, summary=True)
 
# Initialize Zig Standard Library
add(debugger, category='zig.std', type='mem.Allocator', summary='${var.ptr}')
add(debugger, category='zig.std', regex=True, type='^segmented_list\\.SegmentedList\\(.*\\)$', identifier='std_SegmentedList', synth=True, expand=True, summary='len=${var.len}')
add(debugger, category='zig.std', regex=True, type='^multi_array_list\\.MultiArrayList\\(.*\\)$', identifier='std_MultiArrayList', synth=True, expand=True, summary='len=${var.len} capacity=${var.capacity}')
add(debugger, category='zig.std', regex=True, type=MultiArrayList_Entry('.*'), identifier='std_Entry', synth=True, inline_children=True, summary=True)
add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)$', identifier='std_HashMapUnmanaged', synth=True, expand=True, summary=True)
add(debugger, category='zig.std', regex=True, type='^hash_map\\.HashMapUnmanaged\\(.*\\)\\.Entry$', identifier = 'std_Entry', synth=True, inline_children=True, summary=True)
 
# Initialize Zig Stage2 Compiler
add(debugger, category='zig.stage2', type='Zir.Inst', identifier='TagAndPayload', synth=True, inline_children=True, summary=True)
add(debugger, category='zig.stage2', regex=True, type=MultiArrayList_Entry('Zir\\.Inst'), identifier='TagAndPayload', synth=True, inline_children=True, summary=True)
add(debugger, category='zig.stage2', regex=True, type='^Zir\\.Inst\\.Data\\.Data__struct_[1-9][0-9]*$', inline_children=True, summary=True)
add(debugger, category='zig.stage2', type='Zir.Inst::Zir.Inst.Ref', identifier='Inst_Ref', summary=True)
add(debugger, category='zig.stage2', type='Air.Inst', identifier='TagAndPayload', synth=True, inline_children=True, summary=True)
add(debugger, category='zig.stage2', regex=True, type=MultiArrayList_Entry('Air\\.Inst'), identifier='TagAndPayload', synth=True, inline_children=True, summary=True)
add(debugger, category='zig.stage2', regex=True, type='^Air\\.Inst\\.Data\\.Data__struct_[1-9][0-9]*$', inline_children=True, summary=True)
add(debugger, category='zig.stage2', type='Module.Decl::Module.Decl.Index', synth=True)
add(debugger, category='zig.stage2', type='type.Type', identifier='TagOrPayloadPtr', synth=True)
add(debugger, category='zig.stage2', type='type.Type', summary=True)
add(debugger, category='zig.stage2', type='value.Value', identifier='TagOrPayloadPtr', synth=True)
add(debugger, category='zig.stage2', type='value.Value', summary=True)
 
tools/stage2_gdb_pretty_printers.py added: 1877, removed: 639, total 1238
@@ -3,13 +3,55 @@
import re
import gdb.printing
 
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
import stage2_pretty_printers_common as common
 
 
class TypePrinter:
no_payload_count = 4096
 
# Keep in sync with src/type.zig
# Types which have no payload do not need to be entered here.
payload_type_names = {
'array_u8': 'Type.Payload.Len',
'array_u8_sentinel_0': 'Type.Payload.Len',
 
'single_const_pointer': 'Type.Payload.ElemType',
'single_mut_pointer': 'Type.Payload.ElemType',
'many_const_pointer': 'Type.Payload.ElemType',
'many_mut_pointer': 'Type.Payload.ElemType',
'c_const_pointer': 'Type.Payload.ElemType',
'c_mut_pointer': 'Type.Payload.ElemType',
'const_slice': 'Type.Payload.ElemType',
'mut_slice': 'Type.Payload.ElemType',
'optional': 'Type.Payload.ElemType',
'optional_single_mut_pointer': 'Type.Payload.ElemType',
'optional_single_const_pointer': 'Type.Payload.ElemType',
'anyframe_T': 'Type.Payload.ElemType',
 
'int_signed': 'Type.Payload.Bits',
'int_unsigned': 'Type.Payload.Bits',
 
'error_set': 'Type.Payload.ErrorSet',
'error_set_inferred': 'Type.Payload.ErrorSetInferred',
'error_set_merged': 'Type.Payload.ErrorSetMerged',
 
'array': 'Type.Payload.Array',
'vector': 'Type.Payload.Array',
 
'array_sentinel': 'Type.Payload.ArraySentinel',
'pointer': 'Type.Payload.Pointer',
'function': 'Type.Payload.Function',
'error_union': 'Type.Payload.ErrorUnion',
'error_set_single': 'Type.Payload.Name',
'opaque': 'Type.Payload.Opaque',
'struct': 'Type.Payload.Struct',
'union': 'Type.Payload.Union',
'union_tagged': 'Type.Payload.Union',
'enum_full, .enum_nonexhaustive': 'Type.Payload.EnumFull',
'enum_simple': 'Type.Payload.EnumSimple',
'enum_numbered': 'Type.Payload.EnumNumbered',
'empty_struct': 'Type.Payload.ContainerScope',
'tuple': 'Type.Payload.Tuple',
'anon_struct': 'Type.Payload.AnonStruct',
}
 
def __init__(self, val):
self.val = val
 
@@ -17,7 +59,7 @@ class TypePrinter:
tag_if_small_enough = self.val['tag_if_small_enough']
tag_type = tag_if_small_enough.type
 
if tag_if_small_enough < common.Type.no_payload_count:
if tag_if_small_enough < TypePrinter.no_payload_count:
return tag_if_small_enough
else:
return self.val['ptr_otherwise'].dereference()['tag']
@@ -27,7 +69,7 @@ class TypePrinter:
if tag is None:
return None
 
type_name = common.Type.payload_type_names.get(str(tag))
type_name = TypePrinter.payload_type_names.get(str(tag))
if type_name is None:
return None
return gdb.lookup_type('struct type.%s' % type_name)
@@ -36,12 +78,12 @@ class TypePrinter:
tag = self.tag()
if tag is None:
return '(invalid type)'
if self.val['tag_if_small_enough'] < common.Type.no_payload_count:
if self.val['tag_if_small_enough'] < TypePrinter.no_payload_count:
return '.%s' % str(tag)
return None
 
def children(self):
if self.val['tag_if_small_enough'] < common.Type.no_payload_count:
if self.val['tag_if_small_enough'] < TypePrinter.no_payload_count:
return
 
yield ('tag', '.%s' % str(self.tag()))
@@ -51,6 +93,55 @@ class TypePrinter:
yield ('payload', self.val['ptr_otherwise'].cast(payload_type.pointer()).dereference()['data'])
 
class ValuePrinter:
no_payload_count = 4096
 
# Keep in sync with src/value.zig
# Values which have no payload do not need to be entered here.
payload_type_names = {
'big_int_positive': 'Value.Payload.BigInt',
'big_int_negative': 'Value.Payload.BigInt',
 
'extern_fn': 'Value.Payload.ExternFn',
 
'decl_ref': 'Value.Payload.Decl',
 
'repeated': 'Value.Payload.SubValue',
'eu_payload': 'Value.Payload.SubValue',
'opt_payload': 'Value.Payload.SubValue',
'empty_array_sentinel': 'Value.Payload.SubValue',
 
'eu_payload_ptr': 'Value.Payload.PayloadPtr',
'opt_payload_ptr': 'Value.Payload.PayloadPtr',
 
'bytes': 'Value.Payload.Bytes',
'enum_literal': 'Value.Payload.Bytes',
 
'slice': 'Value.Payload.Slice',
 
'enum_field_index': 'Value.Payload.U32',
 
'ty': 'Value.Payload.Ty',
'int_type': 'Value.Payload.IntType',
'int_u64': 'Value.Payload.U64',
'int_i64': 'Value.Payload.I64',
'function': 'Value.Payload.Function',
'variable': 'Value.Payload.Variable',
'decl_ref_mut': 'Value.Payload.DeclRefMut',
'elem_ptr': 'Value.Payload.ElemPtr',
'field_ptr': 'Value.Payload.FieldPtr',
'float_16': 'Value.Payload.Float_16',
'float_32': 'Value.Payload.Float_32',
'float_64': 'Value.Payload.Float_64',
'float_80': 'Value.Payload.Float_80',
'float_128': 'Value.Payload.Float_128',
'error': 'Value.Payload.Error',
'inferred_alloc': 'Value.Payload.InferredAlloc',
'inferred_alloc_comptime': 'Value.Payload.InferredAllocComptime',
'aggregate': 'Value.Payload.Aggregate',
'union': 'Value.Payload.Union',
'bound_fn': 'Value.Payload.BoundFn',
}
 
def __init__(self, val):
self.val = val
 
@@ -58,7 +149,7 @@ class ValuePrinter:
tag_if_small_enough = self.val['tag_if_small_enough']
tag_type = tag_if_small_enough.type
 
if tag_if_small_enough < common.Value.no_payload_count:
if tag_if_small_enough < ValuePrinter.no_payload_count:
return tag_if_small_enough
else:
return self.val['ptr_otherwise'].dereference()['tag']
@@ -68,7 +159,7 @@ class ValuePrinter:
if tag is None:
return None
 
type_name = Comman.Value.payload_type_names.get(str(tag))
type_name = ValuePrinter.payload_type_names.get(str(tag))
if type_name is None:
return None
return gdb.lookup_type('struct value.%s' % type_name)
@@ -77,12 +168,12 @@ class ValuePrinter:
tag = self.tag()
if tag is None:
return '(invalid value)'
if self.val['tag_if_small_enough'] < common.Value.no_payload_count:
if self.val['tag_if_small_enough'] < ValuePrinter.no_payload_count:
return '.%s' % str(tag)
return None
 
def children(self):
if self.val['tag_if_small_enough'] < common.Value.no_payload_count:
if self.val['tag_if_small_enough'] < ValuePrinter.no_payload_count:
return
 
yield ('tag', '.%s' % str(self.tag()))
 
ev/null added: 1877, removed: 639, total 1238
@@ -1,59 +0,0 @@
# pretty printing for stage 2.
# put "command script /path/to/stage2_lldb_pretty_printers.py" and "type category enable stage2" in ~/.lldbinit to load it automatically.
import lldb
import stage2_pretty_printers_common as common
 
category = 'stage2'
module = category + '_lldb_pretty_printers'
 
class type_Type_SynthProvider:
def __init__(self, type, _=None):
self.type = type
 
def update(self):
self.tag = self.type.GetChildMemberWithName('tag_if_small_enough').Clone('tag')
self.payload = None
if self.tag.GetValueAsUnsigned() >= common.Type.no_payload_count:
ptr_otherwise = self.type.GetChildMemberWithName('ptr_otherwise')
self.tag = ptr_otherwise.Dereference().GetChildMemberWithName('tag')
payload_type = self.type.target.FindFirstType('type.' + common.Type.payload_type_names[self.tag.GetValue()])
self.payload = ptr_otherwise.Cast(payload_type.GetPointerType()).Dereference().GetChildMemberWithName('data').Clone('payload')
 
def num_children(self):
return 1 + (self.payload is not None)
 
def get_child_index(self, name):
return ['tag', 'payload'].index(name)
 
def get_child_at_index(self, index):
return [self.tag, self.payload][index]
 
class value_Value_SynthProvider:
def __init__(self, value, _=None):
self.value = value
 
def update(self):
self.tag = self.value.GetChildMemberWithName('tag_if_small_enough').Clone('tag')
self.payload = None
if self.tag.GetValueAsUnsigned() >= common.Value.no_payload_count:
ptr_otherwise = self.value.GetChildMemberWithName('ptr_otherwise')
self.tag = ptr_otherwise.Dereference().GetChildMemberWithName('tag')
payload_type = self.value.target.FindFirstType('value.' + common.Value.payload_type_names[self.tag.GetValue()])
self.payload = ptr_otherwise.Cast(payload_type.GetPointerType()).Dereference().GetChildMemberWithName('data').Clone('payload')
 
def num_children(self):
return 1 + (self.payload is not None)
 
def get_child_index(self, name):
return ['tag', 'payload'].index(name)
 
def get_child_at_index(self, index):
return [self.tag, self.payload][index]
 
def add(debugger, type, summary=False, synth=False):
if summary: debugger.HandleCommand('type summary add --python-function ' + module + '.' + type.replace('.', '_') + '_SummaryProvider "' + type + '" --category ' + category)
if synth: debugger.HandleCommand('type synthetic add --python-class ' + module + '.' + type.replace('.', '_') + '_SynthProvider "' + type + '" --category ' + category)
 
def __lldb_init_module(debugger, _=None):
add(debugger, 'type.Type', synth=True)
add(debugger, 'value.Value', synth=True)
 
ev/null added: 1877, removed: 639, total 1238
@@ -1,98 +0,0 @@
class Type:
no_payload_count = 4096
 
# Keep in sync with src/type.zig
# Types which have no payload do not need to be entered here.
payload_type_names = {
'array_u8': 'Type.Payload.Len',
'array_u8_sentinel_0': 'Type.Payload.Len',
 
'single_const_pointer': 'Type.Payload.ElemType',
'single_mut_pointer': 'Type.Payload.ElemType',
'many_const_pointer': 'Type.Payload.ElemType',
'many_mut_pointer': 'Type.Payload.ElemType',
'c_const_pointer': 'Type.Payload.ElemType',
'c_mut_pointer': 'Type.Payload.ElemType',
'const_slice': 'Type.Payload.ElemType',
'mut_slice': 'Type.Payload.ElemType',
'optional': 'Type.Payload.ElemType',
'optional_single_mut_pointer': 'Type.Payload.ElemType',
'optional_single_const_pointer': 'Type.Payload.ElemType',
'anyframe_T': 'Type.Payload.ElemType',
 
'int_signed': 'Type.Payload.Bits',
'int_unsigned': 'Type.Payload.Bits',
 
'error_set': 'Type.Payload.ErrorSet',
'error_set_inferred': 'Type.Payload.ErrorSetInferred',
'error_set_merged': 'Type.Payload.ErrorSetMerged',
 
'array': 'Type.Payload.Array',
'vector': 'Type.Payload.Array',
 
'array_sentinel': 'Type.Payload.ArraySentinel',
'pointer': 'Type.Payload.Pointer',
'function': 'Type.Payload.Function',
'error_union': 'Type.Payload.ErrorUnion',
'error_set_single': 'Type.Payload.Name',
'opaque': 'Type.Payload.Opaque',
'struct': 'Type.Payload.Struct',
'union': 'Type.Payload.Union',
'union_tagged': 'Type.Payload.Union',
'enum_full, .enum_nonexhaustive': 'Type.Payload.EnumFull',
'enum_simple': 'Type.Payload.EnumSimple',
'enum_numbered': 'Type.Payload.EnumNumbered',
'empty_struct': 'Type.Payload.ContainerScope',
'tuple': 'Type.Payload.Tuple',
'anon_struct': 'Type.Payload.AnonStruct',
}
 
class Value:
no_payload_count = 4096
 
# Keep in sync with src/value.zig
# Values which have no payload do not need to be entered here.
payload_type_names = {
'big_int_positive': 'Value.Payload.BigInt',
'big_int_negative': 'Value.Payload.BigInt',
 
'extern_fn': 'Value.Payload.ExternFn',
 
'decl_ref': 'Value.Payload.Decl',
 
'repeated': 'Value.Payload.SubValue',
'eu_payload': 'Value.Payload.SubValue',
'opt_payload': 'Value.Payload.SubValue',
'empty_array_sentinel': 'Value.Payload.SubValue',
 
'eu_payload_ptr': 'Value.Payload.PayloadPtr',
'opt_payload_ptr': 'Value.Payload.PayloadPtr',
 
'bytes': 'Value.Payload.Bytes',
'enum_literal': 'Value.Payload.Bytes',
 
'slice': 'Value.Payload.Slice',
 
'enum_field_index': 'Value.Payload.U32',
 
'ty': 'Value.Payload.Ty',
'int_type': 'Value.Payload.IntType',
'int_u64': 'Value.Payload.U64',
'int_i64': 'Value.Payload.I64',
'function': 'Value.Payload.Function',
'variable': 'Value.Payload.Variable',
'decl_ref_mut': 'Value.Payload.DeclRefMut',
'elem_ptr': 'Value.Payload.ElemPtr',
'field_ptr': 'Value.Payload.FieldPtr',
'float_16': 'Value.Payload.Float_16',
'float_32': 'Value.Payload.Float_32',
'float_64': 'Value.Payload.Float_64',
'float_80': 'Value.Payload.Float_80',
'float_128': 'Value.Payload.Float_128',
'error': 'Value.Payload.Error',
'inferred_alloc': 'Value.Payload.InferredAlloc',
'inferred_alloc_comptime': 'Value.Payload.InferredAllocComptime',
'aggregate': 'Value.Payload.Aggregate',
'union': 'Value.Payload.Union',
'bound_fn': 'Value.Payload.BoundFn',
}
 
tools/std_gdb_pretty_printers.py added: 1877, removed: 639, total 1238
@@ -26,7 +26,7 @@ class MultiArrayListPrinter:
self.val = val
 
def child_type(self):
(helper_fn, _) = gdb.lookup_symbol('%s.gdbHelper' % self.val.type.name)
(helper_fn, _) = gdb.lookup_symbol('%s.dbHelper' % self.val.type.name)
return helper_fn.type.fields()[1].type.target()
 
def to_string(self):
@@ -65,7 +65,7 @@ class HashMapPrinter:
self.val = val['unmanaged'] if is_managed else val
 
def header_ptr_type(self):
(helper_fn, _) = gdb.lookup_symbol('%s.gdbHelper' % self.val.type.name)
(helper_fn, _) = gdb.lookup_symbol('%s.dbHelper' % self.val.type.name)
return helper_fn.type.fields()[1].type
 
def header(self):