diff options
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/Thread/Condition.zig | 196 | ||||
| -rw-r--r-- | lib/std/builtin.zig | 22 | ||||
| -rw-r--r-- | lib/std/crypto/25519/ed25519.zig | 1 | ||||
| -rw-r--r-- | lib/std/fs/path.zig | 390 | ||||
| -rw-r--r-- | lib/std/fs/test.zig | 4 | ||||
| -rw-r--r-- | lib/std/fs/wasi.zig | 5 | ||||
| -rw-r--r-- | lib/std/target.zig | 4 |
7 files changed, 377 insertions, 245 deletions
diff --git a/lib/std/Thread/Condition.zig b/lib/std/Thread/Condition.zig index 1482c8166d..3625aab576 100644 --- a/lib/std/Thread/Condition.zig +++ b/lib/std/Thread/Condition.zig @@ -194,42 +194,27 @@ const FutexImpl = struct { const signal_mask = 0xffff << 16; fn wait(self: *Impl, mutex: *Mutex, timeout: ?u64) error{Timeout}!void { - // Register that we're waiting on the state by incrementing the wait count. - // This assumes that there can be at most ((1<<16)-1) or 65,355 threads concurrently waiting on the same Condvar. - // If this is hit in practice, then this condvar not working is the least of your concerns. + // Observe the epoch, then check the state again to see if we should wake up. + // The epoch must be observed before we check the state or we could potentially miss a wake() and deadlock: + // + // - T1: s = LOAD(&state) + // - T2: UPDATE(&s, signal) + // - T2: UPDATE(&epoch, 1) + FUTEX_WAKE(&epoch) + // - T1: e = LOAD(&epoch) (was reordered after the state load) + // - T1: s & signals == 0 -> FUTEX_WAIT(&epoch, e) (missed the state update + the epoch change) + // + // Acquire barrier to ensure the epoch load happens before the state load. + var epoch = self.epoch.load(.Acquire); var state = self.state.fetchAdd(one_waiter, .Monotonic); assert(state & waiter_mask != waiter_mask); state += one_waiter; - // Temporarily release the mutex in order to block on the condition variable. mutex.unlock(); defer mutex.lock(); var futex_deadline = Futex.Deadline.init(timeout); - while (true) { - // Try to wake up by consuming a signal and decremented the waiter we added previously. - // Acquire barrier ensures code before the wake() which added the signal happens before we decrement it and return. - while (state & signal_mask != 0) { - const new_state = state - one_waiter - one_signal; - state = self.state.tryCompareAndSwap(state, new_state, .Acquire, .Monotonic) orelse return; - } - - // Observe the epoch, then check the state again to see if we should wake up. - // The epoch must be observed before we check the state or we could potentially miss a wake() and deadlock: - // - // - T1: s = LOAD(&state) - // - T2: UPDATE(&s, signal) - // - T2: UPDATE(&epoch, 1) + FUTEX_WAKE(&epoch) - // - T1: e = LOAD(&epoch) (was reordered after the state load) - // - T1: s & signals == 0 -> FUTEX_WAIT(&epoch, e) (missed the state update + the epoch change) - // - // Acquire barrier to ensure the epoch load happens before the state load. - const epoch = self.epoch.load(.Acquire); - state = self.state.load(.Monotonic); - if (state & signal_mask != 0) { - continue; - } + while (true) { futex_deadline.wait(&self.epoch, epoch) catch |err| switch (err) { // On timeout, we must decrement the waiter we added above. error.Timeout => { @@ -247,6 +232,16 @@ const FutexImpl = struct { } }, }; + + epoch = self.epoch.load(.Acquire); + state = self.state.load(.Monotonic); + + // Try to wake up by consuming a signal and decremented the waiter we added previously. + // Acquire barrier ensures code before the wake() which added the signal happens before we decrement it and return. + while (state & signal_mask != 0) { + const new_state = state - one_waiter - one_signal; + state = self.state.tryCompareAndSwap(state, new_state, .Acquire, .Monotonic) orelse return; + } } } @@ -536,3 +531,150 @@ test "Condition - broadcasting" { t.join(); } } + +test "Condition - broadcasting - wake all threads" { + // Tests issue #12877 + // This test requires spawning threads + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + var num_runs: usize = 1; + const num_threads = 10; + + while (num_runs > 0) : (num_runs -= 1) { + const BroadcastTest = struct { + mutex: Mutex = .{}, + cond: Condition = .{}, + completed: Condition = .{}, + count: usize = 0, + thread_id_to_wake: usize = 0, + threads: [num_threads]std.Thread = undefined, + wakeups: usize = 0, + + fn run(self: *@This(), thread_id: usize) void { + self.mutex.lock(); + defer self.mutex.unlock(); + + // The last broadcast thread to start tells the main test thread it's completed. + self.count += 1; + if (self.count == num_threads) { + self.completed.signal(); + } + + while (self.thread_id_to_wake != thread_id) { + self.cond.timedWait(&self.mutex, 1 * std.time.ns_per_s) catch std.debug.panic("thread_id {d} timeout {d}", .{ thread_id, self.thread_id_to_wake }); + self.wakeups += 1; + } + if (self.thread_id_to_wake <= num_threads) { + // Signal next thread to wake up. + self.thread_id_to_wake += 1; + self.cond.broadcast(); + } + } + }; + + var broadcast_test = BroadcastTest{}; + var thread_id: usize = 1; + for (broadcast_test.threads) |*t| { + t.* = try std.Thread.spawn(.{}, BroadcastTest.run, .{ &broadcast_test, thread_id }); + thread_id += 1; + } + + { + broadcast_test.mutex.lock(); + defer broadcast_test.mutex.unlock(); + + // Wait for all the broadcast threads to spawn. + // timedWait() to detect any potential deadlocks. + while (broadcast_test.count != num_threads) { + try broadcast_test.completed.timedWait( + &broadcast_test.mutex, + 1 * std.time.ns_per_s, + ); + } + + // Signal thread 1 to wake up + broadcast_test.thread_id_to_wake = 1; + broadcast_test.cond.broadcast(); + } + + for (broadcast_test.threads) |t| { + t.join(); + } + } +} + +test "Condition - signal wakes one" { + // This test requires spawning threads + if (builtin.single_threaded) { + return error.SkipZigTest; + } + + var num_runs: usize = 1; + const num_threads = 3; + const timeoutDelay = 10 * std.time.ns_per_ms; + + while (num_runs > 0) : (num_runs -= 1) { + + // Start multiple runner threads, wait for them to start and send the signal + // then. Expect that one thread wake up and all other times out. + // + // Test depends on delay in timedWait! If too small all threads can timeout + // before any one gets wake up. + + const Runner = struct { + mutex: Mutex = .{}, + cond: Condition = .{}, + completed: Condition = .{}, + count: usize = 0, + threads: [num_threads]std.Thread = undefined, + wakeups: usize = 0, + timeouts: usize = 0, + + fn run(self: *@This()) void { + self.mutex.lock(); + defer self.mutex.unlock(); + + // The last started thread tells the main test thread it's completed. + self.count += 1; + if (self.count == num_threads) { + self.completed.signal(); + } + + self.cond.timedWait(&self.mutex, timeoutDelay) catch { + self.timeouts += 1; + return; + }; + self.wakeups += 1; + } + }; + + // Start threads + var runner = Runner{}; + for (runner.threads) |*t| { + t.* = try std.Thread.spawn(.{}, Runner.run, .{&runner}); + } + + { + runner.mutex.lock(); + defer runner.mutex.unlock(); + + // Wait for all the threads to spawn. + // timedWait() to detect any potential deadlocks. + while (runner.count != num_threads) { + try runner.completed.timedWait(&runner.mutex, 1 * std.time.ns_per_s); + } + // Signal one thread, the others should get timeout. + runner.cond.signal(); + } + + for (runner.threads) |t| { + t.join(); + } + + // Expect that only one got singal + try std.testing.expectEqual(runner.wakeups, 1); + try std.testing.expectEqual(runner.timeouts, num_threads - 1); + } +} diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 33604dfee7..1f7d48ccb9 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -168,7 +168,7 @@ pub const AddressSpace = enum { gs, fs, ss, - // GPU address spaces + // GPU address spaces. global, constant, param, @@ -869,6 +869,26 @@ pub noinline fn returnError(st: *StackTrace) void { addErrRetTraceAddr(st, @returnAddress()); } +pub const panic_messages = struct { + pub const unreach = "reached unreachable code"; + pub const unwrap_null = "attempt to use null value"; + pub const cast_to_null = "cast causes pointer to be null"; + pub const incorrect_alignment = "incorrect alignment"; + pub const invalid_error_code = "invalid error code"; + pub const cast_truncated_data = "integer cast truncated bits"; + pub const negative_to_unsigned = "attempt to cast negative value to unsigned integer"; + pub const integer_overflow = "integer overflow"; + pub const shl_overflow = "left shift overflowed bits"; + pub const shr_overflow = "right shift overflowed bits"; + pub const divide_by_zero = "division by zero"; + pub const exact_division_remainder = "exact division produced remainder"; + pub const inactive_union_field = "access of inactive union field"; + pub const integer_part_out_of_bounds = "integer part of floating point value out of bounds"; + pub const corrupt_switch = "switch on corrupt value"; + pub const shift_rhs_too_big = "shift amount is greater than the type size"; + pub const invalid_enum_value = "invalid enum value"; +}; + pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void { if (st.index < st.instruction_addresses.len) st.instruction_addresses[st.index] = addr; diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig index 552bd114a1..149191040a 100644 --- a/lib/std/crypto/25519/ed25519.zig +++ b/lib/std/crypto/25519/ed25519.zig @@ -318,6 +318,7 @@ pub const Ed25519 = struct { h.update(&scalar_and_prefix.prefix); var noise2: [noise_length]u8 = undefined; crypto.random.bytes(&noise2); + h.update(&noise2); if (noise) |*z| { h.update(z); } diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index 0d102493cf..feacf38daf 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -467,55 +467,49 @@ pub fn resolve(allocator: Allocator, paths: []const []const u8) ![]u8 { /// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters. /// Note: all usage of this function should be audited due to the existence of symlinks. /// Without performing actual syscalls, resolving `..` could be incorrect. +/// This API may break in the future: https://github.com/ziglang/zig/issues/13613 pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { - if (paths.len == 0) { - assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd - return process.getCwdAlloc(allocator); - } + assert(paths.len > 0); // determine which disk designator we will result with, if any var result_drive_buf = "_:".*; - var result_disk_designator: []const u8 = ""; - var have_drive_kind = WindowsPath.Kind.None; + var disk_designator: []const u8 = ""; + var drive_kind = WindowsPath.Kind.None; var have_abs_path = false; var first_index: usize = 0; - var max_size: usize = 0; for (paths) |p, i| { const parsed = windowsParsePath(p); if (parsed.is_abs) { have_abs_path = true; first_index = i; - max_size = result_disk_designator.len; } switch (parsed.kind) { - WindowsPath.Kind.Drive => { + .Drive => { result_drive_buf[0] = ascii.toUpper(parsed.disk_designator[0]); - result_disk_designator = result_drive_buf[0..]; - have_drive_kind = WindowsPath.Kind.Drive; + disk_designator = result_drive_buf[0..]; + drive_kind = WindowsPath.Kind.Drive; }, - WindowsPath.Kind.NetworkShare => { - result_disk_designator = parsed.disk_designator; - have_drive_kind = WindowsPath.Kind.NetworkShare; + .NetworkShare => { + disk_designator = parsed.disk_designator; + drive_kind = WindowsPath.Kind.NetworkShare; }, - WindowsPath.Kind.None => {}, + .None => {}, } - max_size += p.len + 1; } // if we will result with a disk designator, loop again to determine // which is the last time the disk designator is absolutely specified, if any // and count up the max bytes for paths related to this disk designator - if (have_drive_kind != WindowsPath.Kind.None) { + if (drive_kind != WindowsPath.Kind.None) { have_abs_path = false; first_index = 0; - max_size = result_disk_designator.len; var correct_disk_designator = false; for (paths) |p, i| { const parsed = windowsParsePath(p); if (parsed.kind != WindowsPath.Kind.None) { - if (parsed.kind == have_drive_kind) { - correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator); + if (parsed.kind == drive_kind) { + correct_disk_designator = compareDiskDesignators(drive_kind, disk_designator, parsed.disk_designator); } else { continue; } @@ -525,92 +519,51 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { } if (parsed.is_abs) { first_index = i; - max_size = result_disk_designator.len; have_abs_path = true; } - max_size += p.len + 1; } } - // Allocate result and fill in the disk designator, calling getCwd if we have to. - var result: []u8 = undefined; - var result_index: usize = 0; - - if (have_abs_path) { - switch (have_drive_kind) { - WindowsPath.Kind.Drive => { - result = try allocator.alloc(u8, max_size); + // Allocate result and fill in the disk designator. + var result = std.ArrayList(u8).init(allocator); + defer result.deinit(); - mem.copy(u8, result, result_disk_designator); - result_index += result_disk_designator.len; + const disk_designator_len: usize = l: { + if (!have_abs_path) break :l 0; + switch (drive_kind) { + .Drive => { + try result.appendSlice(disk_designator); + break :l disk_designator.len; }, - WindowsPath.Kind.NetworkShare => { - result = try allocator.alloc(u8, max_size); + .NetworkShare => { var it = mem.tokenize(u8, paths[first_index], "/\\"); const server_name = it.next().?; const other_name = it.next().?; - result[result_index] = '\\'; - result_index += 1; - result[result_index] = '\\'; - result_index += 1; - mem.copy(u8, result[result_index..], server_name); - result_index += server_name.len; - result[result_index] = '\\'; - result_index += 1; - mem.copy(u8, result[result_index..], other_name); - result_index += other_name.len; - - result_disk_designator = result[0..result_index]; + try result.ensureUnusedCapacity(2 + 1 + server_name.len + other_name.len); + result.appendSliceAssumeCapacity("\\\\"); + result.appendSliceAssumeCapacity(server_name); + result.appendAssumeCapacity('\\'); + result.appendSliceAssumeCapacity(other_name); + + break :l result.items.len; }, - WindowsPath.Kind.None => { - assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd - const cwd = try process.getCwdAlloc(allocator); - defer allocator.free(cwd); - const parsed_cwd = windowsParsePath(cwd); - result = try allocator.alloc(u8, max_size + parsed_cwd.disk_designator.len + 1); - mem.copy(u8, result, parsed_cwd.disk_designator); - result_index += parsed_cwd.disk_designator.len; - result_disk_designator = result[0..parsed_cwd.disk_designator.len]; - if (parsed_cwd.kind == WindowsPath.Kind.Drive) { - result[0] = ascii.toUpper(result[0]); - } - have_drive_kind = parsed_cwd.kind; + .None => { + break :l 1; }, } - } else { - assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd - // TODO call get cwd for the result_disk_designator instead of the global one - const cwd = try process.getCwdAlloc(allocator); - defer allocator.free(cwd); - - result = try allocator.alloc(u8, max_size + cwd.len + 1); - - mem.copy(u8, result, cwd); - result_index += cwd.len; - const parsed_cwd = windowsParsePath(result[0..result_index]); - result_disk_designator = parsed_cwd.disk_designator; - if (parsed_cwd.kind == WindowsPath.Kind.Drive) { - result[0] = ascii.toUpper(result[0]); - // Remove the trailing slash if present, eg. if the cwd is a root - // directory. - if (cwd.len > 0 and cwd[cwd.len - 1] == sep_windows) { - result_index -= 1; - } - } - have_drive_kind = parsed_cwd.kind; - } - errdefer allocator.free(result); + }; - // Now we know the disk designator to use, if any, and what kind it is. And our result - // is big enough to append all the paths to. var correct_disk_designator = true; + var negative_count: usize = 0; + for (paths[first_index..]) |p| { const parsed = windowsParsePath(p); - if (parsed.kind != WindowsPath.Kind.None) { - if (parsed.kind == have_drive_kind) { - correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator); + if (parsed.kind != .None) { + if (parsed.kind == drive_kind) { + const dd = result.items[0..disk_designator_len]; + correct_disk_designator = compareDiskDesignators(drive_kind, dd, parsed.disk_designator); } else { continue; } @@ -619,154 +572,167 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 { continue; } var it = mem.tokenize(u8, p[parsed.disk_designator.len..], "/\\"); - while (it.next()) |component| { + component: while (it.next()) |component| { if (mem.eql(u8, component, ".")) { continue; } else if (mem.eql(u8, component, "..")) { while (true) { - if (result_index == 0 or result_index == result_disk_designator.len) - break; - result_index -= 1; - if (result[result_index] == '\\' or result[result_index] == '/') + if (result.items.len == 0) { + negative_count += 1; + continue :component; + } + if (result.items.len == disk_designator_len) { break; + } + const end_with_sep = switch (result.items[result.items.len - 1]) { + '\\', '/' => true, + else => false, + }; + result.items.len -= 1; + if (end_with_sep) break; } + } else if (!have_abs_path and result.items.len == 0) { + try result.appendSlice(component); } else { - result[result_index] = sep_windows; - result_index += 1; - mem.copy(u8, result[result_index..], component); - result_index += component.len; + try result.ensureUnusedCapacity(1 + component.len); + result.appendAssumeCapacity('\\'); + result.appendSliceAssumeCapacity(component); } } } - if (result_index == result_disk_designator.len) { - result[result_index] = '\\'; - result_index += 1; + if (disk_designator_len != 0 and result.items.len == disk_designator_len) { + try result.append('\\'); + return result.toOwnedSlice(); + } + + if (result.items.len == 0) { + if (negative_count == 0) { + return allocator.dupe(u8, "."); + } else { + const real_result = try allocator.alloc(u8, 3 * negative_count - 1); + var count = negative_count - 1; + var i: usize = 0; + while (count > 0) : (count -= 1) { + real_result[i..][0..3].* = "..\\".*; + i += 3; + } + real_result[i..][0..2].* = "..".*; + return real_result; + } } - return allocator.shrink(result, result_index); + if (negative_count == 0) { + return result.toOwnedSlice(); + } else { + const real_result = try allocator.alloc(u8, 3 * negative_count + result.items.len); + var count = negative_count; + var i: usize = 0; + while (count > 0) : (count -= 1) { + real_result[i..][0..3].* = "..\\".*; + i += 3; + } + mem.copy(u8, real_result[i..], result.items); + return real_result; + } } /// This function is like a series of `cd` statements executed one after another. /// It resolves "." and "..". /// The result does not have a trailing path separator. -/// If all paths are relative it uses the current working directory as a starting point. -/// Note: all usage of this function should be audited due to the existence of symlinks. -/// Without performing actual syscalls, resolving `..` could be incorrect. -pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) ![]u8 { - if (paths.len == 0) { - assert(native_os != .windows); // resolvePosix called on windows can't use getCwd - return process.getCwdAlloc(allocator); - } +/// This function does not perform any syscalls. Executing this series of path +/// lookups on the actual filesystem may produce different results due to +/// symlinks. +pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) Allocator.Error![]u8 { + assert(paths.len > 0); - var first_index: usize = 0; - var have_abs = false; - var max_size: usize = 0; - for (paths) |p, i| { - if (isAbsolutePosix(p)) { - first_index = i; - have_abs = true; - max_size = 0; - } - max_size += p.len + 1; - } - - var result: []u8 = undefined; - var result_index: usize = 0; + var result = std.ArrayList(u8).init(allocator); + defer result.deinit(); - if (have_abs) { - result = try allocator.alloc(u8, max_size); - } else { - assert(native_os != .windows); // resolvePosix called on windows can't use getCwd - const cwd = try process.getCwdAlloc(allocator); - defer allocator.free(cwd); - result = try allocator.alloc(u8, max_size + cwd.len + 1); - mem.copy(u8, result, cwd); - result_index += cwd.len; - } - errdefer allocator.free(result); + var negative_count: usize = 0; + var is_abs = false; - for (paths[first_index..]) |p| { + for (paths) |p| { + if (isAbsolutePosix(p)) { + is_abs = true; + negative_count = 0; + result.clearRetainingCapacity(); + } var it = mem.tokenize(u8, p, "/"); - while (it.next()) |component| { + component: while (it.next()) |component| { if (mem.eql(u8, component, ".")) { continue; } else if (mem.eql(u8, component, "..")) { while (true) { - if (result_index == 0) - break; - result_index -= 1; - if (result[result_index] == '/') - break; + if (result.items.len == 0) { + negative_count += @boolToInt(!is_abs); + continue :component; + } + const ends_with_slash = result.items[result.items.len - 1] == '/'; + result.items.len -= 1; + if (ends_with_slash) break; } + } else if (result.items.len > 0 or is_abs) { + try result.ensureUnusedCapacity(1 + component.len); + result.appendAssumeCapacity('/'); + result.appendSliceAssumeCapacity(component); } else { - result[result_index] = '/'; - result_index += 1; - mem.copy(u8, result[result_index..], component); - result_index += component.len; + try result.appendSlice(component); } } } - if (result_index == 0) { - result[0] = '/'; - result_index += 1; + if (result.items.len == 0) { + if (is_abs) { + return allocator.dupe(u8, "/"); + } + if (negative_count == 0) { + return allocator.dupe(u8, "."); + } else { + const real_result = try allocator.alloc(u8, 3 * negative_count - 1); + var count = negative_count - 1; + var i: usize = 0; + while (count > 0) : (count -= 1) { + real_result[i..][0..3].* = "../".*; + i += 3; + } + real_result[i..][0..2].* = "..".*; + return real_result; + } } - return allocator.shrink(result, result_index); + if (negative_count == 0) { + return result.toOwnedSlice(); + } else { + const real_result = try allocator.alloc(u8, 3 * negative_count + result.items.len); + var count = negative_count; + var i: usize = 0; + while (count > 0) : (count -= 1) { + real_result[i..][0..3].* = "../".*; + i += 3; + } + mem.copy(u8, real_result[i..], result.items); + return real_result; + } } test "resolve" { - if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); + try testResolveWindows(&[_][]const u8{ "a\\b\\c\\", "..\\..\\.." }, ".."); + try testResolveWindows(&[_][]const u8{"."}, "."); - const cwd = try process.getCwdAlloc(testing.allocator); - defer testing.allocator.free(cwd); - if (native_os == .windows) { - if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) { - cwd[0] = ascii.toUpper(cwd[0]); - } - try testResolveWindows(&[_][]const u8{"."}, cwd); - } else { - try testResolvePosix(&[_][]const u8{ "a/b/c/", "../../.." }, cwd); - try testResolvePosix(&[_][]const u8{"."}, cwd); - } + try testResolvePosix(&[_][]const u8{ "a/b/c/", "../../.." }, ".."); + try testResolvePosix(&[_][]const u8{"."}, "."); } test "resolveWindows" { - if (builtin.target.cpu.arch == .aarch64) { - // TODO https://github.com/ziglang/zig/issues/3288 - return error.SkipZigTest; - } - if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); - if (native_os == .windows) { - const cwd = try process.getCwdAlloc(testing.allocator); - defer testing.allocator.free(cwd); - const parsed_cwd = windowsParsePath(cwd); - { - const expected = try join(testing.allocator, &[_][]const u8{ - parsed_cwd.disk_designator, - "usr\\local\\lib\\zig\\std\\array_list.zig", - }); - defer testing.allocator.free(expected); - if (parsed_cwd.kind == WindowsPath.Kind.Drive) { - expected[0] = ascii.toUpper(parsed_cwd.disk_designator[0]); - } - try testResolveWindows(&[_][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" }, expected); - } - { - const expected = try join(testing.allocator, &[_][]const u8{ - cwd, - "usr\\local\\lib\\zig", - }); - defer testing.allocator.free(expected); - if (parsed_cwd.kind == WindowsPath.Kind.Drive) { - expected[0] = ascii.toUpper(parsed_cwd.disk_designator[0]); - } - try testResolveWindows(&[_][]const u8{ "usr/local", "lib\\zig" }, expected); - } - } + try testResolveWindows( + &[_][]const u8{ "Z:\\", "/usr/local", "lib\\zig\\std\\array_list.zig" }, + "Z:\\usr\\local\\lib\\zig\\std\\array_list.zig", + ); + try testResolveWindows( + &[_][]const u8{ "z:\\", "usr/local", "lib\\zig" }, + "Z:\\usr\\local\\lib\\zig", + ); try testResolveWindows(&[_][]const u8{ "c:\\a\\b\\c", "/hi", "ok" }, "C:\\hi\\ok"); try testResolveWindows(&[_][]const u8{ "c:/blah\\blah", "d:/games", "c:../a" }, "C:\\blah\\a"); @@ -781,12 +747,12 @@ test "resolveWindows" { try testResolveWindows(&[_][]const u8{ "c:/", "//server//share" }, "\\\\server\\share\\"); try testResolveWindows(&[_][]const u8{ "c:/", "///some//dir" }, "C:\\some\\dir"); try testResolveWindows(&[_][]const u8{ "C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js" }, "C:\\foo\\tmp.3\\cycles\\root.js"); + + // Keep relative paths relative. + try testResolveWindows(&[_][]const u8{"a/b"}, "a\\b"); } test "resolvePosix" { - if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); - try testResolvePosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c"); try testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }, "/d/e"); try testResolvePosix(&[_][]const u8{ "/a/b/c", "..", "../" }, "/a"); @@ -797,18 +763,21 @@ test "resolvePosix" { try testResolvePosix(&[_][]const u8{ "/var/lib", "/../", "file/" }, "/file"); try testResolvePosix(&[_][]const u8{ "/some/dir", ".", "/absolute/" }, "/absolute"); try testResolvePosix(&[_][]const u8{ "/foo/tmp.3/", "../tmp.3/cycles/root.js" }, "/foo/tmp.3/cycles/root.js"); + + // Keep relative paths relative. + try testResolvePosix(&[_][]const u8{"a/b"}, "a/b"); } fn testResolveWindows(paths: []const []const u8, expected: []const u8) !void { const actual = try resolveWindows(testing.allocator, paths); defer testing.allocator.free(actual); - try testing.expect(mem.eql(u8, actual, expected)); + try testing.expectEqualStrings(expected, actual); } fn testResolvePosix(paths: []const []const u8, expected: []const u8) !void { const actual = try resolvePosix(testing.allocator, paths); defer testing.allocator.free(actual); - try testing.expect(mem.eql(u8, actual, expected)); + try testing.expectEqualStrings(expected, actual); } /// Strip the last component from a file path. @@ -1089,13 +1058,15 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) ! if (parsed_from.kind != parsed_to.kind) { break :x true; } else switch (parsed_from.kind) { - WindowsPath.Kind.NetworkShare => { + .NetworkShare => { break :x !networkShareServersEql(parsed_to.disk_designator, parsed_from.disk_designator); }, - WindowsPath.Kind.Drive => { + .Drive => { break :x ascii.toUpper(parsed_from.disk_designator[0]) != ascii.toUpper(parsed_to.disk_designator[0]); }, - else => unreachable, + .None => { + break :x false; + }, } }; @@ -1194,13 +1165,6 @@ pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![] } test "relative" { - if (builtin.target.cpu.arch == .aarch64) { - // TODO https://github.com/ziglang/zig/issues/3288 - return error.SkipZigTest; - } - if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; - if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/"); - try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games"); try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", ".."); try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc"); @@ -1226,6 +1190,10 @@ test "relative" { try testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz"); try testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz"); + try testRelativeWindows("a/b/c", "a\\b", ".."); + try testRelativeWindows("a/b/c", "a", "..\\.."); + try testRelativeWindows("a/b/c", "a\\b\\c\\d", "d"); + try testRelativePosix("/var/lib", "/var", ".."); try testRelativePosix("/var/lib", "/bin", "../../bin"); try testRelativePosix("/var/lib", "/var/lib", ""); @@ -1243,13 +1211,13 @@ test "relative" { fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) !void { const result = try relativePosix(testing.allocator, from, to); defer testing.allocator.free(result); - try testing.expectEqualSlices(u8, expected_output, result); + try testing.expectEqualStrings(expected_output, result); } fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) !void { const result = try relativeWindows(testing.allocator, from, to); defer testing.allocator.free(result); - try testing.expectEqualSlices(u8, expected_output, result); + try testing.expectEqualStrings(expected_output, result); } /// Returns the extension of the file name (if any). diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index f6168054b6..00e42b6417 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -1095,7 +1095,9 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" { const allocator = testing.allocator; - const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"}; + const cwd = try std.process.getCwdAlloc(allocator); + defer allocator.free(cwd); + const file_paths: [2][]const u8 = .{ cwd, "zig-test-absolute-paths.txt" }; const filename = try fs.path.resolve(allocator, &file_paths); defer allocator.free(filename); diff --git a/lib/std/fs/wasi.zig b/lib/std/fs/wasi.zig index 2051215dfe..6358873ede 100644 --- a/lib/std/fs/wasi.zig +++ b/lib/std/fs/wasi.zig @@ -202,10 +202,7 @@ pub const PreopenList = struct { // POSIX paths, relative to "/" or `cwd_root` depending on whether they start with "." const path = if (cwd_root) |cwd| blk: { const resolve_paths: []const []const u8 = if (raw_path[0] == '.') &.{ cwd, raw_path } else &.{ "/", raw_path }; - break :blk fs.path.resolve(self.buffer.allocator, resolve_paths) catch |err| switch (err) { - error.CurrentWorkingDirectoryUnlinked => unreachable, // root is absolute, so CWD not queried - else => |e| return e, - }; + break :blk try fs.path.resolve(self.buffer.allocator, resolve_paths); } else blk: { // If we were provided no CWD root, we preserve the preopen dir without resolving break :blk try self.buffer.allocator.dupe(u8, raw_path); diff --git a/lib/std/target.zig b/lib/std/target.zig index 745be7dbff..49a7bd1c7d 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1179,10 +1179,12 @@ pub const Target = struct { /// Returns whether this architecture supports the address space pub fn supportsAddressSpace(arch: Arch, address_space: std.builtin.AddressSpace) bool { const is_nvptx = arch == .nvptx or arch == .nvptx64; + const is_spirv = arch == .spirv32 or arch == .spirv64; + const is_gpu = is_nvptx or is_spirv or arch == .amdgcn; return switch (address_space) { .generic => true, .fs, .gs, .ss => arch == .x86_64 or arch == .x86, - .global, .constant, .local, .shared => arch == .amdgcn or is_nvptx, + .global, .constant, .local, .shared => is_gpu, .param => is_nvptx, }; } |
