| author | |
| committer | |
| log | 8c4482ed78fb651c0288f0cd2bdaf328564c6a49 |
| tree | efccaf6ea986a840f3cf4df64da174b7bdd18da5 |
| parent | dfbb6e9879693331c7b8ea57eb7aa8bdf934403f |
| parent | 4236ca40cd21895590c580c9a1f56423c2c2f167 |
16 files changed, 428 insertions(+), 315 deletions(-)
lib/std/Build/Fuzz/WebServer.zig+8-7| ... | @@ -282,13 +282,15 @@ fn buildWasmBinary( | ... | @@ -282,13 +282,15 @@ fn buildWasmBinary( |
| 282 | var result: ?Path = null; | 282 | var result: ?Path = null; |
| 283 | var result_error_bundle = std.zig.ErrorBundle.empty; | 283 | var result_error_bundle = std.zig.ErrorBundle.empty; |
| 284 | 284 | ||
| 285 | const stdout_br = poller.reader(.stdout); | 285 | const stdout = poller.reader(.stdout); |
| 286 | |||
| 286 | poll: while (true) { | 287 | poll: while (true) { |
| 287 | const Header = std.zig.Server.Message.Header; | 288 | const Header = std.zig.Server.Message.Header; |
| 288 | while (stdout_br.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll; | 289 | while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll; |
| 289 | const header = (stdout_br.takeStruct(Header) catch unreachable).*; | 290 | const header = stdout.takeStruct(Header, .little) catch unreachable; |
| 290 | while (stdout_br.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll; | 291 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll; |
| 291 | const body = stdout_br.take(header.bytes_len) catch unreachable; | 292 | const body = stdout.take(header.bytes_len) catch unreachable; |
| 293 | |||
| 292 | switch (header.tag) { | 294 | switch (header.tag) { |
| 293 | .zig_version => { | 295 | .zig_version => { |
| 294 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { | 296 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { |
| ... | @@ -327,8 +329,7 @@ fn buildWasmBinary( | ... | @@ -327,8 +329,7 @@ fn buildWasmBinary( |
| 327 | } | 329 | } |
| 328 | } | 330 | } |
| 329 | 331 | ||
| 330 | const stderr_br = poller.reader(.stderr); | 332 | const stderr_contents = try poller.toOwnedSlice(.stderr); |
| 331 | const stderr_contents = stderr_br.buffered(); | ||
| 332 | if (stderr_contents.len > 0) { | 333 | if (stderr_contents.len > 0) { |
| 333 | std.debug.print("{s}", .{stderr_contents}); | 334 | std.debug.print("{s}", .{stderr_contents}); |
| 334 | } | 335 | } |
lib/std/Build/Step.zig+17-16| ... | @@ -286,7 +286,7 @@ pub fn cast(step: *Step, comptime T: type) ?*T { | ... | @@ -286,7 +286,7 @@ pub fn cast(step: *Step, comptime T: type) ?*T { |
| 286 | } | 286 | } |
| 287 | 287 | ||
| 288 | /// For debugging purposes, prints identifying information about this Step. | 288 | /// For debugging purposes, prints identifying information about this Step. |
| 289 | pub fn dump(step: *Step, w: *std.io.Writer, tty_config: std.io.tty.Config) void { | 289 | pub fn dump(step: *Step, w: *std.Io.Writer, tty_config: std.Io.tty.Config) void { |
| 290 | const debug_info = std.debug.getSelfDebugInfo() catch |err| { | 290 | const debug_info = std.debug.getSelfDebugInfo() catch |err| { |
| 291 | w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{ | 291 | w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{ |
| 292 | @errorName(err), | 292 | @errorName(err), |
| ... | @@ -359,7 +359,7 @@ pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutO | ... | @@ -359,7 +359,7 @@ pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutO |
| 359 | 359 | ||
| 360 | pub const ZigProcess = struct { | 360 | pub const ZigProcess = struct { |
| 361 | child: std.process.Child, | 361 | child: std.process.Child, |
| 362 | poller: std.io.Poller(StreamEnum), | 362 | poller: std.Io.Poller(StreamEnum), |
| 363 | progress_ipc_fd: if (std.Progress.have_ipc) ?std.posix.fd_t else void, | 363 | progress_ipc_fd: if (std.Progress.have_ipc) ?std.posix.fd_t else void, |
| 364 | 364 | ||
| 365 | pub const StreamEnum = enum { stdout, stderr }; | 365 | pub const StreamEnum = enum { stdout, stderr }; |
| ... | @@ -428,7 +428,7 @@ pub fn evalZigProcess( | ... | @@ -428,7 +428,7 @@ pub fn evalZigProcess( |
| 428 | const zp = try gpa.create(ZigProcess); | 428 | const zp = try gpa.create(ZigProcess); |
| 429 | zp.* = .{ | 429 | zp.* = .{ |
| 430 | .child = child, | 430 | .child = child, |
| 431 | .poller = std.io.poll(gpa, ZigProcess.StreamEnum, .{ | 431 | .poller = std.Io.poll(gpa, ZigProcess.StreamEnum, .{ |
| 432 | .stdout = child.stdout.?, | 432 | .stdout = child.stdout.?, |
| 433 | .stderr = child.stderr.?, | 433 | .stderr = child.stderr.?, |
| 434 | }), | 434 | }), |
| ... | @@ -511,12 +511,14 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path { | ... | @@ -511,12 +511,14 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path { |
| 511 | var result: ?Path = null; | 511 | var result: ?Path = null; |
| 512 | 512 | ||
| 513 | const stdout = zp.poller.reader(.stdout); | 513 | const stdout = zp.poller.reader(.stdout); |
| 514 | |||
| 514 | poll: while (true) { | 515 | poll: while (true) { |
| 515 | const Header = std.zig.Server.Message.Header; | 516 | const Header = std.zig.Server.Message.Header; |
| 516 | while (stdout.buffered().len < @sizeOf(Header)) if (!try zp.poller.poll()) break :poll; | 517 | 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; |
| 518 | while (stdout.buffered().len < header.bytes_len) if (!try zp.poller.poll()) break :poll; | 519 | while (stdout.buffered().len < header.bytes_len) if (!try zp.poller.poll()) break :poll; |
| 519 | const body = stdout.take(header.bytes_len) catch unreachable; | 520 | const body = stdout.take(header.bytes_len) catch unreachable; |
| 521 | |||
| 520 | switch (header.tag) { | 522 | switch (header.tag) { |
| 521 | .zig_version => { | 523 | .zig_version => { |
| 522 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { | 524 | if (!std.mem.eql(u8, builtin.zig_version_string, body)) { |
| ... | @@ -606,8 +608,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path { | ... | @@ -606,8 +608,7 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path { |
| 606 | 608 | ||
| 607 | s.result_duration_ns = timer.read(); | 609 | s.result_duration_ns = timer.read(); |
| 608 | 610 | ||
| 609 | const stderr = zp.poller.reader(.stderr); | 611 | const stderr_contents = try zp.poller.toOwnedSlice(.stderr); |
| 610 | const stderr_contents = stderr.buffered(); | ||
| 611 | if (stderr_contents.len > 0) { | 612 | if (stderr_contents.len > 0) { |
| 612 | try s.result_error_msgs.append(arena, try arena.dupe(u8, stderr_contents)); | 613 | try s.result_error_msgs.append(arena, try arena.dupe(u8, stderr_contents)); |
| 613 | } | 614 | } |
| ... | @@ -726,7 +727,7 @@ pub fn allocPrintCmd2( | ... | @@ -726,7 +727,7 @@ pub fn allocPrintCmd2( |
| 726 | argv: []const []const u8, | 727 | argv: []const []const u8, |
| 727 | ) Allocator.Error![]u8 { | 728 | ) Allocator.Error![]u8 { |
| 728 | const shell = struct { | 729 | 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 { |
| 730 | for (string) |c| { | 731 | for (string) |c| { |
| 731 | if (switch (c) { | 732 | if (switch (c) { |
| 732 | else => true, | 733 | else => true, |
| ... | @@ -760,9 +761,9 @@ pub fn allocPrintCmd2( | ... | @@ -760,9 +761,9 @@ pub fn allocPrintCmd2( |
| 760 | } | 761 | } |
| 761 | }; | 762 | }; |
| 762 | 763 | ||
| 763 | var aw: std.io.Writer.Allocating = .init(arena); | 764 | var aw: std.Io.Writer.Allocating = .init(arena); |
| 764 | const writer = &aw.writer; | 765 | 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; |
| 766 | if (opt_env) |env| { | 767 | if (opt_env) |env| { |
| 767 | const process_env_map = std.process.getEnvMap(arena) catch std.process.EnvMap.init(arena); | 768 | const process_env_map = std.process.getEnvMap(arena) catch std.process.EnvMap.init(arena); |
| 768 | var it = env.iterator(); | 769 | var it = env.iterator(); |
| ... | @@ -772,17 +773,17 @@ pub fn allocPrintCmd2( | ... | @@ -772,17 +773,17 @@ pub fn allocPrintCmd2( |
| 772 | if (process_env_map.get(key)) |process_value| { | 773 | if (process_env_map.get(key)) |process_value| { |
| 773 | if (std.mem.eql(u8, value, process_value)) continue; | 774 | if (std.mem.eql(u8, value, process_value)) continue; |
| 774 | } | 775 | } |
| 775 | try writer.print(arena, "{s}=", .{key}); | 776 | writer.print("{s}=", .{key}) catch return error.OutOfMemory; |
| 776 | try shell.escape(writer, value, false); | 777 | shell.escape(writer, value, false) catch return error.OutOfMemory; |
| 777 | try writer.writeByte(arena, ' '); | 778 | writer.writeByte(' ') catch return error.OutOfMemory; |
| 778 | } | 779 | } |
| 779 | } | 780 | } |
| 780 | try shell.escape(writer, argv[0], true); | 781 | shell.escape(writer, argv[0], true) catch return error.OutOfMemory; |
| 781 | for (argv[1..]) |arg| { | 782 | for (argv[1..]) |arg| { |
| 782 | try writer.writeByte(arena, ' '); | 783 | writer.writeByte(' ') catch return error.OutOfMemory; |
| 783 | try shell.escape(writer, arg, false); | 784 | shell.escape(writer, arg, false) catch return error.OutOfMemory; |
| 784 | } | 785 | } |
| 785 | return aw.getWritten(); | 786 | return aw.toOwnedSlice(); |
| 786 | } | 787 | } |
| 787 | 788 | ||
| 788 | /// Prefer `cacheHitAndWatch` unless you already added watch inputs | 789 | /// Prefer `cacheHitAndWatch` unless you already added watch inputs |
lib/std/Build/Step/Run.zig+11-9| ... | @@ -1545,7 +1545,7 @@ fn evalZigTest( | ... | @@ -1545,7 +1545,7 @@ fn evalZigTest( |
| 1545 | const any_write_failed = first_write_failed or poll: while (true) { | 1545 | const any_write_failed = first_write_failed or poll: while (true) { |
| 1546 | const Header = std.zig.Server.Message.Header; | 1546 | const Header = std.zig.Server.Message.Header; |
| 1547 | while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll false; | 1547 | 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; |
| 1549 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll false; | 1549 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll false; |
| 1550 | const body = stdout.take(header.bytes_len) catch unreachable; | 1550 | const body = stdout.take(header.bytes_len) catch unreachable; |
| 1551 | switch (header.tag) { | 1551 | switch (header.tag) { |
| ... | @@ -1808,21 +1808,23 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult { | ... | @@ -1808,21 +1808,23 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !StdIoResult { |
| 1808 | } | 1808 | } |
| 1809 | } | 1809 | } |
| 1810 | 1810 | ||
| 1811 | stdout_bytes = poller.reader(.stdout).buffered(); | 1811 | stdout_bytes = try poller.toOwnedSlice(.stdout); |
| 1812 | stderr_bytes = poller.reader(.stderr).buffered(); | 1812 | stderr_bytes = try poller.toOwnedSlice(.stderr); |
| 1813 | } else { | 1813 | } else { |
| 1814 | var fr = stdout.readerStreaming(); | 1814 | var small_buffer: [1]u8 = undefined; |
| 1815 | stdout_bytes = fr.interface().allocRemaining(arena, run.stdio_limit) catch |err| switch (err) { | 1815 | var stdout_reader = stdout.readerStreaming(&small_buffer); |
| 1816 | stdout_bytes = stdout_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) { | ||
| 1816 | error.OutOfMemory => return error.OutOfMemory, | 1817 | error.OutOfMemory => return error.OutOfMemory, |
| 1817 | error.ReadFailed => return fr.err.?, | 1818 | error.ReadFailed => return stdout_reader.err.?, |
| 1818 | error.StreamTooLong => return error.StdoutStreamTooLong, | 1819 | error.StreamTooLong => return error.StdoutStreamTooLong, |
| 1819 | }; | 1820 | }; |
| 1820 | } | 1821 | } |
| 1821 | } else if (child.stderr) |stderr| { | 1822 | } else if (child.stderr) |stderr| { |
| 1822 | var fr = stderr.readerStreaming(); | 1823 | var small_buffer: [1]u8 = undefined; |
| 1823 | stderr_bytes = fr.interface().allocRemaining(arena, run.stdio_limit) catch |err| switch (err) { | 1824 | var stderr_reader = stderr.readerStreaming(&small_buffer); |
| 1825 | stderr_bytes = stderr_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) { | ||
| 1824 | error.OutOfMemory => return error.OutOfMemory, | 1826 | error.OutOfMemory => return error.OutOfMemory, |
| 1825 | error.ReadFailed => return fr.err.?, | 1827 | error.ReadFailed => return stderr_reader.err.?, |
| 1826 | error.StreamTooLong => return error.StderrStreamTooLong, | 1828 | error.StreamTooLong => return error.StderrStreamTooLong, |
| 1827 | }; | 1829 | }; |
| 1828 | } | 1830 | } |
lib/std/Io.zig+196-143| ... | @@ -92,12 +92,7 @@ pub fn poll( | ... | @@ -92,12 +92,7 @@ pub fn poll( |
| 92 | const enum_fields = @typeInfo(StreamEnum).@"enum".fields; | 92 | const enum_fields = @typeInfo(StreamEnum).@"enum".fields; |
| 93 | var result: Poller(StreamEnum) = .{ | 93 | var result: Poller(StreamEnum) = .{ |
| 94 | .gpa = gpa, | 94 | .gpa = gpa, |
| 95 | .readers = @splat(.{ | 95 | .readers = @splat(.failing), |
| 96 | .unbuffered_reader = .failing, | ||
| 97 | .buffer = &.{}, | ||
| 98 | .end = 0, | ||
| 99 | .seek = 0, | ||
| 100 | }), | ||
| 101 | .poll_fds = undefined, | 96 | .poll_fds = undefined, |
| 102 | .windows = if (is_windows) .{ | 97 | .windows = if (is_windows) .{ |
| 103 | .first_read_done = false, | 98 | .first_read_done = false, |
| ... | @@ -186,21 +181,40 @@ pub fn Poller(comptime StreamEnum: type) type { | ... | @@ -186,21 +181,40 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 186 | } | 181 | } |
| 187 | } | 182 | } |
| 188 | 183 | ||
| 189 | pub inline fn reader(self: *Self, comptime which: StreamEnum) *Reader { | 184 | pub fn reader(self: *Self, which: StreamEnum) *Reader { |
| 190 | return &self.readers[@intFromEnum(which)]; | 185 | return &self.readers[@intFromEnum(which)]; |
| 191 | } | 186 | } |
| 192 | 187 | ||
| 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 | |||
| 193 | fn pollWindows(self: *Self, nanoseconds: ?u64) !bool { | 205 | fn pollWindows(self: *Self, nanoseconds: ?u64) !bool { |
| 194 | const bump_amt = 512; | 206 | const bump_amt = 512; |
| 207 | const gpa = self.gpa; | ||
| 195 | 208 | ||
| 196 | if (!self.windows.first_read_done) { | 209 | if (!self.windows.first_read_done) { |
| 197 | var already_read_data = false; | 210 | var already_read_data = false; |
| 198 | for (0..enum_fields.len) |i| { | 211 | for (0..enum_fields.len) |i| { |
| 199 | const handle = self.windows.active.handles_buf[i]; | 212 | const handle = self.windows.active.handles_buf[i]; |
| 200 | switch (try windowsAsyncReadToFifoAndQueueSmallRead( | 213 | switch (try windowsAsyncReadToFifoAndQueueSmallRead( |
| 214 | gpa, | ||
| 201 | handle, | 215 | handle, |
| 202 | &self.windows.overlapped[i], | 216 | &self.windows.overlapped[i], |
| 203 | &self.fifos[i], | 217 | &self.readers[i], |
| 204 | &self.windows.small_bufs[i], | 218 | &self.windows.small_bufs[i], |
| 205 | bump_amt, | 219 | bump_amt, |
| 206 | )) { | 220 | )) { |
| ... | @@ -247,7 +261,7 @@ pub fn Poller(comptime StreamEnum: type) type { | ... | @@ -247,7 +261,7 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 247 | const handle = self.windows.active.handles_buf[active_idx]; | 261 | const handle = self.windows.active.handles_buf[active_idx]; |
| 248 | 262 | ||
| 249 | const overlapped = &self.windows.overlapped[stream_idx]; | 263 | const overlapped = &self.windows.overlapped[stream_idx]; |
| 250 | const stream_fifo = &self.fifos[stream_idx]; | 264 | const stream_reader = &self.readers[stream_idx]; |
| 251 | const small_buf = &self.windows.small_bufs[stream_idx]; | 265 | const small_buf = &self.windows.small_bufs[stream_idx]; |
| 252 | 266 | ||
| 253 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { | 267 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { |
| ... | @@ -258,12 +272,16 @@ pub fn Poller(comptime StreamEnum: type) type { | ... | @@ -258,12 +272,16 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 258 | }, | 272 | }, |
| 259 | .aborted => unreachable, | 273 | .aborted => unreachable, |
| 260 | }; | 274 | }; |
| 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); | ||
| 262 | 279 | ||
| 263 | switch (try windowsAsyncReadToFifoAndQueueSmallRead( | 280 | switch (try windowsAsyncReadToFifoAndQueueSmallRead( |
| 281 | gpa, | ||
| 264 | handle, | 282 | handle, |
| 265 | overlapped, | 283 | overlapped, |
| 266 | stream_fifo, | 284 | stream_reader, |
| 267 | small_buf, | 285 | small_buf, |
| 268 | bump_amt, | 286 | bump_amt, |
| 269 | )) { | 287 | )) { |
| ... | @@ -298,18 +316,18 @@ pub fn Poller(comptime StreamEnum: type) type { | ... | @@ -298,18 +316,18 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 298 | } | 316 | } |
| 299 | 317 | ||
| 300 | var keep_polling = false; | 318 | 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| { |
| 302 | // Try reading whatever is available before checking the error | 320 | // Try reading whatever is available before checking the error |
| 303 | // conditions. | 321 | // conditions. |
| 304 | // It's still possible to read after a POLL.HUP is received, | 322 | // It's still possible to read after a POLL.HUP is received, |
| 305 | // always check if there's some data waiting to be read first. | 323 | // always check if there's some data waiting to be read first. |
| 306 | if (poll_fd.revents & posix.POLL.IN != 0) { | 324 | 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); |
| 308 | const amt = posix.read(poll_fd.fd, buf) catch |err| switch (err) { | 326 | const amt = posix.read(poll_fd.fd, buf) catch |err| switch (err) { |
| 309 | error.BrokenPipe => 0, // Handle the same as EOF. | 327 | error.BrokenPipe => 0, // Handle the same as EOF. |
| 310 | else => |e| return e, | 328 | else => |e| return e, |
| 311 | }; | 329 | }; |
| 312 | r.advanceBufferEnd(amt); | 330 | advanceBufferEnd(r, amt); |
| 313 | if (amt == 0) { | 331 | if (amt == 0) { |
| 314 | // Remove the fd when the EOF condition is met. | 332 | // Remove the fd when the EOF condition is met. |
| 315 | poll_fd.fd = -1; | 333 | poll_fd.fd = -1; |
| ... | @@ -325,146 +343,181 @@ pub fn Poller(comptime StreamEnum: type) type { | ... | @@ -325,146 +343,181 @@ pub fn Poller(comptime StreamEnum: type) type { |
| 325 | } | 343 | } |
| 326 | return keep_polling; | 344 | return keep_polling; |
| 327 | } | 345 | } |
| 328 | }; | ||
| 329 | } | ||
| 330 | 346 | ||
| 331 | /// The `ReadFile` docuementation states that `lpNumberOfBytesRead` does not have a meaningful | 347 | /// Returns a slice into the unused capacity of `buffer` with at least |
| 332 | /// result when using overlapped I/O, but also that it cannot be `null` on Windows 7. For | 348 | /// `min_len` bytes, extending `buffer` by resizing it with `gpa` as necessary. |
| 333 | /// compatibility, we point it to this dummy variables, which we never otherwise access. | 349 | /// |
| 334 | /// See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile | 350 | /// After calling this function, typically the caller will follow up with a |
| 335 | var win_dummy_bytes_read: u32 = undefined; | 351 | /// call to `advanceBufferEnd` to report the actual number of bytes buffered. |
| 336 | 352 | fn writableSliceGreedyAlloc(r: *Reader, allocator: Allocator, min_len: usize) Allocator.Error![]u8 { | |
| 337 | /// Read as much data as possible from `handle` with `overlapped`, and write it to the FIFO. Before | 353 | { |
| 338 | /// returning, queue a read into `small_buf` so that `WaitForMultipleObjects` returns when more data | 354 | const unused = r.buffer[r.end..]; |
| 339 | /// is available. `handle` must have no pending asynchronous operation. | 355 | if (unused.len >= min_len) return unused; |
| 340 | fn windowsAsyncReadToFifoAndQueueSmallRead( | 356 | } |
| 341 | handle: windows.HANDLE, | 357 | if (r.seek > 0) r.rebase(); |
| 342 | overlapped: *windows.OVERLAPPED, | 358 | { |
| 343 | r: *Reader, | 359 | var list: std.ArrayListUnmanaged(u8) = .{ |
| 344 | small_buf: *[128]u8, | 360 | .items = r.buffer[0..r.end], |
| 345 | bump_amt: usize, | 361 | .capacity = r.buffer.len, |
| 346 | ) !enum { empty, populated, closed_populated, closed } { | 362 | }; |
| 347 | var read_any_data = false; | 363 | defer r.buffer = list.allocatedSlice(); |
| 348 | while (true) { | 364 | try list.ensureUnusedCapacity(allocator, min_len); |
| 349 | const fifo_read_pending = while (true) { | 365 | } |
| 350 | const buf = try r.writableWithSize(bump_amt); | 366 | const unused = r.buffer[r.end..]; |
| 351 | const buf_len = math.cast(u32, buf.len) orelse math.maxInt(u32); | 367 | assert(unused.len >= min_len); |
| 352 | 368 | return unused; | |
| 353 | if (0 == windows.kernel32.ReadFile( | 369 | } |
| 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 | }; | ||
| 364 | 370 | ||
| 365 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { | 371 | /// After writing directly into the unused capacity of `buffer`, this function |
| 366 | .success => |n| n, | 372 | /// updates `end` so that users of `Reader` can receive the data. |
| 367 | .closed => return if (read_any_data) .closed_populated else .closed, | 373 | fn advanceBufferEnd(r: *Reader, n: usize) void { |
| 368 | .aborted => unreachable, | 374 | assert(n <= r.buffer.len - r.end); |
| 369 | }; | 375 | r.end += n; |
| 376 | } | ||
| 370 | 377 | ||
| 371 | read_any_data = true; | 378 | /// The `ReadFile` docuementation states that `lpNumberOfBytesRead` does not have a meaningful |
| 372 | r.update(num_bytes_read); | 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); | ||
| 373 | 400 | ||
| 374 | if (num_bytes_read == buf_len) { | 401 | if (0 == windows.kernel32.ReadFile( |
| 375 | // We filled the buffer, so there's probably more data available. | 402 | handle, |
| 376 | continue; | 403 | buf.ptr, |
| 377 | } else { | 404 | buf_len, |
| 378 | // We didn't fill the buffer, so assume we're out of data. | 405 | &win_dummy_bytes_read, |
| 379 | // There is no pending read. | 406 | overlapped, |
| 380 | break false; | 407 | )) switch (windows.GetLastError()) { |
| 381 | } | 408 | .IO_PENDING => break true, |
| 382 | }; | 409 | .BROKEN_PIPE => return if (read_any_data) .closed_populated else .closed, |
| 410 | else => |err| return windows.unexpectedError(err), | ||
| 411 | }; | ||
| 383 | 412 | ||
| 384 | if (fifo_read_pending) cancel_read: { | 413 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { |
| 385 | // Cancel the pending read into the FIFO. | 414 | .success => |n| n, |
| 386 | _ = windows.kernel32.CancelIo(handle); | 415 | .closed => return if (read_any_data) .closed_populated else .closed, |
| 416 | .aborted => unreachable, | ||
| 417 | }; | ||
| 387 | 418 | ||
| 388 | // We have to wait for the handle to be signalled, i.e. for the cancellation to complete. | 419 | read_any_data = true; |
| 389 | switch (windows.kernel32.WaitForSingleObject(handle, windows.INFINITE)) { | 420 | advanceBufferEnd(r, num_bytes_read); |
| 390 | windows.WAIT_OBJECT_0 => {}, | ||
| 391 | windows.WAIT_FAILED => return windows.unexpectedError(windows.GetLastError()), | ||
| 392 | else => unreachable, | ||
| 393 | } | ||
| 394 | 421 | ||
| 395 | // If it completed before we canceled, make sure to tell the FIFO! | 422 | if (num_bytes_read == buf_len) { |
| 396 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, true)) { | 423 | // We filled the buffer, so there's probably more data available. |
| 397 | .success => |n| n, | 424 | continue; |
| 398 | .closed => return if (read_any_data) .closed_populated else .closed, | 425 | } else { |
| 399 | .aborted => break :cancel_read, | 426 | // We didn't fill the buffer, so assume we're out of data. |
| 400 | }; | 427 | // There is no pending read. |
| 401 | read_any_data = true; | 428 | break false; |
| 402 | r.update(num_bytes_read); | 429 | } |
| 403 | } | 430 | }; |
| 404 | 431 | ||
| 405 | // Try to queue the 1-byte read. | 432 | if (fifo_read_pending) cancel_read: { |
| 406 | if (0 == windows.kernel32.ReadFile( | 433 | // Cancel the pending read into the FIFO. |
| 407 | handle, | 434 | _ = windows.kernel32.CancelIo(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 | }; | ||
| 420 | 435 | ||
| 421 | // We got data back this time. Write it to the FIFO and run the main loop again. | 436 | // We have to wait for the handle to be signalled, i.e. for the cancellation to complete. |
| 422 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { | 437 | switch (windows.kernel32.WaitForSingleObject(handle, windows.INFINITE)) { |
| 423 | .success => |n| n, | 438 | windows.WAIT_OBJECT_0 => {}, |
| 424 | .closed => return if (read_any_data) .closed_populated else .closed, | 439 | windows.WAIT_FAILED => return windows.unexpectedError(windows.GetLastError()), |
| 425 | .aborted => unreachable, | 440 | else => unreachable, |
| 426 | }; | 441 | } |
| 427 | try r.write(small_buf[0..num_bytes_read]); | ||
| 428 | read_any_data = true; | ||
| 429 | } | ||
| 430 | } | ||
| 431 | 442 | ||
| 432 | /// Simple wrapper around `GetOverlappedResult` to determine the result of a `ReadFile` operation. | 443 | // If it completed before we canceled, make sure to tell the FIFO! |
| 433 | /// If `!allow_aborted`, then `aborted` is never returned (`OPERATION_ABORTED` is considered unexpected). | 444 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, true)) { |
| 434 | /// | 445 | .success => |n| n, |
| 435 | /// The `ReadFile` documentation states that the number of bytes read by an overlapped `ReadFile` must be determined using `GetOverlappedResult`, even if the | 446 | .closed => return if (read_any_data) .closed_populated else .closed, |
| 436 | /// operation immediately returns data: | 447 | .aborted => break :cancel_read, |
| 437 | /// "Use NULL for [lpNumberOfBytesRead] if this is an asynchronous operation to avoid potentially | 448 | }; |
| 438 | /// erroneous results." | 449 | read_any_data = true; |
| 439 | /// "If `hFile` was opened with `FILE_FLAG_OVERLAPPED`, the following conditions are in effect: [...] | 450 | advanceBufferEnd(r, num_bytes_read); |
| 440 | /// The lpNumberOfBytesRead parameter should be set to NULL. Use the GetOverlappedResult function to | 451 | } |
| 441 | /// get the actual number of bytes read." | 452 | |
| 442 | /// See: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfile | 453 | // Try to queue the 1-byte read. |
| 443 | fn windowsGetReadResult( | 454 | if (0 == windows.kernel32.ReadFile( |
| 444 | handle: windows.HANDLE, | 455 | handle, |
| 445 | overlapped: *windows.OVERLAPPED, | 456 | small_buf, |
| 446 | allow_aborted: bool, | 457 | small_buf.len, |
| 447 | ) !union(enum) { | 458 | &win_dummy_bytes_read, |
| 448 | success: u32, | 459 | overlapped, |
| 449 | closed, | 460 | )) switch (windows.GetLastError()) { |
| 450 | aborted, | 461 | .IO_PENDING => { |
| 451 | } { | 462 | // 1-byte read pending as intended |
| 452 | var num_bytes_read: u32 = undefined; | 463 | return if (read_any_data) .populated else .empty; |
| 453 | if (0 == windows.kernel32.GetOverlappedResult( | 464 | }, |
| 454 | handle, | 465 | .BROKEN_PIPE => return if (read_any_data) .closed_populated else .closed, |
| 455 | overlapped, | 466 | else => |err| return windows.unexpectedError(err), |
| 456 | &num_bytes_read, | 467 | }; |
| 457 | 0, | 468 | |
| 458 | )) switch (windows.GetLastError()) { | 469 | // We got data back this time. Write it to the FIFO and run the main loop again. |
| 459 | .BROKEN_PIPE => return .closed, | 470 | const num_bytes_read = switch (try windowsGetReadResult(handle, overlapped, false)) { |
| 460 | .OPERATION_ABORTED => |err| if (allow_aborted) { | 471 | .success => |n| n, |
| 461 | return .aborted; | 472 | .closed => return if (read_any_data) .closed_populated else .closed, |
| 462 | } else { | 473 | .aborted => unreachable, |
| 463 | return windows.unexpectedError(err); | 474 | }; |
| 464 | }, | 475 | const buf = small_buf[0..num_bytes_read]; |
| 465 | else => |err| return windows.unexpectedError(err), | 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 | } | ||
| 466 | }; | 520 | }; |
| 467 | return .{ .success = num_bytes_read }; | ||
| 468 | } | 521 | } |
| 469 | 522 | ||
| 470 | /// Given an enum, returns a struct with fields of that enum, each field | 523 | /// 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 | ... | @@ -1241,37 +1241,6 @@ pub fn fillAlloc(r: *Reader, allocator: Allocator, n: usize) FillAllocError!void |
| 1241 | return fill(r, n); | 1241 | return fill(r, n); |
| 1242 | } | 1242 | } |
| 1243 | 1243 | ||
| 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. | ||
| 1249 | pub 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. | ||
| 1270 | pub fn advanceBufferEnd(r: *Reader, n: usize) void { | ||
| 1271 | assert(n <= r.buffer.len - r.end); | ||
| 1272 | r.end += n; | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | fn takeMultipleOf7Leb128(r: *Reader, comptime Result: type) TakeLeb128Error!Result { | 1244 | fn takeMultipleOf7Leb128(r: *Reader, comptime Result: type) TakeLeb128Error!Result { |
| 1276 | const result_info = @typeInfo(Result).int; | 1245 | const result_info = @typeInfo(Result).int; |
| 1277 | comptime assert(result_info.bits % 7 == 0); | 1246 | comptime assert(result_info.bits % 7 == 0); |
lib/std/c.zig+1-1| ... | @@ -7147,7 +7147,7 @@ pub const dirent = switch (native_os) { | ... | @@ -7147,7 +7147,7 @@ pub const dirent = switch (native_os) { |
| 7147 | off: off_t, | 7147 | off: off_t, |
| 7148 | reclen: c_ushort, | 7148 | reclen: c_ushort, |
| 7149 | type: u8, | 7149 | type: u8, |
| 7150 | name: [256:0]u8, | 7150 | name: [255:0]u8, |
| 7151 | }, | 7151 | }, |
| 7152 | else => void, | 7152 | else => void, |
| 7153 | }; | 7153 | }; |
lib/std/posix.zig+21-4| ... | @@ -192,10 +192,27 @@ pub const iovec_const = extern struct { | ... | @@ -192,10 +192,27 @@ pub const iovec_const = extern struct { |
| 192 | len: usize, | 192 | len: usize, |
| 193 | }; | 193 | }; |
| 194 | 194 | ||
| 195 | pub const ACCMODE = enum(u2) { | 195 | pub const ACCMODE = switch (native_os) { |
| 196 | RDONLY = 0, | 196 | // POSIX has a note about the access mode values: |
| 197 | WRONLY = 1, | 197 | // |
| 198 | RDWR = 2, | 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 | }, | ||
| 199 | }; | 216 | }; |
| 200 | 217 | ||
| 201 | pub const TCSA = enum(c_uint) { | 218 | pub const TCSA = enum(c_uint) { |
lib/std/process/Child.zig+38-29| ... | @@ -14,6 +14,7 @@ const assert = std.debug.assert; | ... | @@ -14,6 +14,7 @@ const assert = std.debug.assert; |
| 14 | const native_os = builtin.os.tag; | 14 | const native_os = builtin.os.tag; |
| 15 | const Allocator = std.mem.Allocator; | 15 | const Allocator = std.mem.Allocator; |
| 16 | const ChildProcess = @This(); | 16 | const ChildProcess = @This(); |
| 17 | const ArrayList = std.ArrayListUnmanaged; | ||
| 17 | 18 | ||
| 18 | pub const Id = switch (native_os) { | 19 | pub const Id = switch (native_os) { |
| 19 | .windows => windows.HANDLE, | 20 | .windows => windows.HANDLE, |
| ... | @@ -348,18 +349,6 @@ pub const RunResult = struct { | ... | @@ -348,18 +349,6 @@ pub const RunResult = struct { |
| 348 | stderr: []u8, | 349 | stderr: []u8, |
| 349 | }; | 350 | }; |
| 350 | 351 | ||
| 351 | fn 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 | |||
| 363 | /// Collect the output from the process's stdout and stderr. Will return once all output | 352 | /// Collect the output from the process's stdout and stderr. Will return once all output |
| 364 | /// has been collected. This does not mean that the process has ended. `wait` should still | 353 | /// has been collected. This does not mean that the process has ended. `wait` should still |
| 365 | /// be called to wait for and clean up the process. | 354 | /// be called to wait for and clean up the process. |
| ... | @@ -369,8 +358,8 @@ pub fn collectOutput( | ... | @@ -369,8 +358,8 @@ pub fn collectOutput( |
| 369 | child: ChildProcess, | 358 | child: ChildProcess, |
| 370 | /// Used for `stdout` and `stderr`. | 359 | /// Used for `stdout` and `stderr`. |
| 371 | allocator: Allocator, | 360 | allocator: Allocator, |
| 372 | stdout: *std.ArrayListUnmanaged(u8), | 361 | stdout: *ArrayList(u8), |
| 373 | stderr: *std.ArrayListUnmanaged(u8), | 362 | stderr: *ArrayList(u8), |
| 374 | max_output_bytes: usize, | 363 | max_output_bytes: usize, |
| 375 | ) !void { | 364 | ) !void { |
| 376 | assert(child.stdout_behavior == .Pipe); | 365 | assert(child.stdout_behavior == .Pipe); |
| ... | @@ -382,15 +371,35 @@ pub fn collectOutput( | ... | @@ -382,15 +371,35 @@ pub fn collectOutput( |
| 382 | }); | 371 | }); |
| 383 | defer poller.deinit(); | 372 | defer poller.deinit(); |
| 384 | 373 | ||
| 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 | |||
| 385 | while (try poller.poll()) { | 397 | while (try poller.poll()) { |
| 386 | if (poller.reader(.stdout).bufferedLen() > max_output_bytes) | 398 | if (stdout_r.bufferedLen() > max_output_bytes) |
| 387 | return error.StdoutStreamTooLong; | 399 | return error.StdoutStreamTooLong; |
| 388 | if (poller.reader(.stderr).bufferedLen() > max_output_bytes) | 400 | if (stderr_r.bufferedLen() > max_output_bytes) |
| 389 | return error.StderrStreamTooLong; | 401 | return error.StderrStreamTooLong; |
| 390 | } | 402 | } |
| 391 | |||
| 392 | try writeBufferedReaderToArrayList(allocator, stdout, poller.reader(.stdout)); | ||
| 393 | try writeBufferedReaderToArrayList(allocator, stderr, poller.reader(.stderr)); | ||
| 394 | } | 403 | } |
| 395 | 404 | ||
| 396 | pub const RunError = posix.GetCwdError || posix.ReadError || SpawnError || posix.PollError || error{ | 405 | pub const RunError = posix.GetCwdError || posix.ReadError || SpawnError || posix.PollError || error{ |
| ... | @@ -420,10 +429,10 @@ pub fn run(args: struct { | ... | @@ -420,10 +429,10 @@ pub fn run(args: struct { |
| 420 | child.expand_arg0 = args.expand_arg0; | 429 | child.expand_arg0 = args.expand_arg0; |
| 421 | child.progress_node = args.progress_node; | 430 | child.progress_node = args.progress_node; |
| 422 | 431 | ||
| 423 | var stdout: std.ArrayListUnmanaged(u8) = .empty; | 432 | var stdout: ArrayList(u8) = .empty; |
| 424 | errdefer stdout.deinit(args.allocator); | 433 | defer stdout.deinit(args.allocator); |
| 425 | var stderr: std.ArrayListUnmanaged(u8) = .empty; | 434 | var stderr: ArrayList(u8) = .empty; |
| 426 | errdefer stderr.deinit(args.allocator); | 435 | defer stderr.deinit(args.allocator); |
| 427 | 436 | ||
| 428 | try child.spawn(); | 437 | try child.spawn(); |
| 429 | errdefer { | 438 | errdefer { |
| ... | @@ -431,7 +440,7 @@ pub fn run(args: struct { | ... | @@ -431,7 +440,7 @@ pub fn run(args: struct { |
| 431 | } | 440 | } |
| 432 | try child.collectOutput(args.allocator, &stdout, &stderr, args.max_output_bytes); | 441 | try child.collectOutput(args.allocator, &stdout, &stderr, args.max_output_bytes); |
| 433 | 442 | ||
| 434 | return RunResult{ | 443 | return .{ |
| 435 | .stdout = try stdout.toOwnedSlice(args.allocator), | 444 | .stdout = try stdout.toOwnedSlice(args.allocator), |
| 436 | .stderr = try stderr.toOwnedSlice(args.allocator), | 445 | .stderr = try stderr.toOwnedSlice(args.allocator), |
| 437 | .term = try child.wait(), | 446 | .term = try child.wait(), |
| ... | @@ -877,12 +886,12 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void { | ... | @@ -877,12 +886,12 @@ fn spawnWindows(self: *ChildProcess) SpawnError!void { |
| 877 | var cmd_line_cache = WindowsCommandLineCache.init(self.allocator, self.argv); | 886 | var cmd_line_cache = WindowsCommandLineCache.init(self.allocator, self.argv); |
| 878 | defer cmd_line_cache.deinit(); | 887 | defer cmd_line_cache.deinit(); |
| 879 | 888 | ||
| 880 | var app_buf: std.ArrayListUnmanaged(u16) = .empty; | 889 | var app_buf: ArrayList(u16) = .empty; |
| 881 | defer app_buf.deinit(self.allocator); | 890 | defer app_buf.deinit(self.allocator); |
| 882 | 891 | ||
| 883 | try app_buf.appendSlice(self.allocator, app_name_w); | 892 | try app_buf.appendSlice(self.allocator, app_name_w); |
| 884 | 893 | ||
| 885 | var dir_buf: std.ArrayListUnmanaged(u16) = .empty; | 894 | var dir_buf: ArrayList(u16) = .empty; |
| 886 | defer dir_buf.deinit(self.allocator); | 895 | defer dir_buf.deinit(self.allocator); |
| 887 | 896 | ||
| 888 | if (cwd_path_w.len > 0) { | 897 | if (cwd_path_w.len > 0) { |
| ... | @@ -1022,8 +1031,8 @@ const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8); | ... | @@ -1022,8 +1031,8 @@ const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8); |
| 1022 | /// Note: If the dir is the cwd, dir_buf should be empty (len = 0). | 1031 | /// Note: If the dir is the cwd, dir_buf should be empty (len = 0). |
| 1023 | fn windowsCreateProcessPathExt( | 1032 | fn windowsCreateProcessPathExt( |
| 1024 | allocator: mem.Allocator, | 1033 | allocator: mem.Allocator, |
| 1025 | dir_buf: *std.ArrayListUnmanaged(u16), | 1034 | dir_buf: *ArrayList(u16), |
| 1026 | app_buf: *std.ArrayListUnmanaged(u16), | 1035 | app_buf: *ArrayList(u16), |
| 1027 | pathext: [:0]const u16, | 1036 | pathext: [:0]const u16, |
| 1028 | cmd_line_cache: *WindowsCommandLineCache, | 1037 | cmd_line_cache: *WindowsCommandLineCache, |
| 1029 | envp_ptr: ?[*]u16, | 1038 | envp_ptr: ?[*]u16, |
| ... | @@ -1506,7 +1515,7 @@ const WindowsCommandLineCache = struct { | ... | @@ -1506,7 +1515,7 @@ const WindowsCommandLineCache = struct { |
| 1506 | /// Returns the absolute path of `cmd.exe` within the Windows system directory. | 1515 | /// Returns the absolute path of `cmd.exe` within the Windows system directory. |
| 1507 | /// The caller owns the returned slice. | 1516 | /// The caller owns the returned slice. |
| 1508 | fn windowsCmdExePath(allocator: mem.Allocator) error{ OutOfMemory, Unexpected }![:0]u16 { | 1517 | fn 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); |
| 1510 | errdefer buf.deinit(allocator); | 1519 | errdefer buf.deinit(allocator); |
| 1511 | while (true) { | 1520 | while (true) { |
| 1512 | const unused_slice = buf.unusedCapacitySlice(); | 1521 | const unused_slice = buf.unusedCapacitySlice(); |
src/Compilation.zig+11-13| ... | @@ -6215,19 +6215,20 @@ fn spawnZigRc( | ... | @@ -6215,19 +6215,20 @@ fn spawnZigRc( |
| 6215 | return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv[0], @errorName(err) }); | 6215 | return comp.failWin32Resource(win32_resource, "unable to spawn {s} rc: {s}", .{ argv[0], @errorName(err) }); |
| 6216 | }; | 6216 | }; |
| 6217 | 6217 | ||
| 6218 | var poller = std.io.poll(comp.gpa, enum { stdout }, .{ | 6218 | var poller = std.Io.poll(comp.gpa, enum { stdout, stderr }, .{ |
| 6219 | .stdout = child.stdout.?, | 6219 | .stdout = child.stdout.?, |
| 6220 | .stderr = child.stderr.?, | ||
| 6220 | }); | 6221 | }); |
| 6221 | defer poller.deinit(); | 6222 | defer poller.deinit(); |
| 6222 | 6223 | ||
| 6223 | const stdout = poller.fifo(.stdout); | 6224 | const stdout = poller.reader(.stdout); |
| 6224 | 6225 | ||
| 6225 | poll: while (true) { | 6226 | poll: while (true) { |
| 6226 | while (stdout.readableLength() < @sizeOf(std.zig.Server.Message.Header)) if (!try poller.poll()) break :poll; | 6227 | const MessageHeader = std.zig.Server.Message.Header; |
| 6227 | var header: std.zig.Server.Message.Header = undefined; | 6228 | while (stdout.buffered().len < @sizeOf(MessageHeader)) if (!try poller.poll()) break :poll; |
| 6228 | assert(stdout.read(std.mem.asBytes(&header)) == @sizeOf(std.zig.Server.Message.Header)); | 6229 | const header = stdout.takeStruct(MessageHeader, .little) catch unreachable; |
| 6229 | while (stdout.readableLength() < header.bytes_len) if (!try poller.poll()) break :poll; | 6230 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll; |
| 6230 | const body = stdout.readableSliceOfLen(header.bytes_len); | 6231 | const body = stdout.take(header.bytes_len) catch unreachable; |
| 6231 | 6232 | ||
| 6232 | switch (header.tag) { | 6233 | switch (header.tag) { |
| 6233 | // We expect exactly one ErrorBundle, and if any error_bundle header is | 6234 | // We expect exactly one ErrorBundle, and if any error_bundle header is |
| ... | @@ -6250,13 +6251,10 @@ fn spawnZigRc( | ... | @@ -6250,13 +6251,10 @@ fn spawnZigRc( |
| 6250 | }, | 6251 | }, |
| 6251 | else => {}, // ignore other messages | 6252 | else => {}, // ignore other messages |
| 6252 | } | 6253 | } |
| 6253 | |||
| 6254 | stdout.discard(body.len); | ||
| 6255 | } | 6254 | } |
| 6256 | 6255 | ||
| 6257 | // Just in case there's a failure that didn't send an ErrorBundle (e.g. an error return trace) | 6256 | // 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(); | 6257 | const stderr = poller.reader(.stderr); |
| 6259 | const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); | ||
| 6260 | 6258 | ||
| 6261 | const term = child.wait() catch |err| { | 6259 | const term = child.wait() catch |err| { |
| 6262 | return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv[0], @errorName(err) }); | 6260 | return comp.failWin32Resource(win32_resource, "unable to wait for {s} rc: {s}", .{ argv[0], @errorName(err) }); |
| ... | @@ -6265,12 +6263,12 @@ fn spawnZigRc( | ... | @@ -6265,12 +6263,12 @@ fn spawnZigRc( |
| 6265 | switch (term) { | 6263 | switch (term) { |
| 6266 | .Exited => |code| { | 6264 | .Exited => |code| { |
| 6267 | if (code != 0) { | 6265 | 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()}); |
| 6269 | return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code}); | 6267 | return comp.failWin32Resource(win32_resource, "zig rc exited with code {d}", .{code}); |
| 6270 | } | 6268 | } |
| 6271 | }, | 6269 | }, |
| 6272 | else => { | 6270 | else => { |
| 6273 | log.err("zig rc terminated with stderr:\n{s}", .{stderr}); | 6271 | log.err("zig rc terminated with stderr:\n{s}", .{stderr.buffered()}); |
| 6274 | return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{}); | 6272 | return comp.failWin32Resource(win32_resource, "zig rc terminated unexpectedly", .{}); |
| 6275 | }, | 6273 | }, |
| 6276 | } | 6274 | } |
src/arch/wasm/CodeGen.zig+9-12| ... | @@ -1887,8 +1887,10 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -1887,8 +1887,10 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1887 | .call_never_tail => cg.airCall(inst, .never_tail), | 1887 | .call_never_tail => cg.airCall(inst, .never_tail), |
| 1888 | .call_never_inline => cg.airCall(inst, .never_inline), | 1888 | .call_never_inline => cg.airCall(inst, .never_inline), |
| 1889 | 1889 | ||
| 1890 | .is_err => cg.airIsErr(inst, .i32_ne), | 1890 | .is_err => cg.airIsErr(inst, .i32_ne, .value), |
| 1891 | .is_non_err => cg.airIsErr(inst, .i32_eq), | 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), | ||
| 1892 | 1894 | ||
| 1893 | .is_null => cg.airIsNull(inst, .i32_eq, .value), | 1895 | .is_null => cg.airIsNull(inst, .i32_eq, .value), |
| 1894 | .is_non_null => cg.airIsNull(inst, .i32_ne, .value), | 1896 | .is_non_null => cg.airIsNull(inst, .i32_ne, .value), |
| ... | @@ -1971,8 +1973,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -1971,8 +1973,6 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1971 | .runtime_nav_ptr => cg.airRuntimeNavPtr(inst), | 1973 | .runtime_nav_ptr => cg.airRuntimeNavPtr(inst), |
| 1972 | 1974 | ||
| 1973 | .assembly, | 1975 | .assembly, |
| 1974 | .is_err_ptr, | ||
| 1975 | .is_non_err_ptr, | ||
| 1976 | 1976 | ||
| 1977 | .err_return_trace, | 1977 | .err_return_trace, |
| 1978 | .set_err_return_trace, | 1978 | .set_err_return_trace, |
| ... | @@ -4106,7 +4106,7 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -4106,7 +4106,7 @@ fn airSwitchDispatch(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 4106 | return cg.finishAir(inst, .none, &.{br.operand}); | 4106 | return cg.finishAir(inst, .none, &.{br.operand}); |
| 4107 | } | 4107 | } |
| 4108 | 4108 | ||
| 4109 | fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerError!void { | 4109 | fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void { |
| 4110 | const zcu = cg.pt.zcu; | 4110 | const zcu = cg.pt.zcu; |
| 4111 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; | 4111 | const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4112 | const operand = try cg.resolveInst(un_op); | 4112 | const operand = try cg.resolveInst(un_op); |
| ... | @@ -4123,7 +4123,7 @@ fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerEr | ... | @@ -4123,7 +4123,7 @@ fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, opcode: std.wasm.Opcode) InnerEr |
| 4123 | } | 4123 | } |
| 4124 | 4124 | ||
| 4125 | try cg.emitWValue(operand); | 4125 | try cg.emitWValue(operand); |
| 4126 | if (pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { | 4126 | if (op_kind == .ptr or pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| 4127 | try cg.addMemArg(.i32_load16_u, .{ | 4127 | try cg.addMemArg(.i32_load16_u, .{ |
| 4128 | .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))), | 4128 | .offset = operand.offset() + @as(u32, @intCast(errUnionErrorOffset(pl_ty, zcu))), |
| 4129 | .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?), | 4129 | .alignment = @intCast(Type.anyerror.abiAlignment(zcu).toByteUnits().?), |
| ... | @@ -6463,9 +6463,6 @@ fn lowerTry( | ... | @@ -6463,9 +6463,6 @@ fn lowerTry( |
| 6463 | operand_is_ptr: bool, | 6463 | operand_is_ptr: bool, |
| 6464 | ) InnerError!WValue { | 6464 | ) InnerError!WValue { |
| 6465 | const zcu = cg.pt.zcu; | 6465 | const zcu = cg.pt.zcu; |
| 6466 | if (operand_is_ptr) { | ||
| 6467 | return cg.fail("TODO: lowerTry for pointers", .{}); | ||
| 6468 | } | ||
| 6469 | 6466 | ||
| 6470 | const pl_ty = err_union_ty.errorUnionPayload(zcu); | 6467 | const pl_ty = err_union_ty.errorUnionPayload(zcu); |
| 6471 | const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime(zcu); | 6468 | const pl_has_bits = pl_ty.hasRuntimeBitsIgnoreComptime(zcu); |
| ... | @@ -6476,7 +6473,7 @@ fn lowerTry( | ... | @@ -6476,7 +6473,7 @@ fn lowerTry( |
| 6476 | 6473 | ||
| 6477 | // check if the error tag is set for the error union. | 6474 | // check if the error tag is set for the error union. |
| 6478 | try cg.emitWValue(err_union); | 6475 | try cg.emitWValue(err_union); |
| 6479 | if (pl_has_bits) { | 6476 | if (pl_has_bits or operand_is_ptr) { |
| 6480 | const err_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu)); | 6477 | const err_offset: u32 = @intCast(errUnionErrorOffset(pl_ty, zcu)); |
| 6481 | try cg.addMemArg(.i32_load16_u, .{ | 6478 | try cg.addMemArg(.i32_load16_u, .{ |
| 6482 | .offset = err_union.offset() + err_offset, | 6479 | .offset = err_union.offset() + err_offset, |
| ... | @@ -6498,12 +6495,12 @@ fn lowerTry( | ... | @@ -6498,12 +6495,12 @@ fn lowerTry( |
| 6498 | } | 6495 | } |
| 6499 | 6496 | ||
| 6500 | // if we reach here it means error was not set, and we want the payload | 6497 | // 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) { |
| 6502 | return .none; | 6499 | return .none; |
| 6503 | } | 6500 | } |
| 6504 | 6501 | ||
| 6505 | const pl_offset: u32 = @intCast(errUnionPayloadOffset(pl_ty, zcu)); | 6502 | 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)) { |
| 6507 | return buildPointerOffset(cg, err_union, pl_offset, .new); | 6504 | return buildPointerOffset(cg, err_union, pl_offset, .new); |
| 6508 | } | 6505 | } |
| 6509 | const payload = try cg.load(err_union, pl_ty, pl_offset); | 6506 | 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 { | ... | @@ -414,6 +414,8 @@ pub fn libcFullLinkFlags(target: *const std.Target) []const []const u8 { |
| 414 | .android, .androideabi, .ohos, .ohoseabi => &.{ "-lm", "-lc", "-ldl" }, | 414 | .android, .androideabi, .ohos, .ohoseabi => &.{ "-lm", "-lc", "-ldl" }, |
| 415 | else => &.{ "-lm", "-lpthread", "-lc", "-ldl", "-lrt", "-lutil" }, | 415 | else => &.{ "-lm", "-lpthread", "-lc", "-ldl", "-lrt", "-lutil" }, |
| 416 | }, | 416 | }, |
| 417 | // On SerenityOS libc includes libm, libpthread, libdl, and libssp. | ||
| 418 | .serenity => &.{"-lc"}, | ||
| 417 | else => &.{}, | 419 | else => &.{}, |
| 418 | }; | 420 | }; |
| 419 | return result; | 421 | return result; |
test/behavior/try.zig+79| ... | @@ -121,3 +121,82 @@ test "'return try' through conditional" { | ... | @@ -121,3 +121,82 @@ test "'return try' through conditional" { |
| 121 | comptime std.debug.assert(result == 123); | 121 | comptime std.debug.assert(result == 123); |
| 122 | } | 122 | } |
| 123 | } | 123 | } |
| 124 | |||
| 125 | test "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 | |||
| 158 | test "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 { | ... | @@ -800,6 +800,8 @@ const TestManifestConfigDefaults = struct { |
| 800 | } | 800 | } |
| 801 | // Windows | 801 | // Windows |
| 802 | defaults = defaults ++ "x86_64-windows" ++ ","; | 802 | defaults = defaults ++ "x86_64-windows" ++ ","; |
| 803 | // Wasm | ||
| 804 | defaults = defaults ++ "wasm32-wasi"; | ||
| 803 | break :blk defaults; | 805 | break :blk defaults; |
| 804 | }; | 806 | }; |
| 805 | } else if (std.mem.eql(u8, key, "output_mode")) { | 807 | } else if (std.mem.eql(u8, key, "output_mode")) { |
test/tests.zig+9-10| ... | @@ -1369,16 +1369,15 @@ const test_targets = blk: { | ... | @@ -1369,16 +1369,15 @@ const test_targets = blk: { |
| 1369 | 1369 | ||
| 1370 | // WASI Targets | 1370 | // WASI Targets |
| 1371 | 1371 | ||
| 1372 | // TODO: lowerTry for pointers | 1372 | .{ |
| 1373 | //.{ | 1373 | .target = .{ |
| 1374 | // .target = .{ | 1374 | .cpu_arch = .wasm32, |
| 1375 | // .cpu_arch = .wasm32, | 1375 | .os_tag = .wasi, |
| 1376 | // .os_tag = .wasi, | 1376 | .abi = .none, |
| 1377 | // .abi = .none, | 1377 | }, |
| 1378 | // }, | 1378 | .use_llvm = false, |
| 1379 | // .use_llvm = false, | 1379 | .use_lld = false, |
| 1380 | // .use_lld = false, | 1380 | }, |
| 1381 | //}, | ||
| 1382 | .{ | 1381 | .{ |
| 1383 | .target = .{ | 1382 | .target = .{ |
| 1384 | .cpu_arch = .wasm32, | 1383 | .cpu_arch = .wasm32, |
tools/docgen.zig-1| ... | @@ -3,7 +3,6 @@ const builtin = @import("builtin"); | ... | @@ -3,7 +3,6 @@ const builtin = @import("builtin"); |
| 3 | const io = std.io; | 3 | const io = std.io; |
| 4 | const fs = std.fs; | 4 | const fs = std.fs; |
| 5 | const process = std.process; | 5 | const process = std.process; |
| 6 | const ChildProcess = std.process.Child; | ||
| 7 | const Progress = std.Progress; | 6 | const Progress = std.Progress; |
| 8 | const print = std.debug.print; | 7 | const print = std.debug.print; |
| 9 | const mem = std.mem; | 8 | const mem = std.mem; |
tools/incr-check.zig+24-39| ... | @@ -186,7 +186,7 @@ pub fn main() !void { | ... | @@ -186,7 +186,7 @@ pub fn main() !void { |
| 186 | 186 | ||
| 187 | try child.spawn(); | 187 | try child.spawn(); |
| 188 | 188 | ||
| 189 | var poller = std.io.poll(arena, Eval.StreamEnum, .{ | 189 | var poller = std.Io.poll(arena, Eval.StreamEnum, .{ |
| 190 | .stdout = child.stdout.?, | 190 | .stdout = child.stdout.?, |
| 191 | .stderr = child.stderr.?, | 191 | .stderr = child.stderr.?, |
| 192 | }); | 192 | }); |
| ... | @@ -247,19 +247,15 @@ const Eval = struct { | ... | @@ -247,19 +247,15 @@ const Eval = struct { |
| 247 | 247 | ||
| 248 | fn check(eval: *Eval, poller: *Poller, update: Case.Update, prog_node: std.Progress.Node) !void { | 248 | fn check(eval: *Eval, poller: *Poller, update: Case.Update, prog_node: std.Progress.Node) !void { |
| 249 | const arena = eval.arena; | 249 | const arena = eval.arena; |
| 250 | const Header = std.zig.Server.Message.Header; | 250 | const stdout = poller.reader(.stdout); |
| 251 | const stdout = poller.fifo(.stdout); | 251 | const stderr = poller.reader(.stderr); |
| 252 | const stderr = poller.fifo(.stderr); | ||
| 253 | 252 | ||
| 254 | poll: while (true) { | 253 | poll: while (true) { |
| 255 | while (stdout.readableLength() < @sizeOf(Header)) { | 254 | const Header = std.zig.Server.Message.Header; |
| 256 | if (!(try poller.poll())) break :poll; | 255 | while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll; |
| 257 | } | 256 | const header = stdout.takeStruct(Header, .little) catch unreachable; |
| 258 | const header = stdout.reader().readStruct(Header) catch unreachable; | 257 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll; |
| 259 | while (stdout.readableLength() < header.bytes_len) { | 258 | const body = stdout.take(header.bytes_len) catch unreachable; |
| 260 | if (!(try poller.poll())) break :poll; | ||
| 261 | } | ||
| 262 | const body = stdout.readableSliceOfLen(header.bytes_len); | ||
| 263 | 259 | ||
| 264 | switch (header.tag) { | 260 | switch (header.tag) { |
| 265 | .error_bundle => { | 261 | .error_bundle => { |
| ... | @@ -277,8 +273,8 @@ const Eval = struct { | ... | @@ -277,8 +273,8 @@ const Eval = struct { |
| 277 | .string_bytes = try arena.dupe(u8, string_bytes), | 273 | .string_bytes = try arena.dupe(u8, string_bytes), |
| 278 | .extra = extra_array, | 274 | .extra = extra_array, |
| 279 | }; | 275 | }; |
| 280 | if (stderr.readableLength() > 0) { | 276 | if (stderr.bufferedLen() > 0) { |
| 281 | const stderr_data = try stderr.toOwnedSlice(); | 277 | const stderr_data = try poller.toOwnedSlice(.stderr); |
| 282 | if (eval.allow_stderr) { | 278 | if (eval.allow_stderr) { |
| 283 | std.log.info("error_bundle included stderr:\n{s}", .{stderr_data}); | 279 | std.log.info("error_bundle included stderr:\n{s}", .{stderr_data}); |
| 284 | } else { | 280 | } else { |
| ... | @@ -289,15 +285,14 @@ const Eval = struct { | ... | @@ -289,15 +285,14 @@ const Eval = struct { |
| 289 | try eval.checkErrorOutcome(update, result_error_bundle); | 285 | try eval.checkErrorOutcome(update, result_error_bundle); |
| 290 | } | 286 | } |
| 291 | // This message indicates the end of the update. | 287 | // This message indicates the end of the update. |
| 292 | stdout.discard(body.len); | ||
| 293 | return; | 288 | return; |
| 294 | }, | 289 | }, |
| 295 | .emit_digest => { | 290 | .emit_digest => { |
| 296 | const EbpHdr = std.zig.Server.Message.EmitDigest; | 291 | const EbpHdr = std.zig.Server.Message.EmitDigest; |
| 297 | const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body)); | 292 | const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body)); |
| 298 | _ = ebp_hdr; | 293 | _ = ebp_hdr; |
| 299 | if (stderr.readableLength() > 0) { | 294 | if (stderr.bufferedLen() > 0) { |
| 300 | const stderr_data = try stderr.toOwnedSlice(); | 295 | const stderr_data = try poller.toOwnedSlice(.stderr); |
| 301 | if (eval.allow_stderr) { | 296 | if (eval.allow_stderr) { |
| 302 | std.log.info("emit_digest included stderr:\n{s}", .{stderr_data}); | 297 | std.log.info("emit_digest included stderr:\n{s}", .{stderr_data}); |
| 303 | } else { | 298 | } else { |
| ... | @@ -308,7 +303,6 @@ const Eval = struct { | ... | @@ -308,7 +303,6 @@ const Eval = struct { |
| 308 | if (eval.target.backend == .sema) { | 303 | if (eval.target.backend == .sema) { |
| 309 | try eval.checkSuccessOutcome(update, null, prog_node); | 304 | try eval.checkSuccessOutcome(update, null, prog_node); |
| 310 | // This message indicates the end of the update. | 305 | // This message indicates the end of the update. |
| 311 | stdout.discard(body.len); | ||
| 312 | } | 306 | } |
| 313 | 307 | ||
| 314 | const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len]; | 308 | const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len]; |
| ... | @@ -323,21 +317,18 @@ const Eval = struct { | ... | @@ -323,21 +317,18 @@ const Eval = struct { |
| 323 | 317 | ||
| 324 | try eval.checkSuccessOutcome(update, bin_path, prog_node); | 318 | try eval.checkSuccessOutcome(update, bin_path, prog_node); |
| 325 | // This message indicates the end of the update. | 319 | // This message indicates the end of the update. |
| 326 | stdout.discard(body.len); | ||
| 327 | }, | 320 | }, |
| 328 | else => { | 321 | else => { |
| 329 | // Ignore other messages. | 322 | // Ignore other messages. |
| 330 | stdout.discard(body.len); | ||
| 331 | }, | 323 | }, |
| 332 | } | 324 | } |
| 333 | } | 325 | } |
| 334 | 326 | ||
| 335 | if (stderr.readableLength() > 0) { | 327 | if (stderr.bufferedLen() > 0) { |
| 336 | const stderr_data = try stderr.toOwnedSlice(); | ||
| 337 | if (eval.allow_stderr) { | 328 | 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() }); |
| 339 | } else { | 330 | } 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() }); |
| 341 | } | 332 | } |
| 342 | } | 333 | } |
| 343 | 334 | ||
| ... | @@ -537,25 +528,19 @@ const Eval = struct { | ... | @@ -537,25 +528,19 @@ const Eval = struct { |
| 537 | fn end(eval: *Eval, poller: *Poller) !void { | 528 | fn end(eval: *Eval, poller: *Poller) !void { |
| 538 | requestExit(eval.child, eval); | 529 | requestExit(eval.child, eval); |
| 539 | 530 | ||
| 540 | const Header = std.zig.Server.Message.Header; | 531 | const stdout = poller.reader(.stdout); |
| 541 | const stdout = poller.fifo(.stdout); | 532 | const stderr = poller.reader(.stderr); |
| 542 | const stderr = poller.fifo(.stderr); | ||
| 543 | 533 | ||
| 544 | poll: while (true) { | 534 | poll: while (true) { |
| 545 | while (stdout.readableLength() < @sizeOf(Header)) { | 535 | const Header = std.zig.Server.Message.Header; |
| 546 | if (!(try poller.poll())) break :poll; | 536 | while (stdout.buffered().len < @sizeOf(Header)) if (!try poller.poll()) break :poll; |
| 547 | } | 537 | const header = stdout.takeStruct(Header, .little) catch unreachable; |
| 548 | const header = stdout.reader().readStruct(Header) catch unreachable; | 538 | while (stdout.buffered().len < header.bytes_len) if (!try poller.poll()) break :poll; |
| 549 | while (stdout.readableLength() < header.bytes_len) { | 539 | stdout.toss(header.bytes_len); |
| 550 | if (!(try poller.poll())) break :poll; | ||
| 551 | } | ||
| 552 | const body = stdout.readableSliceOfLen(header.bytes_len); | ||
| 553 | stdout.discard(body.len); | ||
| 554 | } | 540 | } |
| 555 | 541 | ||
| 556 | if (stderr.readableLength() > 0) { | 542 | if (stderr.bufferedLen() > 0) { |
| 557 | const stderr_data = try stderr.toOwnedSlice(); | 543 | eval.fatal("unexpected stderr:\n{s}", .{stderr.buffered()}); |
| 558 | eval.fatal("unexpected stderr:\n{s}", .{stderr_data}); | ||
| 559 | } | 544 | } |
| 560 | } | 545 | } |
| 561 | 546 |