authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-24 19:12:44-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-07-24 19:12:44-07:00
log8c4482ed78fb651c0288f0cd2bdaf328564c6a49
treeefccaf6ea986a840f3cf4df64da174b7bdd18da5
parentdfbb6e9879693331c7b8ea57eb7aa8bdf934403f
parent4236ca40cd21895590c580c9a1f56423c2c2f167

Merge remote-tracking branch 'origin/master' into wrangle-writer-buffering


16 files changed, 428 insertions(+), 315 deletions(-)

lib/std/Build/Fuzz/WebServer.zig+8-7
......@@ -282,13 +282,15 @@ fn buildWasmBinary(
282282 var result: ?Path = null;
283283 var result_error_bundle = std.zig.ErrorBundle.empty;
284284
285 const stdout_br = poller.reader(.stdout);
285 const stdout = poller.reader(.stdout);
286
286287 poll: while (true) {
287288 const Header = std.zig.Server.Message.Header;
288 while (stdout_br.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll;
289 const header = (stdout_br.takeStruct(Header) catch unreachable).*;
290 while (stdout_br.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll;
291 const body = stdout_br.take(header.bytes_len) catch unreachable;
289 while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll;
290 const header = stdout.takeStruct(Header, .little) catch unreachable;
291 while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll;
292 const body = stdout.take(header.bytes_len) catch unreachable;
293
292294 switch (header.tag) {
293295 .zig_version => {
294296 if (!std.mem.eql(u8, builtin.zig_version_string, body)) {
......@@ -327,8 +329,7 @@ fn buildWasmBinary(
327329 }
328330 }
329331
330 const stderr_br = poller.reader(.stderr);
331 const stderr_contents = stderr_br.buffered();
332 const stderr_contents = try poller.toOwnedSlice(.stderr);
332333 if (stderr_contents.len > 0) {
333334 std.debug.print("{s}", .{stderr_contents});
334335 }
lib/std/Build/Step.zig+17-16
......@@ -286,7 +286,7 @@ pub fn cast(step: *Step, comptime T: type) ?*T {
286286}
287287
288288/// For debugging purposes, prints identifying information about this Step.
289pub fn dump(step: *Step, w: *std.io.Writer, tty_config: std.io.tty.Config) void {
289pub fn dump(step: *Step, w: *std.Io.Writer, tty_config: std.Io.tty.Config) void {
290290 const debug_info = std.debug.getSelfDebugInfo() catch |err| {
291291 w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{
292292 @errorName(err),
......@@ -359,7 +359,7 @@ pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutO
359359
360360pub const ZigProcess = struct {
361361 child: std.process.Child,
362 poller: std.io.Poller(StreamEnum),
362 poller: std.Io.Poller(StreamEnum),
363363 progress_ipc_fd: if (std.Progress.have_ipc) ?std.posix.fd_t else void,
364364
365365 pub const StreamEnum = enum { stdout, stderr };
......@@ -428,7 +428,7 @@ pub fn evalZigProcess(
428428 const zp = try gpa.create(ZigProcess);
429429 zp.* = .{
430430 .child = child,
431 .poller = std.io.poll(gpa, ZigProcess.StreamEnum, .{
431 .poller = std.Io.poll(gpa, ZigProcess.StreamEnum, .{
432432 .stdout = child.stdout.?,
433433 .stderr = child.stderr.?,
434434 }),
......@@ -511,12 +511,14 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path {
511511 var result: ?Path = null;
512512
513513 const stdout = zp.poller.reader(.stdout);
514
514515 poll: while (true) {
515516 const Header = std.zig.Server.Message.Header;
516517 while (stdout.buffered().len < @sizeOf(Header)) if (!try zp.poller.poll()) break :poll;
517 const header = (stdout.takeStruct(Header) catch unreachable).*;
518 const header = stdout.takeStruct(Header, .little) catch unreachable;
518519 while (stdout.buffered().len < header.bytes_len) if (!try zp.poller.poll()) break :poll;
519520 const body = stdout.take(header.bytes_len) catch unreachable;
521
520522 switch (header.tag) {
521523 .zig_version => {
522524 if (!std.mem.eql(u8, builtin.zig_version_string, body)) {
......@@ -606,8 +608,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path {
606608
607609 s.result_duration_ns = timer.read();
608610
609 const stderr = zp.poller.reader(.stderr);
610 const stderr_contents = stderr.buffered();
611 const stderr_contents = try zp.poller.toOwnedSlice(.stderr);
611612 if (stderr_contents.len > 0) {
612613 try s.result_error_msgs.append(arena, try arena.dupe(u8, stderr_contents));
613614 }
......@@ -726,7 +727,7 @@ pub fn allocPrintCmd2(
726727 argv: []const []const u8,
727728) Allocator.Error![]u8 {
728729 const shell = struct {
729 fn escape(writer: *std.io.Writer, string: []const u8, is_argv0: bool) !void {
730 fn escape(writer: *std.Io.Writer, string: []const u8, is_argv0: bool) !void {
730731 for (string) |c| {
731732 if (switch (c) {
732733 else => true,
......@@ -760,9 +761,9 @@ pub fn allocPrintCmd2(
760761 }
761762 };
762763
763 var aw: std.io.Writer.Allocating = .init(arena);
764 var aw: std.Io.Writer.Allocating = .init(arena);
764765 const writer = &aw.writer;
765 if (opt_cwd) |cwd| try writer.print(arena, "cd {s} && ", .{cwd});
766 if (opt_cwd) |cwd| writer.print("cd {s} && ", .{cwd}) catch return error.OutOfMemory;
766767 if (opt_env) |env| {
767768 const process_env_map = std.process.getEnvMap(arena) catch std.process.EnvMap.init(arena);
768769 var it = env.iterator();
......@@ -772,17 +773,17 @@ pub fn allocPrintCmd2(
772773 if (process_env_map.get(key)) |process_value| {
773774 if (std.mem.eql(u8, value, process_value)) continue;
774775 }
775 try writer.print(arena, "{s}=", .{key});
776 try shell.escape(writer, value, false);
777 try writer.writeByte(arena, ' ');
776 writer.print("{s}=", .{key}) catch return error.OutOfMemory;
777 shell.escape(writer, value, false) catch return error.OutOfMemory;
778 writer.writeByte(' ') catch return error.OutOfMemory;
778779 }
779780 }
780 try shell.escape(writer, argv[0], true);
781 shell.escape(writer, argv[0], true) catch return error.OutOfMemory;
781782 for (argv[1..]) |arg| {
782 try writer.writeByte(arena, ' ');
783 try shell.escape(writer, arg, false);
783 writer.writeByte(' ') catch return error.OutOfMemory;
784 shell.escape(writer, arg, false) catch return error.OutOfMemory;
784785 }
785 return aw.getWritten();
786 return aw.toOwnedSlice();
786787}
787788
788789/// Prefer `cacheHitAndWatch` unless you already added watch inputs
lib/std/Build/Step/Run.zig+11-9
......@@ -1545,7 +1545,7 @@ fn evalZigTest(
15451545 const any_write_failed = first_write_failed or poll: while (true) {
15461546 const Header = std.zig.Server.Message.Header;
15471547 while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll false;
1548 const header = (stdout.takeStruct(Header, .little) catch unreachable).*;
1548 const header = stdout.takeStruct(Header, .little) catch unreachable;
15491549 while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll false;
15501550 const body = stdout.take(header.bytes_len) catch unreachable;
15511551 switch (header.tag) {
......@@ -1808,21 +1808,23 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult {
18081808 }
18091809 }
18101810
1811 stdout_bytes = poller.reader(.stdout).buffered();
1812 stderr_bytes = poller.reader(.stderr).buffered();
1811 stdout_bytes = try poller.toOwnedSlice(.stdout);
1812 stderr_bytes = try poller.toOwnedSlice(.stderr);
18131813 } else {
1814 var fr = stdout.readerStreaming();
1815 stdout_bytes = fr.interface().allocRemaining(arena, run.stdio_limit) catch |err| switch (err) {
1814 var small_buffer: [1]u8 = undefined;
1815 var stdout_reader = stdout.readerStreaming(&small_buffer);
1816 stdout_bytes = stdout_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) {
18161817 error.OutOfMemory => return error.OutOfMemory,
1817 error.ReadFailed => return fr.err.?,
1818 error.ReadFailed => return stdout_reader.err.?,
18181819 error.StreamTooLong => return error.StdoutStreamTooLong,
18191820 };
18201821 }
18211822 } else if (child.stderr) |stderr| {
1822 var fr = stderr.readerStreaming();
1823 stderr_bytes = fr.interface().allocRemaining(arena, run.stdio_limit) catch |err| switch (err) {
1823 var small_buffer: [1]u8 = undefined;
1824 var stderr_reader = stderr.readerStreaming(&small_buffer);
1825 stderr_bytes = stderr_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) {
18241826 error.OutOfMemory => return error.OutOfMemory,
1825 error.ReadFailed => return fr.err.?,
1827 error.ReadFailed => return stderr_reader.err.?,
18261828 error.StreamTooLong => return error.StderrStreamTooLong,
18271829 };
18281830 }
lib/std/Io.zig+196-143
......@@ -92,12 +92,7 @@ pub fn poll(
9292 const enum_fields = @typeInfo(StreamEnum).@"enum".fields;
9393 var result: Poller(StreamEnum) = .{
9494 .gpa = gpa,
95 .readers = @splat(.{
96 .unbuffered_reader = .failing,
97 .buffer = &.{},
98 .end = 0,
99 .seek = 0,
100 }),
95 .readers = @splat(.failing),
10196 .poll_fds = undefined,
10297 .windows = if (is_windows) .{
10398 .first_read_done = false,
......@@ -186,21 +181,40 @@ pub fn Poller(comptime StreamEnum: type) type {
186181 }
187182 }
188183
189 pub inline fn reader(self: *Self, comptime which: StreamEnum) *Reader {
184 pub fn reader(self: *Self, which: StreamEnum) *Reader {
190185 return &self.readers[@intFromEnum(which)];
191186 }
192187
188 pub fn toOwnedSlice(self: *Self, which: StreamEnum) error{OutOfMemory}![]u8 {
189 const gpa = self.gpa;
190 const r = reader(self, which);
191 if (r.seek == 0) {
192 const new = try gpa.realloc(r.buffer, r.end);
193 r.buffer = &.{};
194 r.end = 0;
195 return new;
196 }
197 const new = try gpa.dupe(u8, r.buffered());
198 gpa.free(r.buffer);
199 r.buffer = &.{};
200 r.seek = 0;
201 r.end = 0;
202 return new;
203 }
204
193205 fn pollWindows(self: *Self, nanoseconds: ?u64) !bool {
194206 const bump_amt = 512;
207 const gpa = self.gpa;
195208
196209 if (!self.windows.first_read_done) {
197210 var already_read_data = false;
198211 for (0..enum_fields.len) |i| {
199212 const handle = self.windows.active.handles_buf[i];
200213 switch (try windowsAsyncReadToFifoAndQueueSmallRead(
214 gpa,
201215 handle,
202216 &self.windows.overlapped[i],
203 &self.fifos[i],
217 &self.readers[i],
204218 &self.windows.small_bufs[i],
205219 bump_amt,
206220 )) {
......@@ -247,7 +261,7 @@ pub fn Poller(comptime StreamEnum: type) type {
247261 const handle = self.windows.active.handles_buf[active_idx];
248262
249263 const overlapped = &self.windows.overlapped[stream_idx];
250 const stream_fifo = &self.fifos[stream_idx];
264 const stream_reader = &self.readers[stream_idx];
251265 const small_buf = &self.windows.small_bufs[stream_idx];
252266
253267 const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) {
......@@ -258,12 +272,16 @@ pub fn Poller(comptime StreamEnum: type) type {
258272 },
259273 .aborted => unreachable,
260274 };
261 try stream_fifo.write(small_buf[0..num_bytes_read]);
275 const buf = small_buf[0..num_bytes_read];
276 const dest = try writableSliceGreedyAlloc(stream_reader, gpa, buf.len);
277 @memcpy(dest[0..buf.len], buf);
278 advanceBufferEnd(stream_reader, buf.len);
262279
263280 switch (try windowsAsyncReadToFifoAndQueueSmallRead(
281 gpa,
264282 handle,
265283 overlapped,
266 stream_fifo,
284 stream_reader,
267285 small_buf,
268286 bump_amt,
269287 )) {
......@@ -298,18 +316,18 @@ pub fn Poller(comptime StreamEnum: type) type {
298316 }
299317
300318 var keep_polling = false;
301 inline for (&self.poll_fds, &self.readers) |*poll_fd, *r| {
319 for (&self.poll_fds, &self.readers) |*poll_fd, *r| {
302320 // Try reading whatever is available before checking the error
303321 // conditions.
304322 // It's still possible to read after a POLL.HUP is received,
305323 // always check if there's some data waiting to be read first.
306324 if (poll_fd.revents & posix.POLL.IN != 0) {
307 const buf = try r.writableSliceGreedyAlloc(gpa, bump_amt);
325 const buf = try writableSliceGreedyAlloc(r, gpa, bump_amt);
308326 const amt = posix.read(poll_fd.fd, buf) catch |err| switch (err) {
309327 error.BrokenPipe => 0, // Handle the same as EOF.
310328 else => |e| return e,
311329 };
312 r.advanceBufferEnd(amt);
330 advanceBufferEnd(r, amt);
313331 if (amt == 0) {
314332 // Remove the fd when the EOF condition is met.
315333 poll_fd.fd = -1;
......@@ -325,146 +343,181 @@ pub fn Poller(comptime StreamEnum: type) type {
325343 }
326344 return keep_polling;
327345 }
328 };
329}
330346
331/// The `ReadFile` docuementation states that `lpNumberOfBytesRead` does not have a meaningful
332/// result when using overlapped I/O, but also that it cannot be `null` on Windows 7. For
333/// compatibility, we point it to this dummy variables, which we never otherwise access.
334/// See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile
335var win_dummy_bytes_read: u32 = undefined;
336
337/// Read as much data as possible from `handle` with `overlapped`, and write it to the FIFO. Before
338/// returning, queue a read into `small_buf` so that `WaitForMultipleObjects` returns when more data
339/// is available. `handle` must have no pending asynchronous operation.
340fn windowsAsyncReadToFifoAndQueueSmallRead(
341 handle: windows.HANDLE,
342 overlapped: *windows.OVERLAPPED,
343 r: *Reader,
344 small_buf: *[128]u8,
345 bump_amt: usize,
346) !enum { empty, populated, closed_populated, closed } {
347 var read_any_data = false;
348 while (true) {
349 const fifo_read_pending = while (true) {
350 const buf = try r.writableWithSize(bump_amt);
351 const buf_len = math.cast(u32, buf.len) orelse math.maxInt(u32);
352
353 if (0 == windows.kernel32.ReadFile(
354 handle,
355 buf.ptr,
356 buf_len,
357 &win_dummy_bytes_read,
358 overlapped,
359 )) switch (windows.GetLastError()) {
360 .IO_PENDING => break true,
361 .BROKEN_PIPE => return if (read_any_data) .closed_populated else .closed,
362 else => |err| return windows.unexpectedError(err),
363 };
347 /// Returns a slice into the unused capacity of `buffer` with at least
348 /// `min_len` bytes, extending `buffer` by resizing it with `gpa` as necessary.
349 ///
350 /// After calling this function, typically the caller will follow up with a
351 /// call to `advanceBufferEnd` to report the actual number of bytes buffered.
352 fn writableSliceGreedyAlloc(r: *Reader, allocator: Allocator, min_len: usize) Allocator.Error![]u8 {
353 {
354 const unused = r.buffer[r.end..];
355 if (unused.len >= min_len) return unused;
356 }
357 if (r.seek > 0) r.rebase();
358 {
359 var list: std.ArrayListUnmanaged(u8) = .{
360 .items = r.buffer[0..r.end],
361 .capacity = r.buffer.len,
362 };
363 defer r.buffer = list.allocatedSlice();
364 try list.ensureUnusedCapacity(allocator, min_len);
365 }
366 const unused = r.buffer[r.end..];
367 assert(unused.len >= min_len);
368 return unused;
369 }
364370
365 const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) {
366 .success => |n| n,
367 .closed => return if (read_any_data) .closed_populated else .closed,
368 .aborted => unreachable,
369 };
371 /// After writing directly into the unused capacity of `buffer`, this function
372 /// updates `end` so that users of `Reader` can receive the data.
373 fn advanceBufferEnd(r: *Reader, n: usize) void {
374 assert(n <= r.buffer.len - r.end);
375 r.end += n;
376 }
370377
371 read_any_data = true;
372 r.update(num_bytes_read);
378 /// The `ReadFile` docuementation states that `lpNumberOfBytesRead` does not have a meaningful
379 /// result when using overlapped I/O, but also that it cannot be `null` on Windows 7. For
380 /// compatibility, we point it to this dummy variables, which we never otherwise access.
381 /// See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile
382 var win_dummy_bytes_read: u32 = undefined;
383
384 /// Read as much data as possible from `handle` with `overlapped`, and write it to the FIFO. Before
385 /// returning, queue a read into `small_buf` so that `WaitForMultipleObjects` returns when more data
386 /// is available. `handle` must have no pending asynchronous operation.
387 fn windowsAsyncReadToFifoAndQueueSmallRead(
388 gpa: Allocator,
389 handle: windows.HANDLE,
390 overlapped: *windows.OVERLAPPED,
391 r: *Reader,
392 small_buf: *[128]u8,
393 bump_amt: usize,
394 ) !enum { empty, populated, closed_populated, closed } {
395 var read_any_data = false;
396 while (true) {
397 const fifo_read_pending = while (true) {
398 const buf = try writableSliceGreedyAlloc(r, gpa, bump_amt);
399 const buf_len = math.cast(u32, buf.len) orelse math.maxInt(u32);
373400
374 if (num_bytes_read == buf_len) {
375 // We filled the buffer, so there's probably more data available.
376 continue;
377 } else {
378 // We didn't fill the buffer, so assume we're out of data.
379 // There is no pending read.
380 break false;
381 }
382 };
401 if (0 == windows.kernel32.ReadFile(
402 handle,
403 buf.ptr,
404 buf_len,
405 &win_dummy_bytes_read,
406 overlapped,
407 )) switch (windows.GetLastError()) {
408 .IO_PENDING => break true,
409 .BROKEN_PIPE => return if (read_any_data) .closed_populated else .closed,
410 else => |err| return windows.unexpectedError(err),
411 };
383412
384 if (fifo_read_pending) cancel_read: {
385 // Cancel the pending read into the FIFO.
386 _ = windows.kernel32.CancelIo(handle);
413 const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) {
414 .success => |n| n,
415 .closed => return if (read_any_data) .closed_populated else .closed,
416 .aborted => unreachable,
417 };
387418
388 // We have to wait for the handle to be signalled, i.e. for the cancellation to complete.
389 switch (windows.kernel32.WaitForSingleObject(handle, windows.INFINITE)) {
390 windows.WAIT_OBJECT_0 => {},
391 windows.WAIT_FAILED => return windows.unexpectedError(windows.GetLastError()),
392 else => unreachable,
393 }
419 read_any_data = true;
420 advanceBufferEnd(r, num_bytes_read);
394421
395 // If it completed before we canceled, make sure to tell the FIFO!
396 const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, true)) {
397 .success => |n| n,
398 .closed => return if (read_any_data) .closed_populated else .closed,
399 .aborted => break :cancel_read,
400 };
401 read_any_data = true;
402 r.update(num_bytes_read);
403 }
422 if (num_bytes_read == buf_len) {
423 // We filled the buffer, so there's probably more data available.
424 continue;
425 } else {
426 // We didn't fill the buffer, so assume we're out of data.
427 // There is no pending read.
428 break false;
429 }
430 };
404431
405 // Try to queue the 1-byte read.
406 if (0 == windows.kernel32.ReadFile(
407 handle,
408 small_buf,
409 small_buf.len,
410 &win_dummy_bytes_read,
411 overlapped,
412 )) switch (windows.GetLastError()) {
413 .IO_PENDING => {
414 // 1-byte read pending as intended
415 return if (read_any_data) .populated else .empty;
416 },
417 .BROKEN_PIPE => return if (read_any_data) .closed_populated else .closed,
418 else => |err| return windows.unexpectedError(err),
419 };
432 if (fifo_read_pending) cancel_read: {
433 // Cancel the pending read into the FIFO.
434 _ = windows.kernel32.CancelIo(handle);
420435
421 // We got data back this time. Write it to the FIFO and run the main loop again.
422 const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) {
423 .success => |n| n,
424 .closed => return if (read_any_data) .closed_populated else .closed,
425 .aborted => unreachable,
426 };
427 try r.write(small_buf[0..num_bytes_read]);
428 read_any_data = true;
429 }
430}
436 // We have to wait for the handle to be signalled, i.e. for the cancellation to complete.
437 switch (windows.kernel32.WaitForSingleObject(handle, windows.INFINITE)) {
438 windows.WAIT_OBJECT_0 => {},
439 windows.WAIT_FAILED => return windows.unexpectedError(windows.GetLastError()),
440 else => unreachable,
441 }
431442
432/// Simple wrapper around `GetOverlappedResult` to determine the result of a `ReadFile` operation.
433/// If `!allow_aborted`, then `aborted` is never returned (`OPERATION_ABORTED` is considered unexpected).
434///
435/// The `ReadFile` documentation states that the number of bytes read by an overlapped `ReadFile` must be determined using `GetOverlappedResult`, even if the
436/// operation immediately returns data:
437/// "Use NULL for [lpNumberOfBytesRead] if this is an asynchronous operation to avoid potentially
438/// erroneous results."
439/// "If `hFile` was opened with `FILE_FLAG_OVERLAPPED`, the following conditions are in effect: [...]
440/// The lpNumberOfBytesRead parameter should be set to NULL. Use the GetOverlappedResult function to
441/// get the actual number of bytes read."
442/// See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile
443fn windowsGetReadResult(
444 handle: windows.HANDLE,
445 overlapped: *windows.OVERLAPPED,
446 allow_aborted: bool,
447) !union(enum) {
448 success: u32,
449 closed,
450 aborted,
451} {
452 var num_bytes_read: u32 = undefined;
453 if (0 == windows.kernel32.GetOverlappedResult(
454 handle,
455 overlapped,
456 &num_bytes_read,
457 0,
458 )) switch (windows.GetLastError()) {
459 .BROKEN_PIPE => return .closed,
460 .OPERATION_ABORTED => |err| if (allow_aborted) {
461 return .aborted;
462 } else {
463 return windows.unexpectedError(err);
464 },
465 else => |err| return windows.unexpectedError(err),
443 // If it completed before we canceled, make sure to tell the FIFO!
444 const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, true)) {
445 .success => |n| n,
446 .closed => return if (read_any_data) .closed_populated else .closed,
447 .aborted => break :cancel_read,
448 };
449 read_any_data = true;
450 advanceBufferEnd(r, num_bytes_read);
451 }
452
453 // Try to queue the 1-byte read.
454 if (0 == windows.kernel32.ReadFile(
455 handle,
456 small_buf,
457 small_buf.len,
458 &win_dummy_bytes_read,
459 overlapped,
460 )) switch (windows.GetLastError()) {
461 .IO_PENDING => {
462 // 1-byte read pending as intended
463 return if (read_any_data) .populated else .empty;
464 },
465 .BROKEN_PIPE => return if (read_any_data) .closed_populated else .closed,
466 else => |err| return windows.unexpectedError(err),
467 };
468
469 // We got data back this time. Write it to the FIFO and run the main loop again.
470 const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) {
471 .success => |n| n,
472 .closed => return if (read_any_data) .closed_populated else .closed,
473 .aborted => unreachable,
474 };
475 const buf = small_buf[0..num_bytes_read];
476 const dest = try writableSliceGreedyAlloc(r, gpa, buf.len);
477 @memcpy(dest[0..buf.len], buf);
478 advanceBufferEnd(r, buf.len);
479 read_any_data = true;
480 }
481 }
482
483 /// Simple wrapper around `GetOverlappedResult` to determine the result of a `ReadFile` operation.
484 /// If `!allow_aborted`, then `aborted` is never returned (`OPERATION_ABORTED` is considered unexpected).
485 ///
486 /// The `ReadFile` documentation states that the number of bytes read by an overlapped `ReadFile` must be determined using `GetOverlappedResult`, even if the
487 /// operation immediately returns data:
488 /// "Use NULL for [lpNumberOfBytesRead] if this is an asynchronous operation to avoid potentially
489 /// erroneous results."
490 /// "If `hFile` was opened with `FILE_FLAG_OVERLAPPED`, the following conditions are in effect: [...]
491 /// The lpNumberOfBytesRead parameter should be set to NULL. Use the GetOverlappedResult function to
492 /// get the actual number of bytes read."
493 /// See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile
494 fn windowsGetReadResult(
495 handle: windows.HANDLE,
496 overlapped: *windows.OVERLAPPED,
497 allow_aborted: bool,
498 ) !union(enum) {
499 success: u32,
500 closed,
501 aborted,
502 } {
503 var num_bytes_read: u32 = undefined;
504 if (0 == windows.kernel32.GetOverlappedResult(
505 handle,
506 overlapped,
507 &num_bytes_read,
508 0,
509 )) switch (windows.GetLastError()) {
510 .BROKEN_PIPE => return .closed,
511 .OPERATION_ABORTED => |err| if (allow_aborted) {
512 return .aborted;
513 } else {
514 return windows.unexpectedError(err);
515 },
516 else => |err| return windows.unexpectedError(err),
517 };
518 return .{ .success = num_bytes_read };
519 }
466520 };
467 return .{ .success = num_bytes_read };
468521}
469522
470523/// Given an enum, returns a struct with fields of that enum, each field
lib/std/Io/Reader.zig-31
......@@ -1241,37 +1241,6 @@ pub fn fillAlloc(r: *Reader, allocator: Allocator, n: usize) FillAllocError!void
12411241 return fill(r, n);
12421242}
12431243
1244/// Returns a slice into the unused capacity of `buffer` with at least
1245/// `min_len` bytes, extending `buffer` by resizing it with `gpa` as necessary.
1246///
1247/// After calling this function, typically the caller will follow up with a
1248/// call to `advanceBufferEnd` to report the actual number of bytes buffered.
1249pub fn writableSliceGreedyAlloc(r: *Reader, allocator: Allocator, min_len: usize) Allocator.Error![]u8 {
1250 {
1251 const unused = r.buffer[r.end..];
1252 if (unused.len >= min_len) return unused;
1253 }
1254 if (r.seek > 0) rebase(r);
1255 {
1256 var list: ArrayList(u8) = .{
1257 .items = r.buffer[0..r.end],
1258 .capacity = r.buffer.len,
1259 };
1260 defer r.buffer = list.allocatedSlice();
1261 try list.ensureUnusedCapacity(allocator, min_len);
1262 }
1263 const unused = r.buffer[r.end..];
1264 assert(unused.len >= min_len);
1265 return unused;
1266}
1267
1268/// After writing directly into the unused capacity of `buffer`, this function
1269/// updates `end` so that users of `Reader` can receive the data.
1270pub fn advanceBufferEnd(r: *Reader, n: usize) void {
1271 assert(n <= r.buffer.len - r.end);
1272 r.end += n;
1273}
1274
12751244fn takeMultipleOf7Leb128(r: *Reader, comptime Result: type) TakeLeb128Error!Result {
12761245 const result_info = @typeInfo(Result).int;
12771246 comptime assert(result_info.bits % 7 == 0);
lib/std/c.zig+1-1
......@@ -7147,7 +7147,7 @@ pub const dirent = switch (native_os) {
71477147 off: off_t,
71487148 reclen: c_ushort,
71497149 type: u8,
7150 name: [256:0]u8,
7150 name: [255:0]u8,
71517151 },
71527152 else => void,
71537153};
lib/std/posix.zig+21-4
......@@ -192,10 +192,27 @@ pub const iovec_const = extern struct {
192192 len: usize,
193193};
194194
195pub const ACCMODE = enum(u2) {
196 RDONLY = 0,
197 WRONLY = 1,
198 RDWR = 2,
195pub const ACCMODE = switch (native_os) {
196 // POSIX has a note about the access mode values:
197 //
198 // In historical implementations the value of O_RDONLY is zero. Because of
199 // that, it is not possible to detect the presence of O_RDONLY and another
200 // option. Future implementations should encode O_RDONLY and O_WRONLY as
201 // bit flags so that: O_RDONLY | O_WRONLY == O_RDWR
202 //
203 // In practice SerenityOS is the only system supported by Zig that
204 // implements this suggestion.
205 // https://github.com/SerenityOS/serenity/blob/4adc51fdf6af7d50679c48b39362e062f5a3b2cb/Kernel/API/POSIX/fcntl.h#L28-L30
206 .serenity => enum(u2) {
207 RDONLY = 1,
208 WRONLY = 2,
209 RDWR = 3,
210 },
211 else => enum(u2) {
212 RDONLY = 0,
213 WRONLY = 1,
214 RDWR = 2,
215 },
199216};
200217
201218pub const TCSA = enum(c_uint) {
lib/std/process/Child.zig+38-29
......@@ -14,6 +14,7 @@ const assert = std.debug.assert;
1414const native_os = builtin.os.tag;
1515const Allocator = std.mem.Allocator;
1616const ChildProcess = @This();
17const ArrayList = std.ArrayListUnmanaged;
1718
1819pub const Id = switch (native_os) {
1920 .windows => windows.HANDLE,
......@@ -348,18 +349,6 @@ pub const RunResult = struct {
348349 stderr: []u8,
349350};
350351
351fn writeBufferedReaderToArrayList(allocator: Allocator, list: *std.ArrayListUnmanaged(u8), r: *std.Io.Reader) !void {
352 assert(r.seek == 0);
353 if (list.capacity == 0) {
354 list.* = .{
355 .items = r.buffered(),
356 .capacity = r.buffer.len,
357 };
358 } else {
359 try list.appendSlice(allocator, r.buffered());
360 }
361}
362
363352/// Collect the output from the process's stdout and stderr. Will return once all output
364353/// has been collected. This does not mean that the process has ended. `wait` should still
365354/// be called to wait for and clean up the process.
......@@ -369,8 +358,8 @@ pub fn collectOutput(
369358 child: ChildProcess,
370359 /// Used for `stdout` and `stderr`.
371360 allocator: Allocator,
372 stdout: *std.ArrayListUnmanaged(u8),
373 stderr: *std.ArrayListUnmanaged(u8),
361 stdout: *ArrayList(u8),
362 stderr: *ArrayList(u8),
374363 max_output_bytes: usize,
375364) !void {
376365 assert(child.stdout_behavior == .Pipe);
......@@ -382,15 +371,35 @@ pub fn collectOutput(
382371 });
383372 defer poller.deinit();
384373
374 const stdout_r = poller.reader(.stdout);
375 stdout_r.buffer = stdout.allocatedSlice();
376 stdout_r.seek = 0;
377 stdout_r.end = stdout.items.len;
378
379 const stderr_r = poller.reader(.stderr);
380 stderr_r.buffer = stderr.allocatedSlice();
381 stderr_r.seek = 0;
382 stderr_r.end = stderr.items.len;
383
384 defer {
385 stdout.* = .{
386 .items = stdout_r.buffer[0..stdout_r.end],
387 .capacity = stdout_r.buffer.len,
388 };
389 stderr.* = .{
390 .items = stderr_r.buffer[0..stderr_r.end],
391 .capacity = stderr_r.buffer.len,
392 };
393 stdout_r.buffer = &.{};
394 stderr_r.buffer = &.{};
395 }
396
385397 while (try poller.poll()) {
386 if (poller.reader(.stdout).bufferedLen() > max_output_bytes)
398 if (stdout_r.bufferedLen() > max_output_bytes)
387399 return error.StdoutStreamTooLong;
388 if (poller.reader(.stderr).bufferedLen() > max_output_bytes)
400 if (stderr_r.bufferedLen() > max_output_bytes)
389401 return error.StderrStreamTooLong;
390402 }
391
392 try writeBufferedReaderToArrayList(allocator, stdout, poller.reader(.stdout));
393 try writeBufferedReaderToArrayList(allocator, stderr, poller.reader(.stderr));
394403}
395404
396405pub const RunError = posix.GetCwdError || posix.ReadError || SpawnError || posix.PollError || error{
......@@ -420,10 +429,10 @@ pub fn run(args: struct {
420429 child.expand_arg0 = args.expand_arg0;
421430 child.progress_node = args.progress_node;
422431
423 var stdout: std.ArrayListUnmanaged(u8) = .empty;
424 errdefer stdout.deinit(args.allocator);
425 var stderr: std.ArrayListUnmanaged(u8) = .empty;
426 errdefer stderr.deinit(args.allocator);
432 var stdout: ArrayList(u8) = .empty;
433 defer stdout.deinit(args.allocator);
434 var stderr: ArrayList(u8) = .empty;
435 defer stderr.deinit(args.allocator);
427436
428437 try child.spawn();
429438 errdefer {
......@@ -431,7 +440,7 @@ pub fn run(args: struct {
431440 }
432441 try child.collectOutput(args.allocator, &stdout, &stderr, args.max_output_bytes);
433442
434 return RunResult{
443 return .{
435444 .stdout = try stdout.toOwnedSlice(args.allocator),
436445 .stderr = try stderr.toOwnedSlice(args.allocator),
437446 .term = try child.wait(),
......@@ -877,12 +886,12 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void {
877886 var cmd_line_cache = WindowsCommandLineCache.init(self.allocator, self.argv);
878887 defer cmd_line_cache.deinit();
879888
880 var app_buf: std.ArrayListUnmanaged(u16) = .empty;
889 var app_buf: ArrayList(u16) = .empty;
881890 defer app_buf.deinit(self.allocator);
882891
883892 try app_buf.appendSlice(self.allocator, app_name_w);
884893
885 var dir_buf: std.ArrayListUnmanaged(u16) = .empty;
894 var dir_buf: ArrayList(u16) = .empty;
886895 defer dir_buf.deinit(self.allocator);
887896
888897 if (cwd_path_w.len > 0) {
......@@ -1022,8 +1031,8 @@ const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8);
10221031/// Note: If the dir is the cwd, dir_buf should be empty (len = 0).
10231032fn windowsCreateProcessPathExt(
10241033 allocator: mem.Allocator,
1025 dir_buf: *std.ArrayListUnmanaged(u16),
1026 app_buf: *std.ArrayListUnmanaged(u16),
1034 dir_buf: *ArrayList(u16),
1035 app_buf: *ArrayList(u16),
10271036 pathext: [:0]const u16,
10281037 cmd_line_cache: *WindowsCommandLineCache,
10291038 envp_ptr: ?[*]u16,
......@@ -1506,7 +1515,7 @@ const WindowsCommandLineCache = struct {
15061515/// Returns the absolute path of `cmd.exe` within the Windows system directory.
15071516/// The caller owns the returned slice.
15081517fn windowsCmdExePath(allocator: mem.Allocator) error{ OutOfMemory, Unexpected }![:0]u16 {
1509 var buf = try std.ArrayListUnmanaged(u16).initCapacity(allocator, 128);
1518 var buf = try ArrayList(u16).initCapacity(allocator, 128);
15101519 errdefer buf.deinit(allocator);
15111520 while (true) {
15121521 const unused_slice = buf.unusedCapacitySlice();
src/Compilation.zig+11-13
......@@ -6215,19 +6215,20 @@ fn spawnZigRc(
62156215 return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv[0], @errorName(err) });
62166216 };
62176217
6218 var poller = std.io.poll(comp.gpa, enum { stdout }, .{
6218 var poller = std.Io.poll(comp.gpa, enum { stdout, stderr }, .{
62196219 .stdout = child.stdout.?,
6220 .stderr = child.stderr.?,
62206221 });
62216222 defer poller.deinit();
62226223
6223 const stdout = poller.fifo(.stdout);
6224 const stdout = poller.reader(.stdout);
62246225
62256226 poll: while (true) {
6226 while (stdout.readableLength() < @sizeOf(std.zig.Server.Message.Header)) if (!try poller.poll()) break :poll;
6227 var header: std.zig.Server.Message.Header = undefined;
6228 assert(stdout.read(std.mem.asBytes(&header)) == @sizeOf(std.zig.Server.Message.Header));
6229 while (stdout.readableLength() < header.bytes_len) if (!try poller.poll()) break :poll;
6230 const body = stdout.readableSliceOfLen(header.bytes_len);
6227 const MessageHeader = std.zig.Server.Message.Header;
6228 while (stdout.buffered().len < @sizeOf(MessageHeader)) if (!try poller.poll()) break :poll;
6229 const header = stdout.takeStruct(MessageHeader, .little) catch unreachable;
6230 while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll;
6231 const body = stdout.take(header.bytes_len) catch unreachable;
62316232
62326233 switch (header.tag) {
62336234 // We expect exactly one ErrorBundle, and if any error_bundle header is
......@@ -6250,13 +6251,10 @@ fn spawnZigRc(
62506251 },
62516252 else => {}, // ignore other messages
62526253 }
6253
6254 stdout.discard(body.len);
62556254 }
62566255
62576256 // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace)
6258 const stderr_reader = child.stderr.?.deprecatedReader();
6259 const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024);
6257 const stderr = poller.reader(.stderr);
62606258
62616259 const term = child.wait() catch |err| {
62626260 return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv[0], @errorName(err) });
......@@ -6265,12 +6263,12 @@ fn spawnZigRc(
62656263 switch (term) {
62666264 .Exited => |code| {
62676265 if (code != 0) {
6268 log.err("zig rc failed with stderr:\n{s}", .{stderr});
6266 log.err("zig rc failed with stderr:\n{s}", .{stderr.buffered()});
62696267 return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code});
62706268 }
62716269 },
62726270 else => {
6273 log.err("zig rc terminated with stderr:\n{s}", .{stderr});
6271 log.err("zig rc terminated with stderr:\n{s}", .{stderr.buffered()});
62746272 return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{});
62756273 },
62766274 }
src/arch/wasm/CodeGen.zig+9-12
......@@ -1887,8 +1887,10 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
18871887 .call_never_tail => cg.airCall(inst, .never_tail),
18881888 .call_never_inline => cg.airCall(inst, .never_inline),
18891889
1890 .is_err => cg.airIsErr(inst, .i32_ne),
1891 .is_non_err => cg.airIsErr(inst, .i32_eq),
1890 .is_err => cg.airIsErr(inst, .i32_ne, .value),
1891 .is_non_err => cg.airIsErr(inst, .i32_eq, .value),
1892 .is_err_ptr => cg.airIsErr(inst, .i32_ne, .ptr),
1893 .is_non_err_ptr => cg.airIsErr(inst, .i32_eq, .ptr),
18921894
18931895 .is_null => cg.airIsNull(inst, .i32_eq, .value),
18941896 .is_non_null => cg.airIsNull(inst, .i32_ne, .value),
......@@ -1971,8 +1973,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
19711973 .runtime_nav_ptr => cg.airRuntimeNavPtr(inst),
19721974
19731975 .assembly,
1974 .is_err_ptr,
1975 .is_non_err_ptr,
19761976
19771977 .err_return_trace,
19781978 .set_err_return_trace,
......@@ -4106,7 +4106,7 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
41064106 return cg.finishAir(inst, .none, &.{br.operand});
41074107}
41084108
4109fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerError!void {
4109fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void {
41104110 const zcu = cg.pt.zcu;
41114111 const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
41124112 const operand = try cg.resolveInst(un_op);
......@@ -4123,7 +4123,7 @@ fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerEr
41234123 }
41244124
41254125 try cg.emitWValue(operand);
4126 if (pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
4126 if (op_kind == .ptr or pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
41274127 try cg.addMemArg(.i32_load16_u, .{
41284128 .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))),
41294129 .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?),
......@@ -6463,9 +6463,6 @@ fn lowerTry(
64636463 operand_is_ptr: bool,
64646464) InnerError!WValue {
64656465 const zcu = cg.pt.zcu;
6466 if (operand_is_ptr) {
6467 return cg.fail("TODO: lowerTry for pointers", .{});
6468 }
64696466
64706467 const pl_ty = err_union_ty.errorUnionPayload(zcu);
64716468 const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime(zcu);
......@@ -6476,7 +6473,7 @@ fn lowerTry(
64766473
64776474 // check if the error tag is set for the error union.
64786475 try cg.emitWValue(err_union);
6479 if (pl_has_bits) {
6476 if (pl_has_bits or operand_is_ptr) {
64806477 const err_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu));
64816478 try cg.addMemArg(.i32_load16_u, .{
64826479 .offset = err_union.offset() + err_offset,
......@@ -6498,12 +6495,12 @@ fn lowerTry(
64986495 }
64996496
65006497 // if we reach here it means error was not set, and we want the payload
6501 if (!pl_has_bits) {
6498 if (!pl_has_bits and !operand_is_ptr) {
65026499 return .none;
65036500 }
65046501
65056502 const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu));
6506 if (isByRef(pl_ty, zcu, cg.target)) {
6503 if (operand_is_ptr or isByRef(pl_ty, zcu, cg.target)) {
65076504 return buildPointerOffset(cg, err_union, pl_offset, .new);
65086505 }
65096506 const payload = try cg.load(err_union, pl_ty, pl_offset);
src/target.zig+2
......@@ -414,6 +414,8 @@ pub fn libcFullLinkFlags(target: *const std.Target) []const []const u8 {
414414 .android, .androideabi, .ohos, .ohoseabi => &.{ "-lm", "-lc", "-ldl" },
415415 else => &.{ "-lm", "-lpthread", "-lc", "-ldl", "-lrt", "-lutil" },
416416 },
417 // On SerenityOS libc includes libm, libpthread, libdl, and libssp.
418 .serenity => &.{"-lc"},
417419 else => &.{},
418420 };
419421 return result;
test/behavior/try.zig+79
......@@ -121,3 +121,82 @@ test "'return try' through conditional" {
121121 comptime std.debug.assert(result == 123);
122122 }
123123}
124
125test "try ptr propagation const" {
126 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
127 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
128 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
129 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
130 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
131
132 const S = struct {
133 fn foo0() !u32 {
134 return 0;
135 }
136
137 fn foo1() error{Bad}!u32 {
138 return 1;
139 }
140
141 fn foo2() anyerror!u32 {
142 return 2;
143 }
144
145 fn doTheTest() !void {
146 const res0: *const u32 = &(try foo0());
147 const res1: *const u32 = &(try foo1());
148 const res2: *const u32 = &(try foo2());
149 try expect(res0.* == 0);
150 try expect(res1.* == 1);
151 try expect(res2.* == 2);
152 }
153 };
154 try S.doTheTest();
155 try comptime S.doTheTest();
156}
157
158test "try ptr propagation mutate" {
159 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
160 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
161 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
162 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
163 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
164
165 const S = struct {
166 fn foo0() !u32 {
167 return 0;
168 }
169
170 fn foo1() error{Bad}!u32 {
171 return 1;
172 }
173
174 fn foo2() anyerror!u32 {
175 return 2;
176 }
177
178 fn doTheTest() !void {
179 var f0 = foo0();
180 var f1 = foo1();
181 var f2 = foo2();
182
183 const res0: *u32 = &(try f0);
184 const res1: *u32 = &(try f1);
185 const res2: *u32 = &(try f2);
186
187 res0.* += 1;
188 res1.* += 1;
189 res2.* += 1;
190
191 try expect(f0 catch unreachable == 1);
192 try expect(f1 catch unreachable == 2);
193 try expect(f2 catch unreachable == 3);
194
195 try expect(res0.* == 1);
196 try expect(res1.* == 2);
197 try expect(res2.* == 3);
198 }
199 };
200 try S.doTheTest();
201 try comptime S.doTheTest();
202}
test/src/Cases.zig+2
......@@ -800,6 +800,8 @@ const TestManifestConfigDefaults = struct {
800800 }
801801 // Windows
802802 defaults = defaults ++ "x86_64-windows" ++ ",";
803 // Wasm
804 defaults = defaults ++ "wasm32-wasi";
803805 break :blk defaults;
804806 };
805807 } else if (std.mem.eql(u8, key, "output_mode")) {
test/tests.zig+9-10
......@@ -1369,16 +1369,15 @@ const test_targets = blk: {
13691369
13701370 // WASI Targets
13711371
1372 // TODO: lowerTry for pointers
1373 //.{
1374 // .target = .{
1375 // .cpu_arch = .wasm32,
1376 // .os_tag = .wasi,
1377 // .abi = .none,
1378 // },
1379 // .use_llvm = false,
1380 // .use_lld = false,
1381 //},
1372 .{
1373 .target = .{
1374 .cpu_arch = .wasm32,
1375 .os_tag = .wasi,
1376 .abi = .none,
1377 },
1378 .use_llvm = false,
1379 .use_lld = false,
1380 },
13821381 .{
13831382 .target = .{
13841383 .cpu_arch = .wasm32,
tools/docgen.zig-1
......@@ -3,7 +3,6 @@ const builtin = @import("builtin");
33const io = std.io;
44const fs = std.fs;
55const process = std.process;
6const ChildProcess = std.process.Child;
76const Progress = std.Progress;
87const print = std.debug.print;
98const mem = std.mem;
tools/incr-check.zig+24-39
......@@ -186,7 +186,7 @@ pub fn main() !void {
186186
187187 try child.spawn();
188188
189 var poller = std.io.poll(arena, Eval.StreamEnum, .{
189 var poller = std.Io.poll(arena, Eval.StreamEnum, .{
190190 .stdout = child.stdout.?,
191191 .stderr = child.stderr.?,
192192 });
......@@ -247,19 +247,15 @@ const Eval = struct {
247247
248248 fn check(eval: *Eval, poller: *Poller, update: Case.Update, prog_node: std.Progress.Node) !void {
249249 const arena = eval.arena;
250 const Header = std.zig.Server.Message.Header;
251 const stdout = poller.fifo(.stdout);
252 const stderr = poller.fifo(.stderr);
250 const stdout = poller.reader(.stdout);
251 const stderr = poller.reader(.stderr);
253252
254253 poll: while (true) {
255 while (stdout.readableLength() < @sizeOf(Header)) {
256 if (!(try poller.poll())) break :poll;
257 }
258 const header = stdout.reader().readStruct(Header) catch unreachable;
259 while (stdout.readableLength() < header.bytes_len) {
260 if (!(try poller.poll())) break :poll;
261 }
262 const body = stdout.readableSliceOfLen(header.bytes_len);
254 const Header = std.zig.Server.Message.Header;
255 while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll;
256 const header = stdout.takeStruct(Header, .little) catch unreachable;
257 while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll;
258 const body = stdout.take(header.bytes_len) catch unreachable;
263259
264260 switch (header.tag) {
265261 .error_bundle => {
......@@ -277,8 +273,8 @@ const Eval = struct {
277273 .string_bytes = try arena.dupe(u8, string_bytes),
278274 .extra = extra_array,
279275 };
280 if (stderr.readableLength() > 0) {
281 const stderr_data = try stderr.toOwnedSlice();
276 if (stderr.bufferedLen() > 0) {
277 const stderr_data = try poller.toOwnedSlice(.stderr);
282278 if (eval.allow_stderr) {
283279 std.log.info("error_bundle included stderr:\n{s}", .{stderr_data});
284280 } else {
......@@ -289,15 +285,14 @@ const Eval = struct {
289285 try eval.checkErrorOutcome(update, result_error_bundle);
290286 }
291287 // This message indicates the end of the update.
292 stdout.discard(body.len);
293288 return;
294289 },
295290 .emit_digest => {
296291 const EbpHdr = std.zig.Server.Message.EmitDigest;
297292 const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body));
298293 _ = ebp_hdr;
299 if (stderr.readableLength() > 0) {
300 const stderr_data = try stderr.toOwnedSlice();
294 if (stderr.bufferedLen() > 0) {
295 const stderr_data = try poller.toOwnedSlice(.stderr);
301296 if (eval.allow_stderr) {
302297 std.log.info("emit_digest included stderr:\n{s}", .{stderr_data});
303298 } else {
......@@ -308,7 +303,6 @@ const Eval = struct {
308303 if (eval.target.backend == .sema) {
309304 try eval.checkSuccessOutcome(update, null, prog_node);
310305 // This message indicates the end of the update.
311 stdout.discard(body.len);
312306 }
313307
314308 const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len];
......@@ -323,21 +317,18 @@ const Eval = struct {
323317
324318 try eval.checkSuccessOutcome(update, bin_path, prog_node);
325319 // This message indicates the end of the update.
326 stdout.discard(body.len);
327320 },
328321 else => {
329322 // Ignore other messages.
330 stdout.discard(body.len);
331323 },
332324 }
333325 }
334326
335 if (stderr.readableLength() > 0) {
336 const stderr_data = try stderr.toOwnedSlice();
327 if (stderr.bufferedLen() > 0) {
337328 if (eval.allow_stderr) {
338 std.log.info("update '{s}' included stderr:\n{s}", .{ update.name, stderr_data });
329 std.log.info("update '{s}' included stderr:\n{s}", .{ update.name, stderr.buffered() });
339330 } else {
340 eval.fatal("update '{s}' failed:\n{s}", .{ update.name, stderr_data });
331 eval.fatal("update '{s}' failed:\n{s}", .{ update.name, stderr.buffered() });
341332 }
342333 }
343334
......@@ -537,25 +528,19 @@ const Eval = struct {
537528 fn end(eval: *Eval, poller: *Poller) !void {
538529 requestExit(eval.child, eval);
539530
540 const Header = std.zig.Server.Message.Header;
541 const stdout = poller.fifo(.stdout);
542 const stderr = poller.fifo(.stderr);
531 const stdout = poller.reader(.stdout);
532 const stderr = poller.reader(.stderr);
543533
544534 poll: while (true) {
545 while (stdout.readableLength() < @sizeOf(Header)) {
546 if (!(try poller.poll())) break :poll;
547 }
548 const header = stdout.reader().readStruct(Header) catch unreachable;
549 while (stdout.readableLength() < header.bytes_len) {
550 if (!(try poller.poll())) break :poll;
551 }
552 const body = stdout.readableSliceOfLen(header.bytes_len);
553 stdout.discard(body.len);
535 const Header = std.zig.Server.Message.Header;
536 while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll;
537 const header = stdout.takeStruct(Header, .little) catch unreachable;
538 while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll;
539 stdout.toss(header.bytes_len);
554540 }
555541
556 if (stderr.readableLength() > 0) {
557 const stderr_data = try stderr.toOwnedSlice();
558 eval.fatal("unexpected stderr:\n{s}", .{stderr_data});
542 if (stderr.bufferedLen() > 0) {
543 eval.fatal("unexpected stderr:\n{s}", .{stderr.buffered()});
559544 }
560545 }
561546