authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-01 18:39:22-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-04 00:27:08-08:00
log1ccc87363a6436da293dbb1bc7bc8db2aa4d7bf7
treef6ae16ea94160a796e0a644afce1e8705bbf1cab
parent50e185b71822c180fccd07826a8dd5e3ec641cc1

std: fixes for WASI


5 files changed, 26 insertions(+), 17 deletions(-)

lib/std/Io/Threaded.zig+3-1
...@@ -12787,7 +12787,7 @@ const processSpawn = switch (native_os) {...@@ -12787,7 +12787,7 @@ const processSpawn = switch (native_os) {
12787fn processSpawnUnsupported(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {12787fn processSpawnUnsupported(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {
12788 _ = userdata;12788 _ = userdata;
12789 _ = options;12789 _ = options;
12790 return error.OperationUnsupported;12790 return error.Unexpected;
12791}12791}
1279212792
12793fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {12793fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {
...@@ -12995,6 +12995,7 @@ fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) proce...@@ -12995,6 +12995,7 @@ fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) proce
12995}12995}
1299612996
12997fn childWait(userdata: ?*anyopaque, child: *std.process.Child) process.Child.WaitError!process.Child.Term {12997fn childWait(userdata: ?*anyopaque, child: *std.process.Child) process.Child.WaitError!process.Child.Term {
12998 if (native_os == .wasi) unreachable;
12998 const t: *Threaded = @ptrCast(@alignCast(userdata));12999 const t: *Threaded = @ptrCast(@alignCast(userdata));
12999 switch (native_os) {13000 switch (native_os) {
13000 .windows => return childWaitWindows(t, child),13001 .windows => return childWaitWindows(t, child),
...@@ -13003,6 +13004,7 @@ fn childWait(userdata: ?*anyopaque, child: *std.process.Child) process.Child.Wai...@@ -13003,6 +13004,7 @@ fn childWait(userdata: ?*anyopaque, child: *std.process.Child) process.Child.Wai
13003}13004}
1300413005
13005fn childKill(userdata: ?*anyopaque, child: *std.process.Child) void {13006fn childKill(userdata: ?*anyopaque, child: *std.process.Child) void {
13007 if (native_os == .wasi) unreachable;
13006 const t: *Threaded = @ptrCast(@alignCast(userdata));13008 const t: *Threaded = @ptrCast(@alignCast(userdata));
13007 if (is_windows) {13009 if (is_windows) {
13008 childKillWindows(t, child, 1) catch childCleanupWindows(child);13010 childKillWindows(t, child, 1) catch childCleanupWindows(child);
lib/std/posix/test.zig+2-2
...@@ -22,10 +22,10 @@ const tmpDir = std.testing.tmpDir;...@@ -22,10 +22,10 @@ const tmpDir = std.testing.tmpDir;
2222
23test "check WASI CWD" {23test "check WASI CWD" {
24 if (native_os == .wasi) {24 if (native_os == .wasi) {
25 if (std.options.wasiCwd() != 3) {25 const cwd: Dir = .cwd();
26 if (cwd.handle != 3) {
26 @panic("WASI code that uses cwd (like this test) needs a preopen for cwd (add '--dir=.' to wasmtime)");27 @panic("WASI code that uses cwd (like this test) needs a preopen for cwd (add '--dir=.' to wasmtime)");
27 }28 }
28
29 if (!builtin.link_libc) {29 if (!builtin.link_libc) {
30 // WASI without-libc hardcodes fd 3 as the FDCWD token so it can be passed directly to WASI calls30 // WASI without-libc hardcodes fd 3 as the FDCWD token so it can be passed directly to WASI calls
31 try expectEqual(3, posix.AT.FDCWD);31 try expectEqual(3, posix.AT.FDCWD);
lib/std/process/Args.zig+15-7
...@@ -10,8 +10,14 @@ const testing = std.testing;...@@ -10,8 +10,14 @@ const testing = std.testing;
1010
11vector: Vector,11vector: Vector,
1212
13/// On WASI without libc, this is `void` because the environment has to be
14/// queried and heap-allocated at runtime.
13pub const Vector = switch (native_os) {15pub const Vector = switch (native_os) {
14 .windows => []const u16, // WTF-16 encoded16 .windows => []const u16, // WTF-16 encoded
17 .wasi => switch (builtin.link_libc) {
18 false => void,
19 true => []const [*:0]const u8,
20 },
15 .freestanding, .other => void,21 .freestanding, .other => void,
16 else => []const [*:0]const u8,22 else => []const [*:0]const u8,
17};23};
...@@ -457,6 +463,8 @@ pub fn iterateAllocator(a: Args, gpa: Allocator) Iterator.InitError!Iterator {...@@ -457,6 +463,8 @@ pub fn iterateAllocator(a: Args, gpa: Allocator) Iterator.InitError!Iterator {
457 return .initAllocator(a, gpa);463 return .initAllocator(a, gpa);
458}464}
459465
466pub const ToSliceError = Iterator.Windows.InitError || Iterator.Wasi.InitError;
467
460/// Returned value may reference several allocations; call `freeSlice` to468/// Returned value may reference several allocations; call `freeSlice` to
461/// release.469/// release.
462///470///
...@@ -464,19 +472,19 @@ pub fn iterateAllocator(a: Args, gpa: Allocator) Iterator.InitError!Iterator {...@@ -464,19 +472,19 @@ pub fn iterateAllocator(a: Args, gpa: Allocator) Iterator.InitError!Iterator {
464/// [WTF-8](https://wtf-8.codeberg.page/).472/// [WTF-8](https://wtf-8.codeberg.page/).
465/// * On other platforms, the result is an opaque sequence of bytes with no473/// * On other platforms, the result is an opaque sequence of bytes with no
466/// particular encoding.474/// particular encoding.
467pub fn toSlice(a: Args, gpa: Allocator) Allocator.Error![][:0]u8 {475pub fn toSlice(a: Args, gpa: Allocator) ToSliceError![][:0]u8 {
468 var it = try a.iterateAllocator(gpa);476 var it = try a.iterateAllocator(gpa);
469 defer it.deinit();477 defer it.deinit();
470478
471 var contents = std.array_list.Managed(u8).init(gpa);479 var contents: std.ArrayList(u8) = .empty;
472 defer contents.deinit();480 defer contents.deinit(gpa);
473481
474 var slice_list = std.array_list.Managed(usize).init(gpa);482 var slice_list: std.ArrayList(usize) = .empty;
475 defer slice_list.deinit();483 defer slice_list.deinit(gpa);
476484
477 while (it.next()) |arg| {485 while (it.next()) |arg| {
478 try contents.appendSlice(arg[0 .. arg.len + 1]);486 try contents.appendSlice(gpa, arg[0 .. arg.len + 1]);
479 try slice_list.append(arg.len);487 try slice_list.append(gpa, arg.len);
480 }488 }
481489
482 const contents_slice = contents.items;490 const contents_slice = contents.items;
lib/std/process/Environ.zig+4-5
...@@ -757,6 +757,9 @@ test Map {...@@ -757,6 +757,9 @@ test Map {
757}757}
758758
759test "convert from Environ to Map and back again" {759test "convert from Environ to Map and back again" {
760 if (native_os == .windows) return;
761 if (native_os == .wasi and !builtin.link_libc) return;
762
760 const gpa = testing.allocator;763 const gpa = testing.allocator;
761764
762 var map: Map = .init(gpa);765 var map: Map = .init(gpa);
...@@ -769,11 +772,7 @@ test "convert from Environ to Map and back again" {...@@ -769,11 +772,7 @@ test "convert from Environ to Map and back again" {
769 defer arena_allocator.deinit();772 defer arena_allocator.deinit();
770 const arena = arena_allocator.allocator();773 const arena = arena_allocator.allocator();
771774
772 const environ: Environ = switch (native_os) {775 const environ: Environ = .{ .block = try map.createBlockPosix(arena, .{}) };
773 .windows => return error.SkipZigTest,
774 .wasi => if (!builtin.libc) return error.SkipZigTest,
775 else => .{ .block = try map.createBlockPosix(arena, .{}) },
776 };
777776
778 try testing.expectEqual(true, environ.contains(gpa, "FOO"));777 try testing.expectEqual(true, environ.contains(gpa, "FOO"));
779 try testing.expectEqual(false, environ.contains(gpa, "BAR"));778 try testing.expectEqual(false, environ.contains(gpa, "BAR"));
lib/std/start.zig+2-2
...@@ -55,7 +55,7 @@ comptime {...@@ -55,7 +55,7 @@ comptime {
55 if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) {55 if (!@hasDecl(root, wasm_start_sym) and @hasDecl(root, "main")) {
56 // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which56 // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which
57 // case it's not required to provide an entrypoint such as main.57 // case it's not required to provide an entrypoint such as main.
58 @export(&wasi_start, .{ .name = wasm_start_sym });58 @export(&startWasi, .{ .name = wasm_start_sym });
59 }59 }
60 } else if (native_arch.isWasm() and native_os == .freestanding) {60 } else if (native_arch.isWasm() and native_os == .freestanding) {
61 // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which61 // Only call main when defined. For WebAssembly it's allowed to pass `-fno-entry` in which
...@@ -90,7 +90,7 @@ fn wasm_freestanding_start() callconv(.c) void {...@@ -90,7 +90,7 @@ fn wasm_freestanding_start() callconv(.c) void {
90 _ = @call(.always_inline, callMain, .{ {}, {} });90 _ = @call(.always_inline, callMain, .{ {}, {} });
91}91}
9292
93fn wasi_start() callconv(.c) void {93fn startWasi() callconv(.c) void {
94 // The function call is marked inline because for some reason LLVM in94 // The function call is marked inline because for some reason LLVM in
95 // release mode fails to inline it, and we want fewer call frames in stack traces.95 // release mode fails to inline it, and we want fewer call frames in stack traces.
96 switch (builtin.wasi_exec_model) {96 switch (builtin.wasi_exec_model) {