diff --git a/build.zig b/build.zig index 42ab60c469..a15a38b0df 100644 --- a/build.zig +++ b/build.zig @@ -305,10 +305,14 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { dependOnLib(b, exe, ctx.llvm); if (exe.target.getOsTag() == .linux) { - try addCxxKnownPath(b, ctx, exe, "libstdc++.a", - \\Unable to determine path to libstdc++.a - \\On Fedora, install libstdc++-static and try again. - ); + // First we try to static link against gcc libstdc++. If that doesn't work, + // we fall back to -lc++ and cross our fingers. + addCxxKnownPath(b, ctx, exe, "libstdc++.a", "") catch |err| switch (err) { + error.RequiredLibraryNotFound => { + exe.linkSystemLibrary("c++"); + }, + else => |e| return e, + }; exe.linkSystemLibrary("pthread"); } else if (exe.target.isFreeBSD()) { @@ -327,7 +331,7 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { // System compiler, not gcc. exe.linkSystemLibrary("c++"); }, - else => return err, + else => |e| return e, } } diff --git a/doc/docgen.zig b/doc/docgen.zig index 4d2625f54f..32ad0cdc5d 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -48,7 +48,7 @@ pub fn main() !void { var toc = try genToc(allocator, &tokenizer); try fs.cwd().makePath(tmp_dir_name); - defer fs.deleteTree(tmp_dir_name) catch {}; + defer fs.cwd().deleteTree(tmp_dir_name) catch {}; try genHtml(allocator, &tokenizer, &toc, buffered_out_stream.outStream(), zig_exe); try buffered_out_stream.flush(); @@ -1096,6 +1096,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var try build_args.append("-lc"); try out.print(" -lc", .{}); } + const target = try std.zig.CrossTarget.parse(.{ + .arch_os_abi = code.target_str orelse "native", + }); if (code.target_str) |triple| { try build_args.appendSlice(&[_][]const u8{ "-target", triple }); if (!code.is_inline) { @@ -1150,7 +1153,15 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var } } - const path_to_exe = mem.trim(u8, exec_result.stdout, " \r\n"); + const path_to_exe_dir = mem.trim(u8, exec_result.stdout, " \r\n"); + const path_to_exe_basename = try std.fmt.allocPrint(allocator, "{}{}", .{ + code.name, + target.exeFileExt(), + }); + const path_to_exe = try fs.path.join(allocator, &[_][]const u8{ + path_to_exe_dir, + path_to_exe_basename, + }); const run_args = &[_][]const u8{path_to_exe}; var exited_with_signal = false; @@ -1486,7 +1497,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var } fn exec(allocator: *mem.Allocator, env_map: *std.BufMap, args: []const []const u8) !ChildProcess.ExecResult { - const result = try ChildProcess.exec(allocator, args, null, env_map, max_doc_file_size); + const result = try ChildProcess.exec2(.{ + .allocator = allocator, + .argv = args, + .env_map = env_map, + .max_output_bytes = max_doc_file_size, + }); switch (result.term) { .Exited => |exit_code| { if (exit_code != 0) { diff --git a/doc/langref.html.in b/doc/langref.html.in index 83e96d5c95..2e13367a00 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2093,8 +2093,9 @@ var foo: u8 align(4) = 100; test "global variable alignment" { assert(@TypeOf(&foo).alignment == 4); assert(@TypeOf(&foo) == *align(4) u8); - const slice = @as(*[1]u8, &foo)[0..]; - assert(@TypeOf(slice) == []align(4) u8); + const as_pointer_to_array: *[1]u8 = &foo; + const as_slice: []u8 = as_pointer_to_array; + assert(@TypeOf(as_slice) == []align(4) u8); } fn derp() align(@sizeOf(usize) * 2) i32 { return 1234; } @@ -2187,7 +2188,8 @@ test "basic slices" { // a slice is that the array's length is part of the type and known at // compile-time, whereas the slice's length is known at runtime. // Both can be accessed with the `len` field. - const slice = array[0..array.len]; + var known_at_runtime_zero: usize = 0; + const slice = array[known_at_runtime_zero..array.len]; assert(&slice[0] == &array[0]); assert(slice.len == array.len); @@ -2207,13 +2209,15 @@ test "basic slices" { {#code_end#}

This is one reason we prefer slices to pointers.

{#code_begin|test|slices#} -const assert = @import("std").debug.assert; -const mem = @import("std").mem; -const fmt = @import("std").fmt; +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; +const fmt = std.fmt; test "using slices for strings" { - // Zig has no concept of strings. String literals are arrays of u8, and - // in general the string type is []u8 (slice of u8). + // Zig has no concept of strings. String literals are const pointers to + // arrays of u8, and by convention parameters that are "strings" are + // expected to be UTF-8 encoded slices of u8. // Here we coerce [5]u8 to []const u8 const hello: []const u8 = "hello"; const world: []const u8 = "世界"; @@ -2222,7 +2226,7 @@ test "using slices for strings" { // You can use slice syntax on an array to convert an array into a slice. const all_together_slice = all_together[0..]; // String concatenation example. - const hello_world = try fmt.bufPrint(all_together_slice, "{} {}", .{hello, world}); + const hello_world = try fmt.bufPrint(all_together_slice, "{} {}", .{ hello, world }); // Generally, you can use UTF-8 and not worry about whether something is a // string. If you don't need to deal with individual characters, no need @@ -2239,23 +2243,15 @@ test "slice pointer" { slice[2] = 3; assert(slice[2] == 3); // The slice is mutable because we sliced a mutable pointer. - assert(@TypeOf(slice) == []u8); + // Furthermore, it is actually a pointer to an array, since the start + // and end indexes were both comptime-known. + assert(@TypeOf(slice) == *[5]u8); // You can also slice a slice: const slice2 = slice[2..3]; assert(slice2.len == 1); assert(slice2[0] == 3); } - -test "slice widening" { - // Zig supports slice widening and slice narrowing. Cast a slice of u8 - // to a slice of anything else, and Zig will perform the length conversion. - const array align(@alignOf(u32)) = [_]u8{ 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13 }; - const slice = mem.bytesAsSlice(u32, array[0..]); - assert(slice.len == 2); - assert(slice[0] == 0x12121212); - assert(slice[1] == 0x13131313); -} {#code_end#} {#see_also|Pointers|for|Arrays#} diff --git a/lib/std/build.zig b/lib/std/build.zig index e8484e9d1c..09c77168d6 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -377,7 +377,7 @@ pub const Builder = struct { if (self.verbose) { warn("rm {}\n", .{full_path}); } - fs.deleteTree(full_path) catch {}; + fs.cwd().deleteTree(full_path) catch {}; } // TODO remove empty directories @@ -847,7 +847,8 @@ pub const Builder = struct { if (self.verbose) { warn("cp {} {} ", .{ source_path, dest_path }); } - const prev_status = try fs.updateFile(source_path, dest_path); + const cwd = fs.cwd(); + const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{}); if (self.verbose) switch (prev_status) { .stale => warn("# installed\n", .{}), .fresh => warn("# up-to-date\n", .{}), @@ -1157,8 +1158,14 @@ pub const LibExeObjStep = struct { valgrind_support: ?bool = null, + /// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF + /// file. link_eh_frame_hdr: bool = false, + /// Place every function in its own section so that unused ones may be + /// safely garbage-collected during the linking phase. + link_function_sections: bool = false, + /// Uses system Wine installation to run cross compiled Windows build artifacts. enable_wine: bool = false, @@ -1884,7 +1891,9 @@ pub const LibExeObjStep = struct { if (self.link_eh_frame_hdr) { try zig_args.append("--eh-frame-hdr"); } - + if (self.link_function_sections) { + try zig_args.append("-ffunction-sections"); + } if (self.single_threaded) { try zig_args.append("--single-threaded"); } @@ -2144,17 +2153,22 @@ pub const LibExeObjStep = struct { try zig_args.append("--cache"); try zig_args.append("on"); - const output_path_nl = try builder.execFromStep(zig_args.toSliceConst(), &self.step); - const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); + const output_dir_nl = try builder.execFromStep(zig_args.toSliceConst(), &self.step); + const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n"); if (self.output_dir) |output_dir| { - const full_dest = try fs.path.join(builder.allocator, &[_][]const u8{ - output_dir, - fs.path.basename(output_path), - }); - try builder.updateFile(output_path, full_dest); + var src_dir = try std.fs.cwd().openDir(build_output_dir, .{ .iterate = true }); + defer src_dir.close(); + + var dest_dir = try std.fs.cwd().openDir(output_dir, .{}); + defer dest_dir.close(); + + var it = src_dir.iterate(); + while (try it.next()) |entry| { + _ = try src_dir.updateFile(entry.name, dest_dir, entry.name, .{}); + } } else { - self.output_dir = fs.path.dirname(output_path).?; + self.output_dir = build_output_dir; } } @@ -2352,7 +2366,7 @@ pub const RemoveDirStep = struct { const self = @fieldParentPtr(RemoveDirStep, "step", step); const full_path = self.builder.pathFromRoot(self.dir_path); - fs.deleteTree(full_path) catch |err| { + fs.cwd().deleteTree(full_path) catch |err| { warn("Unable to remove {}: {}\n", .{ full_path, @errorName(err) }); return err; }; diff --git a/lib/std/build/run.zig b/lib/std/build/run.zig index 91ecd56c3a..3276de9d19 100644 --- a/lib/std/build/run.zig +++ b/lib/std/build/run.zig @@ -29,6 +29,8 @@ pub const RunStep = struct { stdout_action: StdIoAction = .inherit, stderr_action: StdIoAction = .inherit, + stdin_behavior: std.ChildProcess.StdIo = .Inherit, + expected_exit_code: u8 = 0, pub const StdIoAction = union(enum) { @@ -159,7 +161,7 @@ pub const RunStep = struct { child.cwd = cwd; child.env_map = self.env_map orelse self.builder.env_map; - child.stdin_behavior = .Ignore; + child.stdin_behavior = self.stdin_behavior; child.stdout_behavior = stdIoActionToBehavior(self.stdout_action); child.stderr_behavior = stdIoActionToBehavior(self.stderr_action); diff --git a/lib/std/build/write_file.zig b/lib/std/build/write_file.zig index 60c54336e0..0c3f628457 100644 --- a/lib/std/build/write_file.zig +++ b/lib/std/build/write_file.zig @@ -78,7 +78,7 @@ pub const WriteFileStep = struct { warn("unable to make path {}: {}\n", .{ self.output_dir, @errorName(err) }); return err; }; - var dir = try fs.cwd().openDirTraverse(self.output_dir); + var dir = try fs.cwd().openDir(self.output_dir, .{}); defer dir.close(); for (self.files.toSliceConst()) |file| { dir.writeFile(file.basename, file.bytes) catch |err| { diff --git a/lib/std/c.zig b/lib/std/c.zig index 39a865ebbc..43b3d7f317 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -106,6 +106,7 @@ pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int; pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: u32) c_int; pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int; pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int; +pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, new: [*:0]const u8) c_int; pub extern "c" fn chdir(path: [*:0]const u8) c_int; pub extern "c" fn fchdir(fd: fd_t) c_int; pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int; diff --git a/lib/std/crypto/aes.zig b/lib/std/crypto/aes.zig index 1cc166f943..81dc56f0b3 100644 --- a/lib/std/crypto/aes.zig +++ b/lib/std/crypto/aes.zig @@ -15,10 +15,10 @@ fn rotw(w: u32) u32 { // Encrypt one block from src into dst, using the expanded key xk. fn encryptBlock(xk: []const u32, dst: []u8, src: []const u8) void { - var s0 = mem.readIntSliceBig(u32, src[0..4]); - var s1 = mem.readIntSliceBig(u32, src[4..8]); - var s2 = mem.readIntSliceBig(u32, src[8..12]); - var s3 = mem.readIntSliceBig(u32, src[12..16]); + var s0 = mem.readIntBig(u32, src[0..4]); + var s1 = mem.readIntBig(u32, src[4..8]); + var s2 = mem.readIntBig(u32, src[8..12]); + var s3 = mem.readIntBig(u32, src[12..16]); // First round just XORs input with key. s0 ^= xk[0]; @@ -58,18 +58,18 @@ fn encryptBlock(xk: []const u32, dst: []u8, src: []const u8) void { s2 ^= xk[k + 2]; s3 ^= xk[k + 3]; - mem.writeIntSliceBig(u32, dst[0..4], s0); - mem.writeIntSliceBig(u32, dst[4..8], s1); - mem.writeIntSliceBig(u32, dst[8..12], s2); - mem.writeIntSliceBig(u32, dst[12..16], s3); + mem.writeIntBig(u32, dst[0..4], s0); + mem.writeIntBig(u32, dst[4..8], s1); + mem.writeIntBig(u32, dst[8..12], s2); + mem.writeIntBig(u32, dst[12..16], s3); } // Decrypt one block from src into dst, using the expanded key xk. pub fn decryptBlock(xk: []const u32, dst: []u8, src: []const u8) void { - var s0 = mem.readIntSliceBig(u32, src[0..4]); - var s1 = mem.readIntSliceBig(u32, src[4..8]); - var s2 = mem.readIntSliceBig(u32, src[8..12]); - var s3 = mem.readIntSliceBig(u32, src[12..16]); + var s0 = mem.readIntBig(u32, src[0..4]); + var s1 = mem.readIntBig(u32, src[4..8]); + var s2 = mem.readIntBig(u32, src[8..12]); + var s3 = mem.readIntBig(u32, src[12..16]); // First round just XORs input with key. s0 ^= xk[0]; @@ -109,10 +109,10 @@ pub fn decryptBlock(xk: []const u32, dst: []u8, src: []const u8) void { s2 ^= xk[k + 2]; s3 ^= xk[k + 3]; - mem.writeIntSliceBig(u32, dst[0..4], s0); - mem.writeIntSliceBig(u32, dst[4..8], s1); - mem.writeIntSliceBig(u32, dst[8..12], s2); - mem.writeIntSliceBig(u32, dst[12..16], s3); + mem.writeIntBig(u32, dst[0..4], s0); + mem.writeIntBig(u32, dst[4..8], s1); + mem.writeIntBig(u32, dst[8..12], s2); + mem.writeIntBig(u32, dst[12..16], s3); } fn xorBytes(dst: []u8, a: []const u8, b: []const u8) usize { @@ -154,8 +154,8 @@ fn AES(comptime keysize: usize) type { var n: usize = 0; while (n < src.len) { ctx.encrypt(keystream[0..], ctrbuf[0..]); - var ctr_i = std.mem.readIntSliceBig(u128, ctrbuf[0..]); - std.mem.writeIntSliceBig(u128, ctrbuf[0..], ctr_i +% 1); + var ctr_i = std.mem.readIntBig(u128, ctrbuf[0..]); + std.mem.writeIntBig(u128, ctrbuf[0..], ctr_i +% 1); n += xorBytes(dst[n..], src[n..], &keystream); } @@ -251,7 +251,7 @@ fn expandKey(key: []const u8, enc: []u32, dec: []u32) void { var i: usize = 0; var nk = key.len / 4; while (i < nk) : (i += 1) { - enc[i] = mem.readIntSliceBig(u32, key[4 * i .. 4 * i + 4]); + enc[i] = mem.readIntBig(u32, key[4 * i ..][0..4]); } while (i < enc.len) : (i += 1) { var t = enc[i - 1]; diff --git a/lib/std/crypto/blake2.zig b/lib/std/crypto/blake2.zig index e03d8f7dab..fc1d59290e 100644 --- a/lib/std/crypto/blake2.zig +++ b/lib/std/crypto/blake2.zig @@ -123,8 +123,7 @@ fn Blake2s(comptime out_len: usize) type { const rr = d.h[0 .. out_len / 32]; for (rr) |s, j| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceLittle(u32, out[4 * j .. 4 * j + 4], s); + mem.writeIntLittle(u32, out[4 * j ..][0..4], s); } } @@ -135,8 +134,7 @@ fn Blake2s(comptime out_len: usize) type { var v: [16]u32 = undefined; for (m) |*r, i| { - // TODO https://github.com/ziglang/zig/issues/863 - r.* = mem.readIntSliceLittle(u32, b[4 * i .. 4 * i + 4]); + r.* = mem.readIntLittle(u32, b[4 * i ..][0..4]); } var k: usize = 0; @@ -358,8 +356,7 @@ fn Blake2b(comptime out_len: usize) type { const rr = d.h[0 .. out_len / 64]; for (rr) |s, j| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceLittle(u64, out[8 * j .. 8 * j + 8], s); + mem.writeIntLittle(u64, out[8 * j ..][0..8], s); } } @@ -370,7 +367,7 @@ fn Blake2b(comptime out_len: usize) type { var v: [16]u64 = undefined; for (m) |*r, i| { - r.* = mem.readIntSliceLittle(u64, b[8 * i .. 8 * i + 8]); + r.* = mem.readIntLittle(u64, b[8 * i ..][0..8]); } var k: usize = 0; diff --git a/lib/std/crypto/chacha20.zig b/lib/std/crypto/chacha20.zig index d67877b051..f6008745af 100644 --- a/lib/std/crypto/chacha20.zig +++ b/lib/std/crypto/chacha20.zig @@ -61,8 +61,7 @@ fn salsa20_wordtobyte(out: []u8, input: [16]u32) void { } for (x) |_, i| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceLittle(u32, out[4 * i .. 4 * i + 4], x[i] +% input[i]); + mem.writeIntLittle(u32, out[4 * i ..][0..4], x[i] +% input[i]); } } @@ -73,10 +72,10 @@ fn chaCha20_internal(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) vo const c = "expand 32-byte k"; const constant_le = [_]u32{ - mem.readIntSliceLittle(u32, c[0..4]), - mem.readIntSliceLittle(u32, c[4..8]), - mem.readIntSliceLittle(u32, c[8..12]), - mem.readIntSliceLittle(u32, c[12..16]), + mem.readIntLittle(u32, c[0..4]), + mem.readIntLittle(u32, c[4..8]), + mem.readIntLittle(u32, c[8..12]), + mem.readIntLittle(u32, c[12..16]), }; mem.copy(u32, ctx[0..], constant_le[0..4]); @@ -120,19 +119,19 @@ pub fn chaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: var k: [8]u32 = undefined; var c: [4]u32 = undefined; - k[0] = mem.readIntSliceLittle(u32, key[0..4]); - k[1] = mem.readIntSliceLittle(u32, key[4..8]); - k[2] = mem.readIntSliceLittle(u32, key[8..12]); - k[3] = mem.readIntSliceLittle(u32, key[12..16]); - k[4] = mem.readIntSliceLittle(u32, key[16..20]); - k[5] = mem.readIntSliceLittle(u32, key[20..24]); - k[6] = mem.readIntSliceLittle(u32, key[24..28]); - k[7] = mem.readIntSliceLittle(u32, key[28..32]); + k[0] = mem.readIntLittle(u32, key[0..4]); + k[1] = mem.readIntLittle(u32, key[4..8]); + k[2] = mem.readIntLittle(u32, key[8..12]); + k[3] = mem.readIntLittle(u32, key[12..16]); + k[4] = mem.readIntLittle(u32, key[16..20]); + k[5] = mem.readIntLittle(u32, key[20..24]); + k[6] = mem.readIntLittle(u32, key[24..28]); + k[7] = mem.readIntLittle(u32, key[28..32]); c[0] = counter; - c[1] = mem.readIntSliceLittle(u32, nonce[0..4]); - c[2] = mem.readIntSliceLittle(u32, nonce[4..8]); - c[3] = mem.readIntSliceLittle(u32, nonce[8..12]); + c[1] = mem.readIntLittle(u32, nonce[0..4]); + c[2] = mem.readIntLittle(u32, nonce[4..8]); + c[3] = mem.readIntLittle(u32, nonce[8..12]); chaCha20_internal(out, in, k, c); } @@ -147,19 +146,19 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32] var k: [8]u32 = undefined; var c: [4]u32 = undefined; - k[0] = mem.readIntSliceLittle(u32, key[0..4]); - k[1] = mem.readIntSliceLittle(u32, key[4..8]); - k[2] = mem.readIntSliceLittle(u32, key[8..12]); - k[3] = mem.readIntSliceLittle(u32, key[12..16]); - k[4] = mem.readIntSliceLittle(u32, key[16..20]); - k[5] = mem.readIntSliceLittle(u32, key[20..24]); - k[6] = mem.readIntSliceLittle(u32, key[24..28]); - k[7] = mem.readIntSliceLittle(u32, key[28..32]); + k[0] = mem.readIntLittle(u32, key[0..4]); + k[1] = mem.readIntLittle(u32, key[4..8]); + k[2] = mem.readIntLittle(u32, key[8..12]); + k[3] = mem.readIntLittle(u32, key[12..16]); + k[4] = mem.readIntLittle(u32, key[16..20]); + k[5] = mem.readIntLittle(u32, key[20..24]); + k[6] = mem.readIntLittle(u32, key[24..28]); + k[7] = mem.readIntLittle(u32, key[28..32]); c[0] = @truncate(u32, counter); c[1] = @truncate(u32, counter >> 32); - c[2] = mem.readIntSliceLittle(u32, nonce[0..4]); - c[3] = mem.readIntSliceLittle(u32, nonce[4..8]); + c[2] = mem.readIntLittle(u32, nonce[0..4]); + c[3] = mem.readIntLittle(u32, nonce[4..8]); const block_size = (1 << 6); // The full block size is greater than the address space on a 32bit machine @@ -463,8 +462,8 @@ pub fn chacha20poly1305Seal(dst: []u8, plaintext: []const u8, data: []const u8, mac.update(zeros[0..padding]); } var lens: [16]u8 = undefined; - mem.writeIntSliceLittle(u64, lens[0..8], data.len); - mem.writeIntSliceLittle(u64, lens[8..16], plaintext.len); + mem.writeIntLittle(u64, lens[0..8], data.len); + mem.writeIntLittle(u64, lens[8..16], plaintext.len); mac.update(lens[0..]); mac.final(dst[plaintext.len..]); } @@ -500,8 +499,8 @@ pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []const u8, data: []const u8, mac.update(zeros[0..padding]); } var lens: [16]u8 = undefined; - mem.writeIntSliceLittle(u64, lens[0..8], data.len); - mem.writeIntSliceLittle(u64, lens[8..16], ciphertext.len); + mem.writeIntLittle(u64, lens[0..8], data.len); + mem.writeIntLittle(u64, lens[8..16], ciphertext.len); mac.update(lens[0..]); var computedTag: [16]u8 = undefined; mac.final(computedTag[0..]); diff --git a/lib/std/crypto/md5.zig b/lib/std/crypto/md5.zig index d9dd08c904..ac8948ca20 100644 --- a/lib/std/crypto/md5.zig +++ b/lib/std/crypto/md5.zig @@ -112,8 +112,7 @@ pub const Md5 = struct { d.round(d.buf[0..]); for (d.s) |s, j| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceLittle(u32, out[4 * j .. 4 * j + 4], s); + mem.writeIntLittle(u32, out[4 * j ..][0..4], s); } } diff --git a/lib/std/crypto/poly1305.zig b/lib/std/crypto/poly1305.zig index 2395b1c7aa..fda978307d 100644 --- a/lib/std/crypto/poly1305.zig +++ b/lib/std/crypto/poly1305.zig @@ -3,11 +3,11 @@ // https://monocypher.org/ const std = @import("../std.zig"); -const builtin = @import("builtin"); +const builtin = std.builtin; const Endian = builtin.Endian; -const readIntSliceLittle = std.mem.readIntSliceLittle; -const writeIntSliceLittle = std.mem.writeIntSliceLittle; +const readIntLittle = std.mem.readIntLittle; +const writeIntLittle = std.mem.writeIntLittle; pub const Poly1305 = struct { const Self = @This(); @@ -59,19 +59,19 @@ pub const Poly1305 = struct { { var i: usize = 0; while (i < 1) : (i += 1) { - ctx.r[0] = readIntSliceLittle(u32, key[0..4]) & 0x0fffffff; + ctx.r[0] = readIntLittle(u32, key[0..4]) & 0x0fffffff; } } { var i: usize = 1; while (i < 4) : (i += 1) { - ctx.r[i] = readIntSliceLittle(u32, key[i * 4 .. i * 4 + 4]) & 0x0ffffffc; + ctx.r[i] = readIntLittle(u32, key[i * 4 ..][0..4]) & 0x0ffffffc; } } { var i: usize = 0; while (i < 4) : (i += 1) { - ctx.pad[i] = readIntSliceLittle(u32, key[i * 4 + 16 .. i * 4 + 16 + 4]); + ctx.pad[i] = readIntLittle(u32, key[i * 4 + 16 ..][0..4]); } } @@ -168,10 +168,10 @@ pub const Poly1305 = struct { const nb_blocks = nmsg.len >> 4; var i: usize = 0; while (i < nb_blocks) : (i += 1) { - ctx.c[0] = readIntSliceLittle(u32, nmsg[0..4]); - ctx.c[1] = readIntSliceLittle(u32, nmsg[4..8]); - ctx.c[2] = readIntSliceLittle(u32, nmsg[8..12]); - ctx.c[3] = readIntSliceLittle(u32, nmsg[12..16]); + ctx.c[0] = readIntLittle(u32, nmsg[0..4]); + ctx.c[1] = readIntLittle(u32, nmsg[4..8]); + ctx.c[2] = readIntLittle(u32, nmsg[8..12]); + ctx.c[3] = readIntLittle(u32, nmsg[12..16]); polyBlock(ctx); nmsg = nmsg[16..]; } @@ -210,11 +210,10 @@ pub const Poly1305 = struct { const uu2 = (uu1 >> 32) + ctx.h[2] + ctx.pad[2]; // <= 2_00000000 const uu3 = (uu2 >> 32) + ctx.h[3] + ctx.pad[3]; // <= 2_00000000 - // TODO https://github.com/ziglang/zig/issues/863 - writeIntSliceLittle(u32, out[0..], @truncate(u32, uu0)); - writeIntSliceLittle(u32, out[4..], @truncate(u32, uu1)); - writeIntSliceLittle(u32, out[8..], @truncate(u32, uu2)); - writeIntSliceLittle(u32, out[12..], @truncate(u32, uu3)); + writeIntLittle(u32, out[0..4], @truncate(u32, uu0)); + writeIntLittle(u32, out[4..8], @truncate(u32, uu1)); + writeIntLittle(u32, out[8..12], @truncate(u32, uu2)); + writeIntLittle(u32, out[12..16], @truncate(u32, uu3)); ctx.secureZero(); } diff --git a/lib/std/crypto/sha1.zig b/lib/std/crypto/sha1.zig index 5be42180a1..6edf7b745e 100644 --- a/lib/std/crypto/sha1.zig +++ b/lib/std/crypto/sha1.zig @@ -109,8 +109,7 @@ pub const Sha1 = struct { d.round(d.buf[0..]); for (d.s) |s, j| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceBig(u32, out[4 * j .. 4 * j + 4], s); + mem.writeIntBig(u32, out[4 * j ..][0..4], s); } } diff --git a/lib/std/crypto/sha2.zig b/lib/std/crypto/sha2.zig index fd7ad532a3..f004bceac3 100644 --- a/lib/std/crypto/sha2.zig +++ b/lib/std/crypto/sha2.zig @@ -167,8 +167,7 @@ fn Sha2_32(comptime params: Sha2Params32) type { const rr = d.s[0 .. params.out_len / 32]; for (rr) |s, j| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceBig(u32, out[4 * j .. 4 * j + 4], s); + mem.writeIntBig(u32, out[4 * j ..][0..4], s); } } @@ -509,8 +508,7 @@ fn Sha2_64(comptime params: Sha2Params64) type { const rr = d.s[0 .. params.out_len / 64]; for (rr) |s, j| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceBig(u64, out[8 * j .. 8 * j + 8], s); + mem.writeIntBig(u64, out[8 * j ..][0..8], s); } } diff --git a/lib/std/crypto/sha3.zig b/lib/std/crypto/sha3.zig index d7b2fbe256..7c60674d75 100644 --- a/lib/std/crypto/sha3.zig +++ b/lib/std/crypto/sha3.zig @@ -120,7 +120,7 @@ fn keccak_f(comptime F: usize, d: []u8) void { var c = [_]u64{0} ** 5; for (s) |*r, i| { - r.* = mem.readIntSliceLittle(u64, d[8 * i .. 8 * i + 8]); + r.* = mem.readIntLittle(u64, d[8 * i ..][0..8]); } comptime var x: usize = 0; @@ -167,8 +167,7 @@ fn keccak_f(comptime F: usize, d: []u8) void { } for (s) |r, i| { - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntSliceLittle(u64, d[8 * i .. 8 * i + 8], r); + mem.writeIntLittle(u64, d[8 * i ..][0..8], r); } } diff --git a/lib/std/crypto/x25519.zig b/lib/std/crypto/x25519.zig index 16e3f073f8..e2e2bf90e5 100644 --- a/lib/std/crypto/x25519.zig +++ b/lib/std/crypto/x25519.zig @@ -7,8 +7,8 @@ const builtin = @import("builtin"); const fmt = std.fmt; const Endian = builtin.Endian; -const readIntSliceLittle = std.mem.readIntSliceLittle; -const writeIntSliceLittle = std.mem.writeIntSliceLittle; +const readIntLittle = std.mem.readIntLittle; +const writeIntLittle = std.mem.writeIntLittle; // Based on Supercop's ref10 implementation. pub const X25519 = struct { @@ -255,16 +255,16 @@ const Fe = struct { var t: [10]i64 = undefined; - t[0] = readIntSliceLittle(u32, s[0..4]); - t[1] = @as(u32, readIntSliceLittle(u24, s[4..7])) << 6; - t[2] = @as(u32, readIntSliceLittle(u24, s[7..10])) << 5; - t[3] = @as(u32, readIntSliceLittle(u24, s[10..13])) << 3; - t[4] = @as(u32, readIntSliceLittle(u24, s[13..16])) << 2; - t[5] = readIntSliceLittle(u32, s[16..20]); - t[6] = @as(u32, readIntSliceLittle(u24, s[20..23])) << 7; - t[7] = @as(u32, readIntSliceLittle(u24, s[23..26])) << 5; - t[8] = @as(u32, readIntSliceLittle(u24, s[26..29])) << 4; - t[9] = (@as(u32, readIntSliceLittle(u24, s[29..32])) & 0x7fffff) << 2; + t[0] = readIntLittle(u32, s[0..4]); + t[1] = @as(u32, readIntLittle(u24, s[4..7])) << 6; + t[2] = @as(u32, readIntLittle(u24, s[7..10])) << 5; + t[3] = @as(u32, readIntLittle(u24, s[10..13])) << 3; + t[4] = @as(u32, readIntLittle(u24, s[13..16])) << 2; + t[5] = readIntLittle(u32, s[16..20]); + t[6] = @as(u32, readIntLittle(u24, s[20..23])) << 7; + t[7] = @as(u32, readIntLittle(u24, s[23..26])) << 5; + t[8] = @as(u32, readIntLittle(u24, s[26..29])) << 4; + t[9] = (@as(u32, readIntLittle(u24, s[29..32])) & 0x7fffff) << 2; carry1(h, t[0..]); } @@ -544,15 +544,14 @@ const Fe = struct { ut[i] = @bitCast(u32, @intCast(i32, t[i])); } - // TODO https://github.com/ziglang/zig/issues/863 - writeIntSliceLittle(u32, s[0..4], (ut[0] >> 0) | (ut[1] << 26)); - writeIntSliceLittle(u32, s[4..8], (ut[1] >> 6) | (ut[2] << 19)); - writeIntSliceLittle(u32, s[8..12], (ut[2] >> 13) | (ut[3] << 13)); - writeIntSliceLittle(u32, s[12..16], (ut[3] >> 19) | (ut[4] << 6)); - writeIntSliceLittle(u32, s[16..20], (ut[5] >> 0) | (ut[6] << 25)); - writeIntSliceLittle(u32, s[20..24], (ut[6] >> 7) | (ut[7] << 19)); - writeIntSliceLittle(u32, s[24..28], (ut[7] >> 13) | (ut[8] << 12)); - writeIntSliceLittle(u32, s[28..], (ut[8] >> 20) | (ut[9] << 6)); + writeIntLittle(u32, s[0..4], (ut[0] >> 0) | (ut[1] << 26)); + writeIntLittle(u32, s[4..8], (ut[1] >> 6) | (ut[2] << 19)); + writeIntLittle(u32, s[8..12], (ut[2] >> 13) | (ut[3] << 13)); + writeIntLittle(u32, s[12..16], (ut[3] >> 19) | (ut[4] << 6)); + writeIntLittle(u32, s[16..20], (ut[5] >> 0) | (ut[6] << 25)); + writeIntLittle(u32, s[20..24], (ut[6] >> 7) | (ut[7] << 19)); + writeIntLittle(u32, s[24..28], (ut[7] >> 13) | (ut[8] << 12)); + writeIntLittle(u32, s[28..32], (ut[8] >> 20) | (ut[9] << 6)); std.mem.secureZero(i64, t[0..]); } diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index a7d6d6928e..bcc470e9b9 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -1223,7 +1223,8 @@ test "slice" { try testFmt("slice: abc\n", "slice: {}\n", .{value}); } { - const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[0..0]; + var runtime_zero: usize = 0; + const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[runtime_zero..runtime_zero]; try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value}); } diff --git a/lib/std/fs.zig b/lib/std/fs.zig index de6be91f71..333419d02b 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -81,134 +81,74 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: } } -// TODO fix enum literal not casting to error union -const PrevStatus = enum { +pub const PrevStatus = enum { stale, fresh, }; -pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus { - return updateFileMode(source_path, dest_path, null); -} +pub const CopyFileOptions = struct { + /// When this is `null` the mode is copied from the source file. + override_mode: ?File.Mode = null, +}; -/// Check the file size, mtime, and mode of `source_path` and `dest_path`. If they are equal, does nothing. -/// Otherwise, atomically copies `source_path` to `dest_path`. The destination file gains the mtime, -/// atime, and mode of the source file so that the next call to `updateFile` will not need a copy. -/// Returns the previous status of the file before updating. -/// If any of the directories do not exist for dest_path, they are created. -/// TODO rework this to integrate with Dir -pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus { +/// Same as `Dir.updateFile`, except asserts that both `source_path` and `dest_path` +/// are absolute. See `Dir.updateFile` for a function that operates on both +/// absolute and relative paths. +pub fn updateFileAbsolute( + source_path: []const u8, + dest_path: []const u8, + args: CopyFileOptions, +) !PrevStatus { + assert(path.isAbsolute(source_path)); + assert(path.isAbsolute(dest_path)); const my_cwd = cwd(); - - var src_file = try my_cwd.openFile(source_path, .{}); - defer src_file.close(); - - const src_stat = try src_file.stat(); - check_dest_stat: { - const dest_stat = blk: { - var dest_file = my_cwd.openFile(dest_path, .{}) catch |err| switch (err) { - error.FileNotFound => break :check_dest_stat, - else => |e| return e, - }; - defer dest_file.close(); - - break :blk try dest_file.stat(); - }; - - if (src_stat.size == dest_stat.size and - src_stat.mtime == dest_stat.mtime and - src_stat.mode == dest_stat.mode) - { - return PrevStatus.fresh; - } - } - const actual_mode = mode orelse src_stat.mode; - - if (path.dirname(dest_path)) |dirname| { - try cwd().makePath(dirname); - } - - var atomic_file = try AtomicFile.init(dest_path, actual_mode); - defer atomic_file.deinit(); - - try atomic_file.file.writeFileAll(src_file, .{ .in_len = src_stat.size }); - try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime); - try atomic_file.finish(); - return PrevStatus.stale; + return Dir.updateFile(my_cwd, source_path, my_cwd, dest_path, args); } -/// Guaranteed to be atomic. -/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available, -/// there is a possibility of power loss or application termination leaving temporary files present -/// in the same directory as dest_path. -/// Destination file will have the same mode as the source file. -/// TODO rework this to integrate with Dir -pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { - var in_file = try cwd().openFile(source_path, .{}); - defer in_file.close(); - - const stat = try in_file.stat(); - - var atomic_file = try AtomicFile.init(dest_path, stat.mode); - defer atomic_file.deinit(); - - try atomic_file.file.writeFileAll(in_file, .{ .in_len = stat.size }); - return atomic_file.finish(); +/// Same as `Dir.copyFile`, except asserts that both `source_path` and `dest_path` +/// are absolute. See `Dir.copyFile` for a function that operates on both +/// absolute and relative paths. +pub fn copyFileAbsolute(source_path: []const u8, dest_path: []const u8, args: CopyFileOptions) !void { + assert(path.isAbsolute(source_path)); + assert(path.isAbsolute(dest_path)); + const my_cwd = cwd(); + return Dir.copyFile(my_cwd, source_path, my_cwd, dest_path, args); } -/// Guaranteed to be atomic. -/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available, -/// there is a possibility of power loss or application termination leaving temporary files present -/// in the same directory as dest_path. -/// TODO rework this to integrate with Dir -pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { - var in_file = try cwd().openFile(source_path, .{}); - defer in_file.close(); - - var atomic_file = try AtomicFile.init(dest_path, mode); - defer atomic_file.deinit(); - - try atomic_file.file.writeFileAll(in_file, .{}); - return atomic_file.finish(); -} - -/// TODO update this API to avoid a getrandom syscall for every operation. It -/// should accept a random interface. -/// TODO rework this to integrate with Dir +/// TODO update this API to avoid a getrandom syscall for every operation. pub const AtomicFile = struct { file: File, - tmp_path_buf: [MAX_PATH_BYTES]u8, + tmp_path_buf: [MAX_PATH_BYTES - 1:0]u8, dest_path: []const u8, - finished: bool, + file_open: bool, + file_exists: bool, + dir: Dir, const InitError = File.OpenError; - /// dest_path must remain valid for the lifetime of AtomicFile - /// call finish to atomically replace dest_path with contents - pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile { + /// TODO rename this. Callers should go through Dir API + pub fn init2(dest_path: []const u8, mode: File.Mode, dir: Dir) InitError!AtomicFile { const dirname = path.dirname(dest_path); var rand_buf: [12]u8 = undefined; const dirname_component_len = if (dirname) |d| d.len + 1 else 0; const encoded_rand_len = comptime base64.Base64Encoder.calcSize(rand_buf.len); const tmp_path_len = dirname_component_len + encoded_rand_len; - var tmp_path_buf: [MAX_PATH_BYTES]u8 = undefined; - if (tmp_path_len >= tmp_path_buf.len) return error.NameTooLong; + var tmp_path_buf: [MAX_PATH_BYTES - 1:0]u8 = undefined; + if (tmp_path_len > tmp_path_buf.len) return error.NameTooLong; - if (dirname) |dir| { - mem.copy(u8, tmp_path_buf[0..], dir); - tmp_path_buf[dir.len] = path.sep; + if (dirname) |dn| { + mem.copy(u8, tmp_path_buf[0..], dn); + tmp_path_buf[dn.len] = path.sep; } tmp_path_buf[tmp_path_len] = 0; const tmp_path_slice = tmp_path_buf[0..tmp_path_len :0]; - const my_cwd = cwd(); - while (true) { try crypto.randomBytes(rand_buf[0..]); base64_encoder.encode(tmp_path_slice[dirname_component_len..tmp_path_len], &rand_buf); - const file = my_cwd.createFileC( + const file = dir.createFileC( tmp_path_slice, .{ .mode = mode, .exclusive = true }, ) catch |err| switch (err) { @@ -220,33 +160,46 @@ pub const AtomicFile = struct { .file = file, .tmp_path_buf = tmp_path_buf, .dest_path = dest_path, - .finished = false, + .file_open = true, + .file_exists = true, + .dir = dir, }; } } + /// Deprecated. Use `Dir.atomicFile`. + pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile { + return init2(dest_path, mode, cwd()); + } + /// always call deinit, even after successful finish() pub fn deinit(self: *AtomicFile) void { - if (!self.finished) { + if (self.file_open) { self.file.close(); - cwd().deleteFileC(@ptrCast([*:0]u8, &self.tmp_path_buf)) catch {}; - self.finished = true; + self.file_open = false; } + if (self.file_exists) { + self.dir.deleteFileC(&self.tmp_path_buf) catch {}; + self.file_exists = false; + } + self.* = undefined; } pub fn finish(self: *AtomicFile) !void { - assert(!self.finished); + assert(self.file_exists); + if (self.file_open) { + self.file.close(); + self.file_open = false; + } if (std.Target.current.os.tag == .windows) { const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_path); - const tmp_path_w = try os.windows.cStrToPrefixedFileW(@ptrCast([*:0]u8, &self.tmp_path_buf)); - self.file.close(); - self.finished = true; - return os.renameW(&tmp_path_w, &dest_path_w); + const tmp_path_w = try os.windows.cStrToPrefixedFileW(&self.tmp_path_buf); + try os.renameatW(self.dir.fd, &tmp_path_w, self.dir.fd, &dest_path_w, os.windows.TRUE); + self.file_exists = false; } else { const dest_path_c = try os.toPosixPath(self.dest_path); - self.file.close(); - self.finished = true; - return os.renameC(@ptrCast([*:0]u8, &self.tmp_path_buf), &dest_path_c); + try os.renameatZ(self.dir.fd, &self.tmp_path_buf, self.dir.fd, &dest_path_c); + self.file_exists = false; } } }; @@ -274,44 +227,21 @@ pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void { os.windows.CloseHandle(handle); } -/// Returns `error.DirNotEmpty` if the directory is not empty. -/// To delete a directory recursively, see `deleteTree`. +/// Deprecated; use `Dir.deleteDir`. pub fn deleteDir(dir_path: []const u8) !void { return os.rmdir(dir_path); } -/// Same as `deleteDir` except the parameter is a null-terminated UTF8-encoded string. +/// Deprecated; use `Dir.deleteDirC`. pub fn deleteDirC(dir_path: [*:0]const u8) !void { return os.rmdirC(dir_path); } -/// Same as `deleteDir` except the parameter is a null-terminated UTF16LE-encoded string. +/// Deprecated; use `Dir.deleteDirW`. pub fn deleteDirW(dir_path: [*:0]const u16) !void { return os.rmdirW(dir_path); } -/// Removes a symlink, file, or directory. -/// If `full_path` is relative, this is equivalent to `Dir.deleteTree` with the -/// current working directory as the open directory handle. -/// If `full_path` is absolute, this is equivalent to `Dir.deleteTree` with the -/// base directory. -pub fn deleteTree(full_path: []const u8) !void { - if (path.isAbsolute(full_path)) { - const dirname = path.dirname(full_path) orelse return error{ - /// Attempt to remove the root file system path. - /// This error is unreachable if `full_path` is relative. - CannotDeleteRootDirectory, - }.CannotDeleteRootDirectory; - - var dir = try cwd().openDirList(dirname); - defer dir.close(); - - return dir.deleteTree(path.basename(full_path)); - } else { - return cwd().deleteTree(full_path); - } -} - pub const Dir = struct { fd: os.fd_t, @@ -368,7 +298,7 @@ pub const Dir = struct { if (rc == 0) return null; if (rc < 0) { switch (os.errno(rc)) { - os.EBADF => unreachable, + os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability os.EFAULT => unreachable, os.ENOTDIR => unreachable, os.EINVAL => unreachable, @@ -411,13 +341,13 @@ pub const Dir = struct { if (self.index >= self.end_index) { const rc = os.system.getdirentries( self.dir.fd, - self.buf[0..].ptr, + &self.buf, self.buf.len, &self.seek, ); switch (os.errno(rc)) { 0 => {}, - os.EBADF => unreachable, + os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability os.EFAULT => unreachable, os.ENOTDIR => unreachable, os.EINVAL => unreachable, @@ -473,7 +403,7 @@ pub const Dir = struct { const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len); switch (os.linux.getErrno(rc)) { 0 => {}, - os.EBADF => unreachable, + os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability os.EFAULT => unreachable, os.ENOTDIR => unreachable, os.EINVAL => unreachable, @@ -547,7 +477,8 @@ pub const Dir = struct { self.end_index = io.Information; switch (rc) { .SUCCESS => {}, - .ACCESS_DENIED => return error.AccessDenied, + .ACCESS_DENIED => return error.AccessDenied, // Double-check that the Dir was opened with iteration ability + else => return w.unexpectedStatus(rc), } } @@ -625,16 +556,6 @@ pub const Dir = struct { DeviceBusy, } || os.UnexpectedError; - /// Deprecated; call `cwd().openDirList` directly. - pub fn open(dir_path: []const u8) OpenError!Dir { - return cwd().openDirList(dir_path); - } - - /// Deprecated; call `cwd().openDirListC` directly. - pub fn openC(dir_path_c: [*:0]const u8) OpenError!Dir { - return cwd().openDirListC(dir_path_c); - } - pub fn close(self: *Dir) void { if (need_async_thread) { std.event.Loop.instance.?.close(self.fd); @@ -694,7 +615,10 @@ pub const Dir = struct { const access_mask = w.SYNCHRONIZE | (if (flags.read) @as(u32, w.GENERIC_READ) else 0) | (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0); - return self.openFileWindows(sub_path_w, access_mask, w.FILE_OPEN); + return @as(File, .{ + .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, w.FILE_OPEN), + .io_mode = .blocking, + }); } /// Creates, opens, or overwrites a file with write access. @@ -739,7 +663,10 @@ pub const Dir = struct { @as(u32, w.FILE_OVERWRITE_IF) else @as(u32, w.FILE_OPEN_IF); - return self.openFileWindows(sub_path_w, access_mask, creation); + return @as(File, .{ + .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, creation), + .io_mode = .blocking, + }); } /// Deprecated; call `openFile` directly. @@ -757,72 +684,6 @@ pub const Dir = struct { return self.openFileW(sub_path, .{}); } - pub fn openFileWindows( - self: Dir, - sub_path_w: [*:0]const u16, - access_mask: os.windows.ACCESS_MASK, - creation: os.windows.ULONG, - ) File.OpenError!File { - const w = os.windows; - - if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { - return error.IsDir; - } - if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { - return error.IsDir; - } - - var result = File{ - .handle = undefined, - .io_mode = .blocking, - }; - - const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) { - error.Overflow => return error.NameTooLong, - }; - var nt_name = w.UNICODE_STRING{ - .Length = path_len_bytes, - .MaximumLength = path_len_bytes, - .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), - }; - var attr = w.OBJECT_ATTRIBUTES{ - .Length = @sizeOf(w.OBJECT_ATTRIBUTES), - .RootDirectory = if (path.isAbsoluteWindowsW(sub_path_w)) null else self.fd, - .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. - .ObjectName = &nt_name, - .SecurityDescriptor = null, - .SecurityQualityOfService = null, - }; - var io: w.IO_STATUS_BLOCK = undefined; - const rc = w.ntdll.NtCreateFile( - &result.handle, - access_mask, - &attr, - &io, - null, - w.FILE_ATTRIBUTE_NORMAL, - w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, - creation, - w.FILE_NON_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT, - null, - 0, - ); - switch (rc) { - .SUCCESS => return result, - .OBJECT_NAME_INVALID => unreachable, - .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, - .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, - .NO_MEDIA_IN_DEVICE => return error.NoDevice, - .INVALID_PARAMETER => unreachable, - .SHARING_VIOLATION => return error.SharingViolation, - .ACCESS_DENIED => return error.AccessDenied, - .PIPE_BUSY => return error.PipeBusy, - .OBJECT_PATH_SYNTAX_BAD => unreachable, - .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, - else => return w.unexpectedStatus(rc), - } - } - pub fn makeDir(self: Dir, sub_path: []const u8) !void { try os.mkdirat(self.fd, sub_path, default_new_dir_mode); } @@ -881,77 +742,61 @@ pub const Dir = struct { try os.fchdir(self.fd); } - /// Deprecated; call `openDirList` directly. - pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir { - return self.openDirList(sub_path); - } + pub const OpenDirOptions = struct { + /// `true` means the opened directory can be used as the `Dir` parameter + /// for functions which operate based on an open directory handle. When `false`, + /// such operations are Illegal Behavior. + access_sub_paths: bool = true, - /// Deprecated; call `openDirListC` directly. - pub fn openDirC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { - return self.openDirListC(sub_path_c); - } + /// `true` means the opened directory can be scanned for the files and sub-directories + /// of the result. It means the `iterate` function can be called. + iterate: bool = false, + }; - /// Opens a directory at the given path with the ability to access subpaths - /// of the result. Calling `iterate` on the result is illegal behavior; to - /// list the contents of a directory, open it with `openDirList`. - /// - /// Call `close` on the result when done. + /// Opens a directory at the given path. The directory is a system resource that remains + /// open until `close` is called on the result. /// /// Asserts that the path parameter has no null bytes. - pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir { + pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir { if (builtin.os.tag == .windows) { const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); - return self.openDirTraverseW(&sub_path_w); + return self.openDirW(&sub_path_w, args); + } else { + const sub_path_c = try os.toPosixPath(sub_path); + return self.openDirC(&sub_path_c, args); } - - const sub_path_c = try os.toPosixPath(sub_path); - return self.openDirTraverseC(&sub_path_c); } - /// Opens a directory at the given path with the ability to access subpaths and list contents - /// of the result. If the ability to list contents is unneeded, `openDirTraverse` acts the - /// same and may be more efficient. - /// - /// Call `close` on the result when done. - /// - /// Asserts that the path parameter has no null bytes. - pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir { - if (builtin.os.tag == .windows) { - const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); - return self.openDirListW(&sub_path_w); - } - - const sub_path_c = try os.toPosixPath(sub_path); - return self.openDirListC(&sub_path_c); - } - - /// Same as `openDirTraverse` except the parameter is null-terminated. - pub fn openDirTraverseC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { + /// Same as `openDir` except the parameter is null-terminated. + pub fn openDirC(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir { if (builtin.os.tag == .windows) { const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); - return self.openDirTraverseW(&sub_path_w); - } else { + return self.openDirW(&sub_path_w, args); + } else if (!args.iterate) { const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0; - return self.openDirFlagsC(sub_path_c, os.O_RDONLY | os.O_CLOEXEC | O_PATH); - } - } - - /// Same as `openDirList` except the parameter is null-terminated. - pub fn openDirListC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { - if (builtin.os.tag == .windows) { - const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); - return self.openDirListW(&sub_path_w); + return self.openDirFlagsC(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC | O_PATH); } else { - return self.openDirFlagsC(sub_path_c, os.O_RDONLY | os.O_CLOEXEC); + return self.openDirFlagsC(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC); } } + /// Same as `openDir` except the path parameter is WTF-16 encoded, NT-prefixed. + /// This function asserts the target OS is Windows. + pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions) OpenError!Dir { + const w = os.windows; + // TODO remove some of these flags if args.access_sub_paths is false + const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | + w.SYNCHRONIZE | w.FILE_TRAVERSE; + const flags: u32 = if (args.iterate) base_flags | w.FILE_LIST_DIRECTORY else base_flags; + return self.openDirAccessMaskW(sub_path_w, flags); + } + + /// `flags` must contain `os.O_DIRECTORY`. fn openDirFlagsC(self: Dir, sub_path_c: [*:0]const u8, flags: u32) OpenError!Dir { - const os_flags = flags | os.O_DIRECTORY; const result = if (need_async_thread) - std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, 0) + std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, flags, 0) else - os.openatC(self.fd, sub_path_c, os_flags, 0); + os.openatC(self.fd, sub_path_c, flags, 0); const fd = result catch |err| switch (err) { error.FileTooBig => unreachable, // can't happen for directories error.IsDir => unreachable, // we're providing O_DIRECTORY @@ -962,22 +807,6 @@ pub const Dir = struct { return Dir{ .fd = fd }; } - /// Same as `openDirTraverse` except the path parameter is UTF16LE, NT-prefixed. - /// This function is Windows-only. - pub fn openDirTraverseW(self: Dir, sub_path_w: [*:0]const u16) OpenError!Dir { - const w = os.windows; - - return self.openDirAccessMaskW(sub_path_w, w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE); - } - - /// Same as `openDirList` except the path parameter is UTF16LE, NT-prefixed. - /// This function is Windows-only. - pub fn openDirListW(self: Dir, sub_path_w: [*:0]const u16) OpenError!Dir { - const w = os.windows; - - return self.openDirAccessMaskW(sub_path_w, w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE | w.FILE_LIST_DIRECTORY); - } - fn openDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32) OpenError!Dir { const w = os.windows; @@ -1198,7 +1027,7 @@ pub const Dir = struct { error.Unexpected, => |e| return e, } - var dir = self.openDirList(sub_path) catch |err| switch (err) { + var dir = self.openDir(sub_path, .{ .iterate = true }) catch |err| switch (err) { error.NotDir => { if (got_access_denied) { return error.AccessDenied; @@ -1231,7 +1060,6 @@ pub const Dir = struct { var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined; var dir_name: []const u8 = sub_path; - var parent_dir = self; // Here we must avoid recursion, in order to provide O(1) memory guarantee of this function. // Go through each entry and if it is not a directory, delete it. If it is a directory, @@ -1263,7 +1091,7 @@ pub const Dir = struct { => |e| return e, } - const new_dir = dir.openDirList(entry.name) catch |err| switch (err) { + const new_dir = dir.openDir(entry.name, .{ .iterate = true }) catch |err| switch (err) { error.NotDir => { if (got_access_denied) { return error.AccessDenied; @@ -1370,9 +1198,96 @@ pub const Dir = struct { pub fn accessW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) AccessError!void { return os.faccessatW(self.fd, sub_path_w, 0, 0); } + + /// Check the file size, mtime, and mode of `source_path` and `dest_path`. If they are equal, does nothing. + /// Otherwise, atomically copies `source_path` to `dest_path`. The destination file gains the mtime, + /// atime, and mode of the source file so that the next call to `updateFile` will not need a copy. + /// Returns the previous status of the file before updating. + /// If any of the directories do not exist for dest_path, they are created. + pub fn updateFile( + source_dir: Dir, + source_path: []const u8, + dest_dir: Dir, + dest_path: []const u8, + options: CopyFileOptions, + ) !PrevStatus { + var src_file = try source_dir.openFile(source_path, .{}); + defer src_file.close(); + + const src_stat = try src_file.stat(); + const actual_mode = options.override_mode orelse src_stat.mode; + check_dest_stat: { + const dest_stat = blk: { + var dest_file = dest_dir.openFile(dest_path, .{}) catch |err| switch (err) { + error.FileNotFound => break :check_dest_stat, + else => |e| return e, + }; + defer dest_file.close(); + + break :blk try dest_file.stat(); + }; + + if (src_stat.size == dest_stat.size and + src_stat.mtime == dest_stat.mtime and + actual_mode == dest_stat.mode) + { + return PrevStatus.fresh; + } + } + + if (path.dirname(dest_path)) |dirname| { + try dest_dir.makePath(dirname); + } + + var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = actual_mode }); + defer atomic_file.deinit(); + + try atomic_file.file.writeFileAll(src_file, .{ .in_len = src_stat.size }); + try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime); + try atomic_file.finish(); + return PrevStatus.stale; + } + + /// Guaranteed to be atomic. + /// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available, + /// there is a possibility of power loss or application termination leaving temporary files present + /// in the same directory as dest_path. + pub fn copyFile( + source_dir: Dir, + source_path: []const u8, + dest_dir: Dir, + dest_path: []const u8, + options: CopyFileOptions, + ) !void { + var in_file = try source_dir.openFile(source_path, .{}); + defer in_file.close(); + + var size: ?u64 = null; + const mode = options.override_mode orelse blk: { + const stat = try in_file.stat(); + size = stat.size; + break :blk stat.mode; + }; + + var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode }); + defer atomic_file.deinit(); + + try atomic_file.file.writeFileAll(in_file, .{ .in_len = size }); + return atomic_file.finish(); + } + + pub const AtomicFileOptions = struct { + mode: File.Mode = File.default_mode, + }; + + /// `dest_path` must remain valid for the lifetime of `AtomicFile`. + /// Call `AtomicFile.finish` to atomically replace `dest_path` with contents. + pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions) !AtomicFile { + return AtomicFile.init2(dest_path, options.mode, self); + } }; -/// Returns an handle to the current working directory that is open for traversal. +/// Returns an handle to the current working directory. It is not opened with iteration capability. /// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior. /// On POSIX targets, this function is comptime-callable. pub fn cwd() Dir { @@ -1450,6 +1365,25 @@ pub fn deleteFileAbsoluteW(absolute_path_w: [*:0]const u16) DeleteFileError!void return cwd().deleteFileW(absolute_path_w); } +/// Removes a symlink, file, or directory. +/// This is equivalent to `Dir.deleteTree` with the base directory. +/// Asserts that the path is absolute. See `Dir.deleteTree` for a function that +/// operates on both absolute and relative paths. +/// Asserts that the path parameter has no null bytes. +pub fn deleteTreeAbsolute(absolute_path: []const u8) !void { + assert(path.isAbsolute(absolute_path)); + const dirname = path.dirname(absolute_path) orelse return error{ + /// Attempt to remove the root file system path. + /// This error is unreachable if `absolute_path` is relative. + CannotDeleteRootDirectory, + }.CannotDeleteRootDirectory; + + var dir = try cwd().openDir(dirname, .{}); + defer dir.close(); + + return dir.deleteTree(path.basename(absolute_path)); +} + pub const Walker = struct { stack: std.ArrayList(StackItem), name_buffer: std.Buffer, @@ -1484,7 +1418,7 @@ pub const Walker = struct { try self.name_buffer.appendByte(path.sep); try self.name_buffer.append(base.name); if (base.kind == .Directory) { - var new_dir = top.dir_it.dir.openDirList(base.name) catch |err| switch (err) { + var new_dir = top.dir_it.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) { error.NameTooLong => unreachable, // no path sep in base.name else => |e| return e, }; @@ -1522,7 +1456,7 @@ pub const Walker = struct { pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { assert(!mem.endsWith(u8, dir_path, path.sep_str)); - var dir = try cwd().openDirList(dir_path); + var dir = try cwd().openDir(dir_path, .{ .iterate = true }); errdefer dir.close(); var name_buffer = try std.Buffer.init(allocator, dir_path); @@ -1541,13 +1475,12 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { return walker; } -/// Read value of a symbolic link. -/// The return value is a slice of buffer, from index `0`. +/// Deprecated; use `Dir.readLink`. pub fn readLink(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { return os.readlink(pathname, buffer); } -/// Same as `readLink`, except the parameter is null-terminated. +/// Deprecated; use `Dir.readLinkC`. pub fn readLinkC(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { return os.readlinkC(pathname_c, buffer); } @@ -1654,6 +1587,7 @@ pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const } /// `realpath`, except caller must free the returned memory. +/// TODO integrate with `Dir` pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 { var buf: [MAX_PATH_BYTES]u8 = undefined; return mem.dupe(allocator, u8, try os.realpath(pathname, &buf)); @@ -1662,6 +1596,9 @@ pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 { test "" { _ = makeDirAbsolute; _ = makeDirAbsoluteZ; + _ = copyFileAbsolute; + _ = updateFileAbsolute; + _ = Dir.copyFile; _ = @import("fs/path.zig"); _ = @import("fs/file.zig"); _ = @import("fs/get_app_data_dir.zig"); diff --git a/lib/std/fs/watch.zig b/lib/std/fs/watch.zig index 3180240a72..96fe1538e4 100644 --- a/lib/std/fs/watch.zig +++ b/lib/std/fs/watch.zig @@ -619,7 +619,7 @@ test "write a file, watch it, write it again" { if (true) return error.SkipZigTest; try fs.cwd().makePath(test_tmp_dir); - defer os.deleteTree(test_tmp_dir) catch {}; + defer fs.cwd().deleteTree(test_tmp_dir) catch {}; const allocator = std.heap.page_allocator; return testFsWatch(&allocator); diff --git a/lib/std/hash/auto_hash.zig b/lib/std/hash/auto_hash.zig index 2c6b37e3db..b5d6c528a5 100644 --- a/lib/std/hash/auto_hash.zig +++ b/lib/std/hash/auto_hash.zig @@ -40,7 +40,9 @@ pub fn hashPointer(hasher: var, key: var, comptime strat: HashStrategy) void { .DeepRecursive => hashArray(hasher, key, .DeepRecursive), }, - .Many, .C, => switch (strat) { + .Many, + .C, + => switch (strat) { .Shallow => hash(hasher, @ptrToInt(key), .Shallow), else => @compileError( \\ unknown-length pointers and C pointers cannot be hashed deeply. @@ -236,9 +238,11 @@ test "hash slice shallow" { defer std.testing.allocator.destroy(array1); array1.* = [_]u32{ 1, 2, 3, 4, 5, 6 }; const array2 = [_]u32{ 1, 2, 3, 4, 5, 6 }; - const a = array1[0..]; - const b = array2[0..]; - const c = array1[0..3]; + // TODO audit deep/shallow - maybe it has the wrong behavior with respect to array pointers and slices + var runtime_zero: usize = 0; + const a = array1[runtime_zero..]; + const b = array2[runtime_zero..]; + const c = array1[runtime_zero..3]; testing.expect(testHashShallow(a) == testHashShallow(a)); testing.expect(testHashShallow(a) != testHashShallow(array1)); testing.expect(testHashShallow(a) != testHashShallow(b)); diff --git a/lib/std/hash/siphash.zig b/lib/std/hash/siphash.zig index ccef47c4b2..ebafdd6855 100644 --- a/lib/std/hash/siphash.zig +++ b/lib/std/hash/siphash.zig @@ -39,8 +39,8 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round pub fn init(key: []const u8) Self { assert(key.len >= 16); - const k0 = mem.readIntSliceLittle(u64, key[0..8]); - const k1 = mem.readIntSliceLittle(u64, key[8..16]); + const k0 = mem.readIntLittle(u64, key[0..8]); + const k1 = mem.readIntLittle(u64, key[8..16]); var d = Self{ .v0 = k0 ^ 0x736f6d6570736575, @@ -111,7 +111,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round fn round(self: *Self, b: []const u8) void { assert(b.len == 8); - const m = mem.readIntSliceLittle(u64, b[0..]); + const m = mem.readIntLittle(u64, b[0..8]); self.v3 ^= m; // TODO this is a workaround, should be able to supply the value without a separate variable diff --git a/lib/std/hash/wyhash.zig b/lib/std/hash/wyhash.zig index 8fcbbbce4c..8b7edd246f 100644 --- a/lib/std/hash/wyhash.zig +++ b/lib/std/hash/wyhash.zig @@ -11,7 +11,7 @@ const primes = [_]u64{ fn read_bytes(comptime bytes: u8, data: []const u8) u64 { const T = std.meta.IntType(false, 8 * bytes); - return mem.readIntSliceLittle(T, data[0..bytes]); + return mem.readIntLittle(T, data[0..bytes]); } fn read_8bytes_swapped(data: []const u8) u64 { diff --git a/lib/std/io/serialization.zig b/lib/std/io/serialization.zig index 9b96b844e1..49e2fb8dcd 100644 --- a/lib/std/io/serialization.zig +++ b/lib/std/io/serialization.zig @@ -1,6 +1,10 @@ const std = @import("../std.zig"); const builtin = std.builtin; const io = std.io; +const assert = std.debug.assert; +const math = std.math; +const meta = std.meta; +const trait = meta.trait; pub const Packing = enum { /// Pack data to byte alignment @@ -252,7 +256,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v); } - try self.out_stream.write(&buffer); + try self.out_stream.writeAll(&buffer); } /// Serializes the passed value into the stream diff --git a/lib/std/json.zig b/lib/std/json.zig index f5a72d86da..3dd8088785 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -2249,11 +2249,16 @@ pub const StringifyOptions = struct { // TODO: allow picking if []u8 is string or array? }; +pub const StringifyError = error{ + TooMuchData, + DifferentData, +}; + pub fn stringify( value: var, options: StringifyOptions, out_stream: var, -) !void { +) StringifyError!void { const T = @TypeOf(value); switch (@typeInfo(T)) { .Float, .ComptimeFloat => { @@ -2320,9 +2325,15 @@ pub fn stringify( return; }, .Pointer => |ptr_info| switch (ptr_info.size) { - .One => { - // TODO: avoid loops? - return try stringify(value.*, options, out_stream); + .One => switch (@typeInfo(ptr_info.child)) { + .Array => { + const Slice = []const std.meta.Elem(ptr_info.child); + return stringify(@as(Slice, value), options, out_stream); + }, + else => { + // TODO: avoid loops? + return stringify(value.*, options, out_stream); + }, }, // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972) .Slice => { @@ -2381,9 +2392,7 @@ pub fn stringify( }, else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"), }, - .Array => |info| { - return try stringify(value[0..], options, out_stream); - }, + .Array => return stringify(&value, options, out_stream), else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"), } unreachable; diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 95d0764f68..2a3c9d508a 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -520,13 +520,13 @@ pub const Int = struct { comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: var, - ) FmtError!void { + ) !void { self.assertWritable(); // TODO look at fmt and support other bases // TODO support read-only fixed integers const str = self.toString(self.allocator.?, 10) catch @panic("TODO make this non allocating"); defer self.allocator.?.free(str); - return out_stream.print(str); + return out_stream.writeAll(str); } /// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively. diff --git a/lib/std/mem.zig b/lib/std/mem.zig index c767765652..fa2794160b 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -116,7 +116,7 @@ pub const Allocator = struct { pub fn allocSentinel(self: *Allocator, comptime Elem: type, n: usize, comptime sentinel: Elem) Error![:sentinel]Elem { var ptr = try self.alloc(Elem, n + 1); ptr[n] = sentinel; - return ptr[0 .. n :sentinel]; + return ptr[0..n :sentinel]; } pub fn alignedAlloc( @@ -496,14 +496,14 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool { return true; } -/// Deprecated. Use `span`. +/// Deprecated. Use `spanZ`. pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T { - return ptr[0..len(ptr) :0]; + return ptr[0..lenZ(ptr) :0]; } -/// Deprecated. Use `span`. +/// Deprecated. Use `spanZ`. pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T { - return ptr[0..len(ptr) :0]; + return ptr[0..lenZ(ptr) :0]; } /// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and @@ -548,6 +548,9 @@ test "Span" { /// returns a slice. If there is a sentinel on the input type, there will be a /// sentinel on the output type. The constness of the output type matches /// the constness of the input type. +/// +/// When there is both a sentinel and an array length or slice length, the +/// length value is used instead of the sentinel. pub fn span(ptr: var) Span(@TypeOf(ptr)) { const Result = Span(@TypeOf(ptr)); const l = len(ptr); @@ -560,20 +563,42 @@ pub fn span(ptr: var) Span(@TypeOf(ptr)) { test "span" { var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; - const ptr = array[0..2 :3].ptr; + const ptr = @as([*:3]u16, array[0..2 :3]); testing.expect(eql(u16, span(ptr), &[_]u16{ 1, 2 })); testing.expect(eql(u16, span(&array), &[_]u16{ 1, 2, 3, 4, 5 })); } +/// Same as `span`, except when there is both a sentinel and an array +/// length or slice length, scans the memory for the sentinel value +/// rather than using the length. +pub fn spanZ(ptr: var) Span(@TypeOf(ptr)) { + const Result = Span(@TypeOf(ptr)); + const l = lenZ(ptr); + if (@typeInfo(Result).Pointer.sentinel) |s| { + return ptr[0..l :s]; + } else { + return ptr[0..l]; + } +} + +test "spanZ" { + var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; + const ptr = @as([*:3]u16, array[0..2 :3]); + testing.expect(eql(u16, spanZ(ptr), &[_]u16{ 1, 2 })); + testing.expect(eql(u16, spanZ(&array), &[_]u16{ 1, 2, 3, 4, 5 })); +} + /// Takes a pointer to an array, an array, a sentinel-terminated pointer, /// or a slice, and returns the length. +/// In the case of a sentinel-terminated array, it uses the array length. +/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel. pub fn len(ptr: var) usize { return switch (@typeInfo(@TypeOf(ptr))) { .Array => |info| info.len, .Pointer => |info| switch (info.size) { .One => switch (@typeInfo(info.child)) { - .Array => |x| x.len, - else => @compileError("invalid type given to std.mem.length"), + .Array => ptr.len, + else => @compileError("invalid type given to std.mem.len"), }, .Many => if (info.sentinel) |sentinel| indexOfSentinel(info.child, sentinel, ptr) @@ -582,7 +607,7 @@ pub fn len(ptr: var) usize { .C => indexOfSentinel(info.child, 0, ptr), .Slice => ptr.len, }, - else => @compileError("invalid type given to std.mem.length"), + else => @compileError("invalid type given to std.mem.len"), }; } @@ -594,9 +619,67 @@ test "len" { testing.expect(len(&array) == 5); testing.expect(len(array[0..3]) == 3); array[2] = 0; - const ptr = array[0..2 :0].ptr; + const ptr = @as([*:0]u16, array[0..2 :0]); testing.expect(len(ptr) == 2); } + { + var array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 }; + testing.expect(len(&array) == 5); + array[2] = 0; + testing.expect(len(&array) == 5); + } +} + +/// Takes a pointer to an array, an array, a sentinel-terminated pointer, +/// or a slice, and returns the length. +/// In the case of a sentinel-terminated array, it scans the array +/// for a sentinel and uses that for the length, rather than using the array length. +/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel. +pub fn lenZ(ptr: var) usize { + return switch (@typeInfo(@TypeOf(ptr))) { + .Array => |info| if (info.sentinel) |sentinel| + indexOfSentinel(info.child, sentinel, &ptr) + else + info.len, + .Pointer => |info| switch (info.size) { + .One => switch (@typeInfo(info.child)) { + .Array => |x| if (x.sentinel) |sentinel| + indexOfSentinel(x.child, sentinel, ptr) + else + ptr.len, + else => @compileError("invalid type given to std.mem.lenZ"), + }, + .Many => if (info.sentinel) |sentinel| + indexOfSentinel(info.child, sentinel, ptr) + else + @compileError("length of pointer with no sentinel"), + .C => indexOfSentinel(info.child, 0, ptr), + .Slice => if (info.sentinel) |sentinel| + indexOfSentinel(info.child, sentinel, ptr.ptr) + else + ptr.len, + }, + else => @compileError("invalid type given to std.mem.lenZ"), + }; +} + +test "lenZ" { + testing.expect(lenZ("aoeu") == 4); + + { + var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; + testing.expect(lenZ(&array) == 5); + testing.expect(lenZ(array[0..3]) == 3); + array[2] = 0; + const ptr = @as([*:0]u16, array[0..2 :0]); + testing.expect(lenZ(ptr) == 2); + } + { + var array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 }; + testing.expect(lenZ(&array) == 5); + array[2] = 0; + testing.expect(lenZ(&array) == 2); + } } pub fn indexOfSentinel(comptime Elem: type, comptime sentinel: Elem, ptr: [*:sentinel]const Elem) usize { @@ -810,8 +893,7 @@ pub const readIntBig = switch (builtin.endian) { pub fn readIntSliceNative(comptime T: type, bytes: []const u8) T { const n = @divExact(T.bit_count, 8); assert(bytes.len >= n); - // TODO https://github.com/ziglang/zig/issues/863 - return readIntNative(T, @ptrCast(*const [n]u8, bytes.ptr)); + return readIntNative(T, bytes[0..n]); } /// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0 @@ -849,8 +931,7 @@ pub fn readInt(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8, en pub fn readIntSlice(comptime T: type, bytes: []const u8, endian: builtin.Endian) T { const n = @divExact(T.bit_count, 8); assert(bytes.len >= n); - // TODO https://github.com/ziglang/zig/issues/863 - return readInt(T, @ptrCast(*const [n]u8, bytes.ptr), endian); + return readInt(T, bytes[0..n], endian); } test "comptime read/write int" { @@ -1572,24 +1653,24 @@ pub fn nativeToBig(comptime T: type, x: T) T { } fn AsBytesReturnType(comptime P: type) type { - if (comptime !trait.isSingleItemPtr(P)) + if (!trait.isSingleItemPtr(P)) @compileError("expected single item pointer, passed " ++ @typeName(P)); - const size = @as(usize, @sizeOf(meta.Child(P))); - const alignment = comptime meta.alignment(P); + const size = @sizeOf(meta.Child(P)); + const alignment = meta.alignment(P); if (alignment == 0) { - if (comptime trait.isConstPtr(P)) + if (trait.isConstPtr(P)) return *const [size]u8; return *[size]u8; } - if (comptime trait.isConstPtr(P)) + if (trait.isConstPtr(P)) return *align(alignment) const [size]u8; return *align(alignment) [size]u8; } -///Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness. +/// Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness. pub fn asBytes(ptr: var) AsBytesReturnType(@TypeOf(ptr)) { const P = @TypeOf(ptr); return @ptrCast(AsBytesReturnType(P), ptr); @@ -1736,34 +1817,50 @@ fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type { } pub fn bytesAsSlice(comptime T: type, bytes: var) BytesAsSliceReturnType(T, @TypeOf(bytes)) { - const bytesSlice = if (comptime trait.isPtrTo(.Array)(@TypeOf(bytes))) bytes[0..] else bytes; - // let's not give an undefined pointer to @ptrCast // it may be equal to zero and fail a null check - if (bytesSlice.len == 0) { + if (bytes.len == 0) { return &[0]T{}; } - const bytesType = @TypeOf(bytesSlice); - const alignment = comptime meta.alignment(bytesType); + const Bytes = @TypeOf(bytes); + const alignment = comptime meta.alignment(Bytes); - const castTarget = if (comptime trait.isConstPtr(bytesType)) [*]align(alignment) const T else [*]align(alignment) T; + const cast_target = if (comptime trait.isConstPtr(Bytes)) [*]align(alignment) const T else [*]align(alignment) T; - return @ptrCast(castTarget, bytesSlice.ptr)[0..@divExact(bytes.len, @sizeOf(T))]; + return @ptrCast(cast_target, bytes)[0..@divExact(bytes.len, @sizeOf(T))]; } test "bytesAsSlice" { - const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; - const slice = bytesAsSlice(u16, bytes[0..]); - testing.expect(slice.len == 2); - testing.expect(bigToNative(u16, slice[0]) == 0xDEAD); - testing.expect(bigToNative(u16, slice[1]) == 0xBEEF); + { + const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; + const slice = bytesAsSlice(u16, bytes[0..]); + testing.expect(slice.len == 2); + testing.expect(bigToNative(u16, slice[0]) == 0xDEAD); + testing.expect(bigToNative(u16, slice[1]) == 0xBEEF); + } + { + const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; + var runtime_zero: usize = 0; + const slice = bytesAsSlice(u16, bytes[runtime_zero..]); + testing.expect(slice.len == 2); + testing.expect(bigToNative(u16, slice[0]) == 0xDEAD); + testing.expect(bigToNative(u16, slice[1]) == 0xBEEF); + } } test "bytesAsSlice keeps pointer alignment" { - var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 }; - const numbers = bytesAsSlice(u32, bytes[0..]); - comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32); + { + var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 }; + const numbers = bytesAsSlice(u32, bytes[0..]); + comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32); + } + { + var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 }; + var runtime_zero: usize = 0; + const numbers = bytesAsSlice(u32, bytes[runtime_zero..]); + comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32); + } } test "bytesAsSlice on a packed struct" { @@ -1799,21 +1896,19 @@ fn SliceAsBytesReturnType(comptime sliceType: type) type { } pub fn sliceAsBytes(slice: var) SliceAsBytesReturnType(@TypeOf(slice)) { - const actualSlice = if (comptime trait.isPtrTo(.Array)(@TypeOf(slice))) slice[0..] else slice; - const actualSliceTypeInfo = @typeInfo(@TypeOf(actualSlice)).Pointer; + const Slice = @TypeOf(slice); // let's not give an undefined pointer to @ptrCast // it may be equal to zero and fail a null check - if (actualSlice.len == 0 and actualSliceTypeInfo.sentinel == null) { + if (slice.len == 0 and comptime meta.sentinel(Slice) == null) { return &[0]u8{}; } - const sliceType = @TypeOf(actualSlice); - const alignment = comptime meta.alignment(sliceType); + const alignment = comptime meta.alignment(Slice); - const castTarget = if (comptime trait.isConstPtr(sliceType)) [*]align(alignment) const u8 else [*]align(alignment) u8; + const cast_target = if (comptime trait.isConstPtr(Slice)) [*]align(alignment) const u8 else [*]align(alignment) u8; - return @ptrCast(castTarget, actualSlice.ptr)[0 .. actualSlice.len * @sizeOf(comptime meta.Child(sliceType))]; + return @ptrCast(cast_target, slice)[0 .. slice.len * @sizeOf(meta.Elem(Slice))]; } test "sliceAsBytes" { @@ -1883,39 +1978,6 @@ test "sliceAsBytes and bytesAsSlice back" { testing.expect(bytes[11] == math.maxInt(u8)); } -fn SubArrayPtrReturnType(comptime T: type, comptime length: usize) type { - if (trait.isConstPtr(T)) - return *const [length]meta.Child(meta.Child(T)); - return *[length]meta.Child(meta.Child(T)); -} - -/// Given a pointer to an array, returns a pointer to a portion of that array, preserving constness. -/// TODO this will be obsoleted by https://github.com/ziglang/zig/issues/863 -pub fn subArrayPtr( - ptr: var, - comptime start: usize, - comptime length: usize, -) SubArrayPtrReturnType(@TypeOf(ptr), length) { - assert(start + length <= ptr.*.len); - - const ReturnType = SubArrayPtrReturnType(@TypeOf(ptr), length); - const T = meta.Child(meta.Child(@TypeOf(ptr))); - return @ptrCast(ReturnType, &ptr[start]); -} - -test "subArrayPtr" { - const a1: [6]u8 = "abcdef".*; - const sub1 = subArrayPtr(&a1, 2, 3); - testing.expect(eql(u8, sub1, "cde")); - - var a2: [6]u8 = "abcdef".*; - var sub2 = subArrayPtr(&a2, 2, 3); - - testing.expect(eql(u8, sub2, "cde")); - sub2[1] = 'X'; - testing.expect(eql(u8, &a2, "abcXef")); -} - /// Round an address up to the nearest aligned address /// The alignment must be a power of 2 and greater than 0. pub fn alignForward(addr: usize, alignment: usize) usize { diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 65809abb5c..7343cfc51a 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -104,7 +104,7 @@ pub fn Child(comptime T: type) type { .Array => |info| info.child, .Pointer => |info| info.child, .Optional => |info| info.child, - else => @compileError("Expected pointer, optional, or array type, " ++ "found '" ++ @typeName(T) ++ "'"), + else => @compileError("Expected pointer, optional, or array type, found '" ++ @typeName(T) ++ "'"), }; } @@ -115,30 +115,65 @@ test "std.meta.Child" { testing.expect(Child(?u8) == u8); } -/// Given a type with a sentinel e.g. `[:0]u8`, returns the sentinel -pub fn Sentinel(comptime T: type) Child(T) { - // comptime asserts that ptr has a sentinel +/// Given a "memory span" type, returns the "element type". +pub fn Elem(comptime T: type) type { switch (@typeInfo(T)) { - .Array => |arrayInfo| { - return comptime arrayInfo.sentinel.?; + .Array => |info| return info.child, + .Pointer => |info| switch (info.size) { + .One => switch (@typeInfo(info.child)) { + .Array => |array_info| return array_info.child, + else => {}, + }, + .Many, .C, .Slice => return info.child, }, - .Pointer => |ptrInfo| { - switch (ptrInfo.size) { - .Many, .Slice => { - return comptime ptrInfo.sentinel.?; + else => {}, + } + @compileError("Expected pointer, slice, or array, found '" ++ @typeName(T) ++ "'"); +} + +test "std.meta.Elem" { + testing.expect(Elem([1]u8) == u8); + testing.expect(Elem([*]u8) == u8); + testing.expect(Elem([]u8) == u8); + testing.expect(Elem(*[10]u8) == u8); +} + +/// Given a type which can have a sentinel e.g. `[:0]u8`, returns the sentinel value, +/// or `null` if there is not one. +/// Types which cannot possibly have a sentinel will be a compile error. +pub fn sentinel(comptime T: type) ?Elem(T) { + switch (@typeInfo(T)) { + .Array => |info| return info.sentinel, + .Pointer => |info| { + switch (info.size) { + .Many, .Slice => return info.sentinel, + .One => switch (@typeInfo(info.child)) { + .Array => |array_info| return array_info.sentinel, + else => {}, }, else => {}, } }, else => {}, } - @compileError("not a sentinel type, found '" ++ @typeName(T) ++ "'"); + @compileError("type '" ++ @typeName(T) ++ "' cannot possibly have a sentinel"); } -test "std.meta.Sentinel" { - testing.expectEqual(@as(u8, 0), Sentinel([:0]u8)); - testing.expectEqual(@as(u8, 0), Sentinel([*:0]u8)); - testing.expectEqual(@as(u8, 0), Sentinel([5:0]u8)); +test "std.meta.sentinel" { + testSentinel(); + comptime testSentinel(); +} + +fn testSentinel() void { + testing.expectEqual(@as(u8, 0), sentinel([:0]u8).?); + testing.expectEqual(@as(u8, 0), sentinel([*:0]u8).?); + testing.expectEqual(@as(u8, 0), sentinel([5:0]u8).?); + testing.expectEqual(@as(u8, 0), sentinel(*const [5:0]u8).?); + + testing.expect(sentinel([]u8) == null); + testing.expect(sentinel([*]u8) == null); + testing.expect(sentinel([5]u8) == null); + testing.expect(sentinel(*const [5]u8) == null); } pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout { diff --git a/lib/std/meta/trait.zig b/lib/std/meta/trait.zig index c0fb8c5025..c99e1389f4 100644 --- a/lib/std/meta/trait.zig +++ b/lib/std/meta/trait.zig @@ -230,9 +230,10 @@ pub fn isSingleItemPtr(comptime T: type) bool { test "std.meta.trait.isSingleItemPtr" { const array = [_]u8{0} ** 10; - testing.expect(isSingleItemPtr(@TypeOf(&array[0]))); - testing.expect(!isSingleItemPtr(@TypeOf(array))); - testing.expect(!isSingleItemPtr(@TypeOf(array[0..1]))); + comptime testing.expect(isSingleItemPtr(@TypeOf(&array[0]))); + comptime testing.expect(!isSingleItemPtr(@TypeOf(array))); + var runtime_zero: usize = 0; + testing.expect(!isSingleItemPtr(@TypeOf(array[runtime_zero..1]))); } pub fn isManyItemPtr(comptime T: type) bool { @@ -259,7 +260,8 @@ pub fn isSlice(comptime T: type) bool { test "std.meta.trait.isSlice" { const array = [_]u8{0} ** 10; - testing.expect(isSlice(@TypeOf(array[0..]))); + var runtime_zero: usize = 0; + testing.expect(isSlice(@TypeOf(array[runtime_zero..]))); testing.expect(!isSlice(@TypeOf(array))); testing.expect(!isSlice(@TypeOf(&array[0]))); } @@ -276,7 +278,7 @@ pub fn isIndexable(comptime T: type) bool { test "std.meta.trait.isIndexable" { const array = [_]u8{0} ** 10; - const slice = array[0..]; + const slice = @as([]const u8, &array); testing.expect(isIndexable(@TypeOf(array))); testing.expect(isIndexable(@TypeOf(&array))); diff --git a/lib/std/net.zig b/lib/std/net.zig index c2328b9bd0..b9c1281191 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -612,8 +612,7 @@ fn linuxLookupName( } else { mem.copy(u8, &sa6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff"); mem.copy(u8, &da6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff"); - // TODO https://github.com/ziglang/zig/issues/863 - mem.writeIntNative(u32, @ptrCast(*[4]u8, da6.addr[12..].ptr), addr.addr.in.addr); + mem.writeIntNative(u32, da6.addr[12..], addr.addr.in.addr); da4.addr = addr.addr.in.addr; da = @ptrCast(*os.sockaddr, &da4); dalen = @sizeOf(os.sockaddr_in); @@ -821,7 +820,7 @@ fn linuxLookupNameFromHosts( // Skip to the delimiter in the stream, to fix parsing try stream.skipUntilDelimiterOrEof('\n'); // Use the truncated line. A truncated comment or hostname will be handled correctly. - break :blk line_buf[0..]; + break :blk @as([]u8, &line_buf); // TODO the cast should not be necessary }, else => |e| return e, }) |line| { @@ -958,7 +957,10 @@ fn linuxLookupNameFromDns( } } - var ap = [2][]u8{ apbuf[0][0..0], apbuf[1][0..0] }; + var ap = [2][]u8{ apbuf[0], apbuf[1] }; + ap[0].len = 0; + ap[1].len = 0; + try resMSendRc(qp[0..nq], ap[0..nq], apbuf[0..nq], rc); var i: usize = 0; @@ -1015,7 +1017,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void { // Skip to the delimiter in the stream, to fix parsing try stream.skipUntilDelimiterOrEof('\n'); // Give an empty line to the while loop, which will be skipped. - break :blk line_buf[0..0]; + break :blk @as([]u8, line_buf[0..0]); // TODO the cast should not be necessary }, else => |e| return e, }) |line| { diff --git a/lib/std/os.zig b/lib/std/os.zig index ed99c48021..2dec2c4c1d 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -461,13 +461,11 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void { ); switch (rc) { - .SUCCESS => {}, + .SUCCESS => return, .INVALID_HANDLE => unreachable, // Handle not open for writing .ACCESS_DENIED => return error.CannotTruncate, else => return windows.unexpectedStatus(rc), } - - return; } while (true) { @@ -852,6 +850,7 @@ pub const OpenError = error{ /// Open and possibly create a file. Keeps trying if it gets interrupted. /// See also `openC`. +/// TODO support windows pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t { const file_path_c = try toPosixPath(file_path); return openC(&file_path_c, flags, perm); @@ -859,6 +858,7 @@ pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t { /// Open and possibly create a file. Keeps trying if it gets interrupted. /// See also `open`. +/// TODO support windows pub fn openC(file_path: [*:0]const u8, flags: u32, perm: usize) OpenError!fd_t { while (true) { const rc = system.open(file_path, flags, perm); @@ -892,6 +892,7 @@ pub fn openC(file_path: [*:0]const u8, flags: u32, perm: usize) OpenError!fd_t { /// Open and possibly create a file. Keeps trying if it gets interrupted. /// `file_path` is relative to the open directory handle `dir_fd`. /// See also `openatC`. +/// TODO support windows pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t { const file_path_c = try toPosixPath(file_path); return openatC(dir_fd, &file_path_c, flags, mode); @@ -900,6 +901,7 @@ pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) Ope /// Open and possibly create a file. Keeps trying if it gets interrupted. /// `file_path` is relative to the open directory handle `dir_fd`. /// See also `openat`. +/// TODO support windows pub fn openatC(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t { while (true) { const rc = system.openat(dir_fd, file_path, flags, mode); @@ -1527,6 +1529,9 @@ const RenameError = error{ RenameAcrossMountPoints, InvalidUtf8, BadPathName, + NoDevice, + SharingViolation, + PipeBusy, } || UnexpectedError; /// Change the name or location of a file. @@ -1580,6 +1585,113 @@ pub fn renameW(old_path: [*:0]const u16, new_path: [*:0]const u16) RenameError!v return windows.MoveFileExW(old_path, new_path, flags); } +/// Change the name or location of a file based on an open directory handle. +pub fn renameat( + old_dir_fd: fd_t, + old_path: []const u8, + new_dir_fd: fd_t, + new_path: []const u8, +) RenameError!void { + if (builtin.os.tag == .windows) { + const old_path_w = try windows.sliceToPrefixedFileW(old_path); + const new_path_w = try windows.sliceToPrefixedFileW(new_path); + return renameatW(old_dir_fd, &old_path_w, new_dir_fd, &new_path_w, windows.TRUE); + } else { + const old_path_c = try toPosixPath(old_path); + const new_path_c = try toPosixPath(new_path); + return renameatZ(old_dir_fd, &old_path_c, new_dir_fd, &new_path_c); + } +} + +/// Same as `renameat` except the parameters are null-terminated byte arrays. +pub fn renameatZ( + old_dir_fd: fd_t, + old_path: [*:0]const u8, + new_dir_fd: fd_t, + new_path: [*:0]const u8, +) RenameError!void { + if (builtin.os.tag == .windows) { + const old_path_w = try windows.cStrToPrefixedFileW(old_path); + const new_path_w = try windows.cStrToPrefixedFileW(new_path); + return renameatW(old_dir_fd, &old_path_w, new_dir_fd, &new_path_w, windows.TRUE); + } + + switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) { + 0 => return, + EACCES => return error.AccessDenied, + EPERM => return error.AccessDenied, + EBUSY => return error.FileBusy, + EDQUOT => return error.DiskQuota, + EFAULT => unreachable, + EINVAL => unreachable, + EISDIR => return error.IsDir, + ELOOP => return error.SymLinkLoop, + EMLINK => return error.LinkQuotaExceeded, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOTDIR => return error.NotDir, + ENOMEM => return error.SystemResources, + ENOSPC => return error.NoSpaceLeft, + EEXIST => return error.PathAlreadyExists, + ENOTEMPTY => return error.PathAlreadyExists, + EROFS => return error.ReadOnlyFileSystem, + EXDEV => return error.RenameAcrossMountPoints, + else => |err| return unexpectedErrno(err), + } +} + +/// Same as `renameat` except the parameters are null-terminated UTF16LE encoded byte arrays. +/// Assumes target is Windows. +/// TODO these args can actually be slices when using ntdll. audit the rest of the W functions too. +pub fn renameatW( + old_dir_fd: fd_t, + old_path: [*:0]const u16, + new_dir_fd: fd_t, + new_path_w: [*:0]const u16, + ReplaceIfExists: windows.BOOLEAN, +) RenameError!void { + const access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE; + const src_fd = try windows.OpenFileW(old_dir_fd, old_path, null, access_mask, windows.FILE_OPEN); + defer windows.CloseHandle(src_fd); + + const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1); + var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION)) = undefined; + const new_path = mem.span(new_path_w); + const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path.len * 2; + if (struct_len > struct_buf_len) return error.NameTooLong; + + const rename_info = @ptrCast(*windows.FILE_RENAME_INFORMATION, &rename_info_buf); + + rename_info.* = .{ + .ReplaceIfExists = ReplaceIfExists, + .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(new_path_w)) null else new_dir_fd, + .FileNameLength = @intCast(u32, new_path.len * 2), // already checked error.NameTooLong + .FileName = undefined, + }; + std.mem.copy(u16, @as([*]u16, &rename_info.FileName)[0..new_path.len], new_path); + + var io_status_block: windows.IO_STATUS_BLOCK = undefined; + + const rc = windows.ntdll.NtSetInformationFile( + src_fd, + &io_status_block, + rename_info, + @intCast(u32, struct_len), // already checked for error.NameTooLong + .FileRenameInformation, + ); + + switch (rc) { + .SUCCESS => return, + .INVALID_HANDLE => unreachable, + .INVALID_PARAMETER => unreachable, + .OBJECT_PATH_SYNTAX_BAD => unreachable, + .ACCESS_DENIED => return error.AccessDenied, + .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, + .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, + else => return windows.unexpectedStatus(rc), + } +} + pub const MakeDirError = error{ AccessDenied, DiskQuota, @@ -2072,7 +2184,7 @@ const ListenError = error{ OperationNotSupported, } || UnexpectedError; -pub fn listen(sockfd: i32, backlog: u32) ListenError!void { +pub fn listen(sockfd: fd_t, backlog: u32) ListenError!void { const rc = system.listen(sockfd, backlog); switch (errno(rc)) { 0 => return, @@ -2363,7 +2475,7 @@ pub fn connect(sockfd: fd_t, sock_addr: *const sockaddr, len: socklen_t) Connect } } -pub fn getsockoptError(sockfd: i32) ConnectError!void { +pub fn getsockoptError(sockfd: fd_t) ConnectError!void { var err_code: u32 = undefined; var size: u32 = @sizeOf(u32); const rc = system.getsockopt(sockfd, SOL_SOCKET, SO_ERROR, @ptrCast([*]u8, &err_code), &size); @@ -3051,6 +3163,31 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 { } } +pub const FcntlError = error{ + PermissionDenied, + FileBusy, + ProcessFdQuotaExceeded, + Locked, +} || UnexpectedError; + +pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize { + while (true) { + const rc = system.fcntl(fd, cmd, arg); + switch (errno(rc)) { + 0 => return @intCast(usize, rc), + EINTR => continue, + EACCES => return error.Locked, + EBADF => unreachable, + EBUSY => return error.FileBusy, + EINVAL => unreachable, // invalid parameters + EPERM => return error.PermissionDenied, + EMFILE => return error.ProcessFdQuotaExceeded, + ENOTDIR => unreachable, // invalid parameter + else => |err| return unexpectedErrno(err), + } + } +} + pub const RealPathError = error{ FileNotFound, AccessDenied, @@ -3125,6 +3262,7 @@ pub fn realpathC(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP } /// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded. +/// TODO use ntdll for better semantics pub fn realpathW(pathname: [*:0]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { const h_file = try windows.CreateFileW( pathname, diff --git a/lib/std/os/bits/dragonfly.zig b/lib/std/os/bits/dragonfly.zig index 27b2733b76..4a9b51d472 100644 --- a/lib/std/os/bits/dragonfly.zig +++ b/lib/std/os/bits/dragonfly.zig @@ -283,6 +283,8 @@ pub const F_LOCK = 1; pub const F_TLOCK = 2; pub const F_TEST = 3; +pub const FD_CLOEXEC = 1; + pub const AT_FDCWD = -328243; pub const AT_SYMLINK_NOFOLLOW = 1; pub const AT_REMOVEDIR = 2; diff --git a/lib/std/os/bits/freebsd.zig b/lib/std/os/bits/freebsd.zig index 02fe7e75b3..6f97884e16 100644 --- a/lib/std/os/bits/freebsd.zig +++ b/lib/std/os/bits/freebsd.zig @@ -355,6 +355,8 @@ pub const F_GETOWN_EX = 16; pub const F_GETOWNER_UIDS = 17; +pub const FD_CLOEXEC = 1; + pub const SEEK_SET = 0; pub const SEEK_CUR = 1; pub const SEEK_END = 2; diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 2a58c14490..89364909c9 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -136,6 +136,8 @@ pub const MAP_FIXED_NOREPLACE = 0x100000; /// For anonymous mmap, memory could be uninitialized pub const MAP_UNINITIALIZED = 0x4000000; +pub const FD_CLOEXEC = 1; + pub const F_OK = 0; pub const X_OK = 1; pub const W_OK = 2; diff --git a/lib/std/os/bits/netbsd.zig b/lib/std/os/bits/netbsd.zig index 735485695a..4abd4c8c5e 100644 --- a/lib/std/os/bits/netbsd.zig +++ b/lib/std/os/bits/netbsd.zig @@ -312,6 +312,8 @@ pub const F_GETLK = 7; pub const F_SETLK = 8; pub const F_SETLKW = 9; +pub const FD_CLOEXEC = 1; + pub const SEEK_SET = 0; pub const SEEK_CUR = 1; pub const SEEK_END = 2; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index ba7356d62c..ae48d831cf 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -465,17 +465,17 @@ pub fn renameat(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const return syscall4( SYS_renameat, @bitCast(usize, @as(isize, oldfd)), - @ptrToInt(old), + @ptrToInt(oldpath), @bitCast(usize, @as(isize, newfd)), - @ptrToInt(new), + @ptrToInt(newpath), ); } else { return syscall5( SYS_renameat2, @bitCast(usize, @as(isize, oldfd)), - @ptrToInt(old), + @ptrToInt(oldpath), @bitCast(usize, @as(isize, newfd)), - @ptrToInt(new), + @ptrToInt(newpath), 0, ); } @@ -588,6 +588,10 @@ pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize { return syscall4(SYS_wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0); } +pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) usize { + return syscall3(SYS_fcntl, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, cmd)), arg); +} + var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime); // We must follow the C calling convention when we call into the VDSO diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 1ef6798b50..154932d9c5 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -1,7 +1,8 @@ const std = @import("../std.zig"); const os = std.os; const testing = std.testing; -const expect = std.testing.expect; +const expect = testing.expect; +const expectEqual = testing.expectEqual; const io = std.io; const fs = std.fs; const mem = std.mem; @@ -19,8 +20,8 @@ test "makePath, put some files in it, deleteTree" { try fs.cwd().makePath("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c"); try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense"); try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah"); - try fs.deleteTree("os_test_tmp"); - if (fs.cwd().openDirTraverse("os_test_tmp")) |dir| { + try fs.cwd().deleteTree("os_test_tmp"); + if (fs.cwd().openDir("os_test_tmp", .{})) |dir| { @panic("expected error"); } else |err| { expect(err == error.FileNotFound); @@ -37,7 +38,7 @@ test "access file" { try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", ""); try os.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", os.F_OK); - try fs.deleteTree("os_test_tmp"); + try fs.cwd().deleteTree("os_test_tmp"); } fn testThreadIdFn(thread_id: *Thread.Id) void { @@ -46,9 +47,9 @@ fn testThreadIdFn(thread_id: *Thread.Id) void { test "sendfile" { try fs.cwd().makePath("os_test_tmp"); - defer fs.deleteTree("os_test_tmp") catch {}; + defer fs.cwd().deleteTree("os_test_tmp") catch {}; - var dir = try fs.cwd().openDirList("os_test_tmp"); + var dir = try fs.cwd().openDir("os_test_tmp", .{}); defer dir.close(); const line1 = "line1\n"; @@ -112,14 +113,16 @@ test "fs.copyFile" { const dest_file = "tmp_test_copy_file2.txt"; const dest_file2 = "tmp_test_copy_file3.txt"; - try fs.cwd().writeFile(src_file, data); - defer fs.cwd().deleteFile(src_file) catch {}; + const cwd = fs.cwd(); - try fs.copyFile(src_file, dest_file); - defer fs.cwd().deleteFile(dest_file) catch {}; + try cwd.writeFile(src_file, data); + defer cwd.deleteFile(src_file) catch {}; - try fs.copyFileMode(src_file, dest_file2, File.default_mode); - defer fs.cwd().deleteFile(dest_file2) catch {}; + try cwd.copyFile(src_file, cwd, dest_file, .{}); + defer cwd.deleteFile(dest_file) catch {}; + + try cwd.copyFile(src_file, cwd, dest_file2, .{ .override_mode = File.default_mode }); + defer cwd.deleteFile(dest_file2) catch {}; try expectFileContents(dest_file, data); try expectFileContents(dest_file2, data); @@ -446,3 +449,32 @@ test "getenv" { expect(os.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null); } } + +test "fcntl" { + if (builtin.os.tag == .windows) + return error.SkipZigTest; + + const test_out_file = "os_tmp_test"; + + const file = try fs.cwd().createFile(test_out_file, .{}); + defer { + file.close(); + fs.cwd().deleteFile(test_out_file) catch {}; + } + + // Note: The test assumes createFile opens the file with O_CLOEXEC + { + const flags = try os.fcntl(file.handle, os.F_GETFD, 0); + expect((flags & os.FD_CLOEXEC) != 0); + } + { + _ = try os.fcntl(file.handle, os.F_SETFD, 0); + const flags = try os.fcntl(file.handle, os.F_GETFD, 0); + expect((flags & os.FD_CLOEXEC) == 0); + } + { + _ = try os.fcntl(file.handle, os.F_SETFD, os.FD_CLOEXEC); + const flags = try os.fcntl(file.handle, os.F_GETFD, 0); + expect((flags & os.FD_CLOEXEC) != 0); + } +} diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 6c9a1f24b7..813a77c275 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -88,6 +88,82 @@ pub fn CreateFileW( return result; } +pub const OpenError = error{ + IsDir, + FileNotFound, + NoDevice, + SharingViolation, + AccessDenied, + PipeBusy, + PathAlreadyExists, + Unexpected, + NameTooLong, +}; + +/// TODO rename to CreateFileW +/// TODO actually we don't need the path parameter to be null terminated +pub fn OpenFileW( + dir: ?HANDLE, + sub_path_w: [*:0]const u16, + sa: ?*SECURITY_ATTRIBUTES, + access_mask: ACCESS_MASK, + creation: ULONG, +) OpenError!HANDLE { + if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { + return error.IsDir; + } + if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { + return error.IsDir; + } + + var result: HANDLE = undefined; + + const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) { + error.Overflow => return error.NameTooLong, + }; + var nt_name = UNICODE_STRING{ + .Length = path_len_bytes, + .MaximumLength = path_len_bytes, + .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), + }; + var attr = OBJECT_ATTRIBUTES{ + .Length = @sizeOf(OBJECT_ATTRIBUTES), + .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir, + .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. + .ObjectName = &nt_name, + .SecurityDescriptor = if (sa) |ptr| ptr.lpSecurityDescriptor else null, + .SecurityQualityOfService = null, + }; + var io: IO_STATUS_BLOCK = undefined; + const rc = ntdll.NtCreateFile( + &result, + access_mask, + &attr, + &io, + null, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, + creation, + FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, + null, + 0, + ); + switch (rc) { + .SUCCESS => return result, + .OBJECT_NAME_INVALID => unreachable, + .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, + .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, + .NO_MEDIA_IN_DEVICE => return error.NoDevice, + .INVALID_PARAMETER => unreachable, + .SHARING_VIOLATION => return error.SharingViolation, + .ACCESS_DENIED => return error.AccessDenied, + .PIPE_BUSY => return error.PipeBusy, + .OBJECT_PATH_SYNTAX_BAD => unreachable, + .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, + else => return unexpectedStatus(rc), + } +} + pub const CreatePipeError = error{Unexpected}; pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void { @@ -1200,7 +1276,15 @@ pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError { // 614 is the length of the longest windows error desciption var buf_u16: [614]u16 = undefined; var buf_u8: [614]u8 = undefined; - var len = kernel32.FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, null, err, MAKELANGID(LANG.NEUTRAL, SUBLANG.DEFAULT), buf_u16[0..].ptr, buf_u16.len / @sizeOf(TCHAR), null); + const len = kernel32.FormatMessageW( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + null, + err, + MAKELANGID(LANG.NEUTRAL, SUBLANG.DEFAULT), + &buf_u16, + buf_u16.len / @sizeOf(TCHAR), + null, + ); _ = std.unicode.utf16leToUtf8(&buf_u8, buf_u16[0..len]) catch unreachable; std.debug.warn("error.Unexpected: GetLastError({}): {}\n", .{ @enumToInt(err), buf_u8[0..len] }); std.debug.dumpCurrentStackTrace(null); diff --git a/lib/std/os/windows/bits.zig b/lib/std/os/windows/bits.zig index 1e49b903cb..16cc6d7441 100644 --- a/lib/std/os/windows/bits.zig +++ b/lib/std/os/windows/bits.zig @@ -242,6 +242,13 @@ pub const FILE_NAME_INFORMATION = extern struct { FileName: [1]WCHAR, }; +pub const FILE_RENAME_INFORMATION = extern struct { + ReplaceIfExists: BOOLEAN, + RootDirectory: ?HANDLE, + FileNameLength: ULONG, + FileName: [1]WCHAR, +}; + pub const IO_STATUS_BLOCK = extern struct { // "DUMMYUNIONNAME" expands to "u" u: extern union { diff --git a/lib/std/rand.zig b/lib/std/rand.zig index 31891f5f0e..ede46e3065 100644 --- a/lib/std/rand.zig +++ b/lib/std/rand.zig @@ -5,7 +5,7 @@ // ``` // var buf: [8]u8 = undefined; // try std.crypto.randomBytes(buf[0..]); -// const seed = mem.readIntSliceLittle(u64, buf[0..8]); +// const seed = mem.readIntLittle(u64, buf[0..8]); // // var r = DefaultPrng.init(seed); // diff --git a/lib/std/thread.zig b/lib/std/thread.zig index b2f8a44a47..596a8f3cd9 100644 --- a/lib/std/thread.zig +++ b/lib/std/thread.zig @@ -6,6 +6,8 @@ const windows = std.os.windows; const c = std.c; const assert = std.debug.assert; +const bad_startfn_ret = "expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"; + pub const Thread = struct { data: Data, @@ -158,15 +160,34 @@ pub const Thread = struct { }; fn threadMain(raw_arg: windows.LPVOID) callconv(.C) windows.DWORD { const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*; + switch (@typeInfo(@TypeOf(startFn).ReturnType)) { - .Int => { - return startFn(arg); + .NoReturn => { + startFn(arg); }, .Void => { startFn(arg); return 0; }, - else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), + .Int => |info| { + if (info.bits != 8) { + @compileError(bad_startfn_ret); + } + return startFn(arg); + }, + .ErrorUnion => |info| { + if (info.payload != void) { + @compileError(bad_startfn_ret); + } + startFn(arg) catch |err| { + std.debug.warn("error: {}\n", .{@errorName(err)}); + if (@errorReturnTrace()) |trace| { + std.debug.dumpStackTrace(trace.*); + } + }; + return 0; + }, + else => @compileError(bad_startfn_ret), } } }; @@ -202,14 +223,32 @@ pub const Thread = struct { const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*; switch (@typeInfo(@TypeOf(startFn).ReturnType)) { - .Int => { - return startFn(arg); + .NoReturn => { + startFn(arg); }, .Void => { startFn(arg); return 0; }, - else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), + .Int => |info| { + if (info.bits != 8) { + @compileError(bad_startfn_ret); + } + return startFn(arg); + }, + .ErrorUnion => |info| { + if (info.payload != void) { + @compileError(bad_startfn_ret); + } + startFn(arg) catch |err| { + std.debug.warn("error: {}\n", .{@errorName(err)}); + if (@errorReturnTrace()) |trace| { + std.debug.dumpStackTrace(trace.*); + } + }; + return 0; + }, + else => @compileError(bad_startfn_ret), } } fn posixThreadMain(ctx: ?*c_void) callconv(.C) ?*c_void { diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig index 8ed51fa145..a971a730e8 100644 --- a/lib/std/unicode.zig +++ b/lib/std/unicode.zig @@ -251,12 +251,12 @@ pub const Utf16LeIterator = struct { pub fn nextCodepoint(it: *Utf16LeIterator) !?u21 { assert(it.i <= it.bytes.len); if (it.i == it.bytes.len) return null; - const c0: u21 = mem.readIntSliceLittle(u16, it.bytes[it.i .. it.i + 2]); + const c0: u21 = mem.readIntLittle(u16, it.bytes[it.i..][0..2]); if (c0 & ~@as(u21, 0x03ff) == 0xd800) { // surrogate pair it.i += 2; if (it.i >= it.bytes.len) return error.DanglingSurrogateHalf; - const c1: u21 = mem.readIntSliceLittle(u16, it.bytes[it.i .. it.i + 2]); + const c1: u21 = mem.readIntLittle(u16, it.bytes[it.i..][0..2]); if (c1 & ~@as(u21, 0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf; it.i += 2; return 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff)); @@ -630,11 +630,11 @@ test "utf8ToUtf16LeWithNull" { } } -/// Converts a UTF-8 string literal into a UTF-16LE string literal. -pub fn utf8ToUtf16LeStringLiteral(comptime utf8: []const u8) *const [calcUtf16LeLen(utf8) :0] u16 { +/// Converts a UTF-8 string literal into a UTF-16LE string literal. +pub fn utf8ToUtf16LeStringLiteral(comptime utf8: []const u8) *const [calcUtf16LeLen(utf8):0]u16 { comptime { const len: usize = calcUtf16LeLen(utf8); - var utf16le: [len :0]u16 = [_ :0]u16{0} ** len; + var utf16le: [len:0]u16 = [_:0]u16{0} ** len; const utf16le_len = utf8ToUtf16Le(&utf16le, utf8[0..]) catch |err| @compileError(err); assert(len == utf16le_len); return &utf16le; @@ -660,8 +660,8 @@ fn calcUtf16LeLen(utf8: []const u8) usize { } test "utf8ToUtf16LeStringLiteral" { -{ - const bytes = [_:0]u16{ 0x41 }; + { + const bytes = [_:0]u16{0x41}; const utf16 = utf8ToUtf16LeStringLiteral("A"); testing.expectEqualSlices(u16, &bytes, utf16); testing.expect(utf16[1] == 0); @@ -673,19 +673,19 @@ test "utf8ToUtf16LeStringLiteral" { testing.expect(utf16[2] == 0); } { - const bytes = [_:0]u16{ 0x02FF }; + const bytes = [_:0]u16{0x02FF}; const utf16 = utf8ToUtf16LeStringLiteral("\u{02FF}"); testing.expectEqualSlices(u16, &bytes, utf16); testing.expect(utf16[1] == 0); } { - const bytes = [_:0]u16{ 0x7FF }; + const bytes = [_:0]u16{0x7FF}; const utf16 = utf8ToUtf16LeStringLiteral("\u{7FF}"); testing.expectEqualSlices(u16, &bytes, utf16); testing.expect(utf16[1] == 0); } { - const bytes = [_:0]u16{ 0x801 }; + const bytes = [_:0]u16{0x801}; const utf16 = utf8ToUtf16LeStringLiteral("\u{801}"); testing.expectEqualSlices(u16, &bytes, utf16); testing.expect(utf16[1] == 0); diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 6738bb34d7..6bbb74dab0 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -743,11 +743,11 @@ pub const Node = struct { var i = index; switch (self.init_arg_expr) { - InitArg.Type => |t| { + .Type => |t| { if (i < 1) return t; i -= 1; }, - InitArg.None, InitArg.Enum => {}, + .None, .Enum => {}, } if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i).*; @@ -907,12 +907,7 @@ pub const Node = struct { } switch (self.return_type) { - // TODO allow this and next prong to share bodies since the types are the same - ReturnType.Explicit => |node| { - if (i < 1) return node; - i -= 1; - }, - ReturnType.InferErrorSet => |node| { + .Explicit, .InferErrorSet => |node| { if (i < 1) return node; i -= 1; }, @@ -937,9 +932,7 @@ pub const Node = struct { pub fn lastToken(self: *const FnProto) TokenIndex { if (self.body_node) |body_node| return body_node.lastToken(); switch (self.return_type) { - // TODO allow this and next prong to share bodies since the types are the same - ReturnType.Explicit => |node| return node.lastToken(), - ReturnType.InferErrorSet => |node| return node.lastToken(), + .Explicit, .InferErrorSet => |node| return node.lastToken(), } } }; @@ -1515,55 +1508,55 @@ pub const Node = struct { i -= 1; switch (self.op) { - Op.Catch => |maybe_payload| { + .Catch => |maybe_payload| { if (maybe_payload) |payload| { if (i < 1) return payload; i -= 1; } }, - Op.Add, - Op.AddWrap, - Op.ArrayCat, - Op.ArrayMult, - Op.Assign, - Op.AssignBitAnd, - Op.AssignBitOr, - Op.AssignBitShiftLeft, - Op.AssignBitShiftRight, - Op.AssignBitXor, - Op.AssignDiv, - Op.AssignSub, - Op.AssignSubWrap, - Op.AssignMod, - Op.AssignAdd, - Op.AssignAddWrap, - Op.AssignMul, - Op.AssignMulWrap, - Op.BangEqual, - Op.BitAnd, - Op.BitOr, - Op.BitShiftLeft, - Op.BitShiftRight, - Op.BitXor, - Op.BoolAnd, - Op.BoolOr, - Op.Div, - Op.EqualEqual, - Op.ErrorUnion, - Op.GreaterOrEqual, - Op.GreaterThan, - Op.LessOrEqual, - Op.LessThan, - Op.MergeErrorSets, - Op.Mod, - Op.Mul, - Op.MulWrap, - Op.Period, - Op.Range, - Op.Sub, - Op.SubWrap, - Op.UnwrapOptional, + .Add, + .AddWrap, + .ArrayCat, + .ArrayMult, + .Assign, + .AssignBitAnd, + .AssignBitOr, + .AssignBitShiftLeft, + .AssignBitShiftRight, + .AssignBitXor, + .AssignDiv, + .AssignSub, + .AssignSubWrap, + .AssignMod, + .AssignAdd, + .AssignAddWrap, + .AssignMul, + .AssignMulWrap, + .BangEqual, + .BitAnd, + .BitOr, + .BitShiftLeft, + .BitShiftRight, + .BitXor, + .BoolAnd, + .BoolOr, + .Div, + .EqualEqual, + .ErrorUnion, + .GreaterOrEqual, + .GreaterThan, + .LessOrEqual, + .LessThan, + .MergeErrorSets, + .Mod, + .Mul, + .MulWrap, + .Period, + .Range, + .Sub, + .SubWrap, + .UnwrapOptional, => {}, } @@ -1594,7 +1587,6 @@ pub const Node = struct { Await, BitNot, BoolNot, - Cancel, OptionalType, Negation, NegationWrap, @@ -1631,8 +1623,7 @@ pub const Node = struct { var i = index; switch (self.op) { - // TODO https://github.com/ziglang/zig/issues/1107 - Op.SliceType => |addr_of_info| { + .PtrType, .SliceType => |addr_of_info| { if (addr_of_info.sentinel) |sentinel| { if (i < 1) return sentinel; i -= 1; @@ -1644,14 +1635,7 @@ pub const Node = struct { } }, - Op.PtrType => |addr_of_info| { - if (addr_of_info.align_info) |align_info| { - if (i < 1) return align_info.node; - i -= 1; - } - }, - - Op.ArrayType => |array_info| { + .ArrayType => |array_info| { if (i < 1) return array_info.len_expr; i -= 1; if (array_info.sentinel) |sentinel| { @@ -1660,16 +1644,15 @@ pub const Node = struct { } }, - Op.AddressOf, - Op.Await, - Op.BitNot, - Op.BoolNot, - Op.Cancel, - Op.OptionalType, - Op.Negation, - Op.NegationWrap, - Op.Try, - Op.Resume, + .AddressOf, + .Await, + .BitNot, + .BoolNot, + .OptionalType, + .Negation, + .NegationWrap, + .Try, + .Resume, => {}, } @@ -1853,19 +1836,14 @@ pub const Node = struct { var i = index; switch (self.kind) { - Kind.Break => |maybe_label| { + .Break, + .Continue => |maybe_label| { if (maybe_label) |label| { if (i < 1) return label; i -= 1; } }, - Kind.Continue => |maybe_label| { - if (maybe_label) |label| { - if (i < 1) return label; - i -= 1; - } - }, - Kind.Return => {}, + .Return => {}, } if (self.rhs) |rhs| { @@ -1886,17 +1864,13 @@ pub const Node = struct { } switch (self.kind) { - Kind.Break => |maybe_label| { + .Break, + .Continue => |maybe_label| { if (maybe_label) |label| { return label.lastToken(); } }, - Kind.Continue => |maybe_label| { - if (maybe_label) |label| { - return label.lastToken(); - } - }, - Kind.Return => return self.ltoken, + .Return => return self.ltoken, } return self.ltoken; @@ -2137,11 +2111,11 @@ pub const Node = struct { i -= 1; switch (self.kind) { - Kind.Variable => |variable_name| { + .Variable => |variable_name| { if (i < 1) return &variable_name.base; i -= 1; }, - Kind.Return => |return_type| { + .Return => |return_type| { if (i < 1) return return_type; i -= 1; }, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 9a574c6231..b0c3e6d759 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -23,7 +23,7 @@ pub fn parse(allocator: *Allocator, source: []const u8) Allocator.Error!*Tree { var arena = std.heap.ArenaAllocator.init(allocator); errdefer arena.deinit(); const tree = try arena.allocator.create(ast.Tree); - tree.* = ast.Tree{ + tree.* = .{ .source = source, .root_node = undefined, .arena_allocator = arena, @@ -66,10 +66,10 @@ pub fn parse(allocator: *Allocator, source: []const u8) Allocator.Error!*Tree { /// Root <- skip ContainerMembers eof fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!*Node.Root { const node = try arena.create(Node.Root); - node.* = Node.Root{ + node.* = .{ .decls = try parseContainerMembers(arena, it, tree), .eof_token = eatToken(it, .Eof) orelse { - try tree.errors.push(AstError{ + try tree.errors.push(.{ .ExpectedContainerMembers = .{ .token = it.index }, }); return error.ParseError; @@ -139,8 +139,8 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No } if (visib_token != null) { - try tree.errors.push(AstError{ - .ExpectedPubItem = AstError.ExpectedPubItem{ .token = it.index }, + try tree.errors.push(.{ + .ExpectedPubItem = .{ .token = it.index }, }); return error.ParseError; } @@ -157,8 +157,8 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No // Dangling doc comment if (doc_comments != null) { - try tree.errors.push(AstError{ - .UnattachedDocComment = AstError.UnattachedDocComment{ .token = doc_comments.?.firstToken() }, + try tree.errors.push(.{ + .UnattachedDocComment = .{ .token = doc_comments.?.firstToken() }, }); } break; @@ -177,7 +177,7 @@ fn parseContainerDocComments(arena: *Allocator, it: *TokenIterator, tree: *Tree) if (lines.len == 0) return null; const node = try arena.create(Node.DocComment); - node.* = Node.DocComment{ + node.* = .{ .lines = lines, }; return &node.base; @@ -186,15 +186,15 @@ fn parseContainerDocComments(arena: *Allocator, it: *TokenIterator, tree: *Tree) /// TestDecl <- KEYWORD_test STRINGLITERALSINGLE Block fn parseTestDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const test_token = eatToken(it, .Keyword_test) orelse return null; - const name_node = try expectNode(arena, it, tree, parseStringLiteralSingle, AstError{ - .ExpectedStringLiteral = AstError.ExpectedStringLiteral{ .token = it.index }, + const name_node = try expectNode(arena, it, tree, parseStringLiteralSingle, .{ + .ExpectedStringLiteral = .{ .token = it.index }, }); - const block_node = try expectNode(arena, it, tree, parseBlock, AstError{ - .ExpectedLBrace = AstError.ExpectedLBrace{ .token = it.index }, + const block_node = try expectNode(arena, it, tree, parseBlock, .{ + .ExpectedLBrace = .{ .token = it.index }, }); const test_node = try arena.create(Node.TestDecl); - test_node.* = Node.TestDecl{ + test_node.* = .{ .doc_comments = null, .test_token = test_token, .name = name_node, @@ -211,12 +211,12 @@ fn parseTopLevelComptime(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?* return null; }; putBackToken(it, lbrace); - const block_node = try expectNode(arena, it, tree, parseBlockExpr, AstError{ - .ExpectedLabelOrLBrace = AstError.ExpectedLabelOrLBrace{ .token = it.index }, + const block_node = try expectNode(arena, it, tree, parseBlockExpr, .{ + .ExpectedLabelOrLBrace = .{ .token = it.index }, }); const comptime_node = try arena.create(Node.Comptime); - comptime_node.* = Node.Comptime{ + comptime_node.* = .{ .doc_comments = null, .comptime_token = tok, .expr = block_node, @@ -250,8 +250,8 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node fn_node.body_node = body_node; return node; } - try tree.errors.push(AstError{ - .ExpectedSemiOrLBrace = AstError.ExpectedSemiOrLBrace{ .token = it.index }, + try tree.errors.push(.{ + .ExpectedSemiOrLBrace = .{ .token = it.index }, }); return null; } @@ -277,8 +277,8 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node } if (thread_local_token != null) { - try tree.errors.push(AstError{ - .ExpectedVarDecl = AstError.ExpectedVarDecl{ .token = it.index }, + try tree.errors.push(.{ + .ExpectedVarDecl = .{ .token = it.index }, }); return error.ParseError; } @@ -291,8 +291,8 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node } const use_node = (try parseUse(arena, it, tree)) orelse return null; - const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const expr_node = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); const semicolon_token = try expectToken(it, tree, .Semicolon); const use_node_raw = use_node.cast(Node.Use).?; @@ -310,7 +310,7 @@ fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { if (fnCC == .Extern) { putBackToken(it, fnCC.Extern); // 'extern' is also used in ContainerDecl } else { - try tree.errors.push(AstError{ + try tree.errors.push(.{ .ExpectedToken = .{ .token = it.index, .expected_id = .Keyword_fn }, }); return error.ParseError; @@ -328,16 +328,16 @@ fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const exclamation_token = eatToken(it, .Bang); const return_type_expr = (try parseVarType(arena, it, tree)) orelse - try expectNode(arena, it, tree, parseTypeExpr, AstError{ - .ExpectedReturnType = AstError.ExpectedReturnType{ .token = it.index }, + try expectNode(arena, it, tree, parseTypeExpr, .{ + .ExpectedReturnType = .{ .token = it.index }, }); - const return_type = if (exclamation_token != null) - Node.FnProto.ReturnType{ + const return_type: Node.FnProto.ReturnType = if (exclamation_token != null) + .{ .InferErrorSet = return_type_expr, } else - Node.FnProto.ReturnType{ + .{ .Explicit = return_type_expr, }; @@ -347,7 +347,7 @@ fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { null; const fn_proto_node = try arena.create(Node.FnProto); - fn_proto_node.* = Node.FnProto{ + fn_proto_node.* = .{ .doc_comments = null, .visib_token = null, .fn_token = fn_token, @@ -382,8 +382,8 @@ fn parseVarDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const name_token = try expectToken(it, tree, .Identifier); const type_node = if (eatToken(it, .Colon) != null) - try expectNode(arena, it, tree, parseTypeExpr, AstError{ - .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index }, + try expectNode(arena, it, tree, parseTypeExpr, .{ + .ExpectedTypeExpr = .{ .token = it.index }, }) else null; @@ -391,14 +391,14 @@ fn parseVarDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const section_node = try parseLinkSection(arena, it, tree); const eq_token = eatToken(it, .Equal); const init_node = if (eq_token != null) blk: { - break :blk try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + break :blk try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); } else null; const semicolon_token = try expectToken(it, tree, .Semicolon); const node = try arena.create(Node.VarDecl); - node.* = Node.VarDecl{ + node.* = .{ .doc_comments = null, .visib_token = null, .thread_local_token = null, @@ -433,22 +433,22 @@ fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No node.* = .{ .token = var_tok }; type_expr = &node.base; } else { - type_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{ - .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index }, + type_expr = try expectNode(arena, it, tree, parseTypeExpr, .{ + .ExpectedTypeExpr = .{ .token = it.index }, }); align_expr = try parseByteAlign(arena, it, tree); } } const value_expr = if (eatToken(it, .Equal)) |_| - try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }) else null; const node = try arena.create(Node.ContainerField); - node.* = Node.ContainerField{ + node.* = .{ .doc_comments = null, .comptime_token = comptime_token, .name_token = name_token, @@ -481,12 +481,12 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No } if (comptime_token) |token| { - const block_expr = try expectNode(arena, it, tree, parseBlockExprStatement, AstError{ - .ExpectedBlockOrAssignment = AstError.ExpectedBlockOrAssignment{ .token = it.index }, + const block_expr = try expectNode(arena, it, tree, parseBlockExprStatement, .{ + .ExpectedBlockOrAssignment = .{ .token = it.index }, }); const node = try arena.create(Node.Comptime); - node.* = Node.Comptime{ + node.* = .{ .doc_comments = null, .comptime_token = token, .expr = block_expr, @@ -511,13 +511,13 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No const semicolon = eatToken(it, .Semicolon); const body_node = if (semicolon == null) blk: { - break :blk try expectNode(arena, it, tree, parseBlockExprStatement, AstError{ - .ExpectedBlockOrExpression = AstError.ExpectedBlockOrExpression{ .token = it.index }, + break :blk try expectNode(arena, it, tree, parseBlockExprStatement, .{ + .ExpectedBlockOrExpression = .{ .token = it.index }, }); } else null; const node = try arena.create(Node.Suspend); - node.* = Node.Suspend{ + node.* = .{ .suspend_token = suspend_token, .body = body_node, }; @@ -526,11 +526,11 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No const defer_token = eatToken(it, .Keyword_defer) orelse eatToken(it, .Keyword_errdefer); if (defer_token) |token| { - const expr_node = try expectNode(arena, it, tree, parseBlockExprStatement, AstError{ - .ExpectedBlockOrExpression = AstError.ExpectedBlockOrExpression{ .token = it.index }, + const expr_node = try expectNode(arena, it, tree, parseBlockExprStatement, .{ + .ExpectedBlockOrExpression = .{ .token = it.index }, }); const node = try arena.create(Node.Defer); - node.* = Node.Defer{ + node.* = .{ .defer_token = token, .expr = expr_node, }; @@ -561,8 +561,8 @@ fn parseIfStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node } else null; if (block_expr == null and assign_expr == null) { - try tree.errors.push(AstError{ - .ExpectedBlockOrAssignment = AstError.ExpectedBlockOrAssignment{ .token = it.index }, + try tree.errors.push(.{ + .ExpectedBlockOrAssignment = .{ .token = it.index }, }); return error.ParseError; } @@ -572,12 +572,12 @@ fn parseIfStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const else_node = if (semicolon == null) blk: { const else_token = eatToken(it, .Keyword_else) orelse break :blk null; const payload = try parsePayload(arena, it, tree); - const else_body = try expectNode(arena, it, tree, parseStatement, AstError{ - .InvalidToken = AstError.InvalidToken{ .token = it.index }, + const else_body = try expectNode(arena, it, tree, parseStatement, .{ + .InvalidToken = .{ .token = it.index }, }); const node = try arena.create(Node.Else); - node.* = Node.Else{ + node.* = .{ .else_token = else_token, .payload = payload, .body = else_body, @@ -599,8 +599,8 @@ fn parseIfStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node if_prefix.@"else" = else_node; return if_node; } - try tree.errors.push(AstError{ - .ExpectedSemiOrElse = AstError.ExpectedSemiOrElse{ .token = it.index }, + try tree.errors.push(.{ + .ExpectedSemiOrElse = .{ .token = it.index }, }); return error.ParseError; } @@ -628,8 +628,8 @@ fn parseLabeledStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?* } if (label_token != null) { - try tree.errors.push(AstError{ - .ExpectedLabelable = AstError.ExpectedLabelable{ .token = it.index }, + try tree.errors.push(.{ + .ExpectedLabelable = .{ .token = it.index }, }); return error.ParseError; } @@ -665,12 +665,12 @@ fn parseForStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node for_prefix.body = block_expr_node; if (eatToken(it, .Keyword_else)) |else_token| { - const statement_node = try expectNode(arena, it, tree, parseStatement, AstError{ - .InvalidToken = AstError.InvalidToken{ .token = it.index }, + const statement_node = try expectNode(arena, it, tree, parseStatement, .{ + .InvalidToken = .{ .token = it.index }, }); const else_node = try arena.create(Node.Else); - else_node.* = Node.Else{ + else_node.* = .{ .else_token = else_token, .payload = null, .body = statement_node, @@ -689,12 +689,12 @@ fn parseForStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node if (eatToken(it, .Semicolon) != null) return node; if (eatToken(it, .Keyword_else)) |else_token| { - const statement_node = try expectNode(arena, it, tree, parseStatement, AstError{ - .ExpectedStatement = AstError.ExpectedStatement{ .token = it.index }, + const statement_node = try expectNode(arena, it, tree, parseStatement, .{ + .ExpectedStatement = .{ .token = it.index }, }); const else_node = try arena.create(Node.Else); - else_node.* = Node.Else{ + else_node.* = .{ .else_token = else_token, .payload = null, .body = statement_node, @@ -703,8 +703,8 @@ fn parseForStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node return node; } - try tree.errors.push(AstError{ - .ExpectedSemiOrElse = AstError.ExpectedSemiOrElse{ .token = it.index }, + try tree.errors.push(.{ + .ExpectedSemiOrElse = .{ .token = it.index }, }); return null; } @@ -725,12 +725,12 @@ fn parseWhileStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No if (eatToken(it, .Keyword_else)) |else_token| { const payload = try parsePayload(arena, it, tree); - const statement_node = try expectNode(arena, it, tree, parseStatement, AstError{ - .InvalidToken = AstError.InvalidToken{ .token = it.index }, + const statement_node = try expectNode(arena, it, tree, parseStatement, .{ + .InvalidToken = .{ .token = it.index }, }); const else_node = try arena.create(Node.Else); - else_node.* = Node.Else{ + else_node.* = .{ .else_token = else_token, .payload = payload, .body = statement_node, @@ -751,12 +751,12 @@ fn parseWhileStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No if (eatToken(it, .Keyword_else)) |else_token| { const payload = try parsePayload(arena, it, tree); - const statement_node = try expectNode(arena, it, tree, parseStatement, AstError{ - .ExpectedStatement = AstError.ExpectedStatement{ .token = it.index }, + const statement_node = try expectNode(arena, it, tree, parseStatement, .{ + .ExpectedStatement = .{ .token = it.index }, }); const else_node = try arena.create(Node.Else); - else_node.* = Node.Else{ + else_node.* = .{ .else_token = else_token, .payload = payload, .body = statement_node, @@ -765,8 +765,8 @@ fn parseWhileStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No return node; } - try tree.errors.push(AstError{ - .ExpectedSemiOrElse = AstError.ExpectedSemiOrElse{ .token = it.index }, + try tree.errors.push(.{ + .ExpectedSemiOrElse = .{ .token = it.index }, }); return null; } @@ -894,8 +894,8 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node } if (eatToken(it, .Keyword_comptime)) |token| { - const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const expr_node = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); const node = try arena.create(Node.Comptime); node.* = .{ @@ -907,8 +907,8 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node } if (eatToken(it, .Keyword_noasync)) |token| { - const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const expr_node = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); const node = try arena.create(Node.Noasync); node.* = .{ @@ -930,13 +930,13 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node } if (eatToken(it, .Keyword_resume)) |token| { - const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const expr_node = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); const node = try arena.create(Node.PrefixOp); node.* = .{ .op_token = token, - .op = Node.PrefixOp.Op.Resume, + .op = .Resume, .rhs = expr_node, }; return &node.base; @@ -992,7 +992,7 @@ fn parseBlock(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const rbrace = try expectToken(it, tree, .RBrace); const block_node = try arena.create(Node.Block); - block_node.* = Node.Block{ + block_node.* = .{ .label = null, .lbrace = lbrace, .statements = statements, @@ -1019,8 +1019,8 @@ fn parseLoopExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { if (inline_token == null) return null; // If we've seen "inline", there should have been a "for" or "while" - try tree.errors.push(AstError{ - .ExpectedInlinable = AstError.ExpectedInlinable{ .token = it.index }, + try tree.errors.push(.{ + .ExpectedInlinable = .{ .token = it.index }, }); return error.ParseError; } @@ -1030,18 +1030,18 @@ fn parseForExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = (try parseForPrefix(arena, it, tree)) orelse return null; const for_prefix = node.cast(Node.For).?; - const body_node = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const body_node = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); for_prefix.body = body_node; if (eatToken(it, .Keyword_else)) |else_token| { - const body = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const body = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); const else_node = try arena.create(Node.Else); - else_node.* = Node.Else{ + else_node.* = .{ .else_token = else_token, .payload = null, .body = body, @@ -1058,19 +1058,19 @@ fn parseWhileExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const node = (try parseWhilePrefix(arena, it, tree)) orelse return null; const while_prefix = node.cast(Node.While).?; - const body_node = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const body_node = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); while_prefix.body = body_node; if (eatToken(it, .Keyword_else)) |else_token| { const payload = try parsePayload(arena, it, tree); - const body = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const body = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); const else_node = try arena.create(Node.Else); - else_node.* = Node.Else{ + else_node.* = .{ .else_token = else_token, .payload = payload, .body = body, @@ -1098,14 +1098,14 @@ fn parseInitList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.Suf const lbrace = eatToken(it, .LBrace) orelse return null; var init_list = Node.SuffixOp.Op.InitList.init(arena); - const op = blk: { + const op: Node.SuffixOp.Op = blk: { if (try parseFieldInit(arena, it, tree)) |field_init| { try init_list.push(field_init); while (eatToken(it, .Comma)) |_| { const next = (try parseFieldInit(arena, it, tree)) orelse break; try init_list.push(next); } - break :blk Node.SuffixOp.Op{ .StructInitializer = init_list }; + break :blk .{ .StructInitializer = init_list }; } if (try parseExpr(arena, it, tree)) |expr| { @@ -1114,14 +1114,14 @@ fn parseInitList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.Suf const next = (try parseExpr(arena, it, tree)) orelse break; try init_list.push(next); } - break :blk Node.SuffixOp.Op{ .ArrayInitializer = init_list }; + break :blk .{ .ArrayInitializer = init_list }; } - break :blk Node.SuffixOp.Op{ .StructInitializer = init_list }; + break :blk .{ .StructInitializer = init_list }; }; const node = try arena.create(Node.SuffixOp); - node.* = Node.SuffixOp{ + node.* = .{ .lhs = .{ .node = undefined }, // set by caller .op = op, .rtoken = try expectToken(it, tree, .RBrace), @@ -1140,8 +1140,8 @@ fn parseErrorUnionExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No if (try SimpleBinOpParseFn(.Bang, Node.InfixOp.Op.ErrorUnion)(arena, it, tree)) |node| { const error_union = node.cast(Node.InfixOp).?; - const type_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{ - .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index }, + const type_expr = try expectNode(arena, it, tree, parseTypeExpr, .{ + .ExpectedTypeExpr = .{ .token = it.index }, }); error_union.lhs = suffix_expr; error_union.rhs = type_expr; @@ -1168,8 +1168,8 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { return parsePrimaryTypeExpr(arena, it, tree); } // TODO: Implement hack for parsing `async fn ...` in ast_parse_suffix_expr - var res = try expectNode(arena, it, tree, parsePrimaryTypeExpr, AstError{ - .ExpectedPrimaryTypeExpr = AstError.ExpectedPrimaryTypeExpr{ .token = it.index }, + var res = try expectNode(arena, it, tree, parsePrimaryTypeExpr, .{ + .ExpectedPrimaryTypeExpr = .{ .token = it.index }, }); while (try parseSuffixOp(arena, it, tree)) |node| { @@ -1182,16 +1182,16 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { } const params = (try parseFnCallArguments(arena, it, tree)) orelse { - try tree.errors.push(AstError{ - .ExpectedParamList = AstError.ExpectedParamList{ .token = it.index }, + try tree.errors.push(.{ + .ExpectedParamList = .{ .token = it.index }, }); return null; }; const node = try arena.create(Node.SuffixOp); - node.* = Node.SuffixOp{ + node.* = .{ .lhs = .{ .node = res }, - .op = Node.SuffixOp.Op{ - .Call = Node.SuffixOp.Op.Call{ + .op = .{ + .Call = .{ .params = params.list, .async_token = async_token, }, @@ -1215,10 +1215,10 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { } if (try parseFnCallArguments(arena, it, tree)) |params| { const call = try arena.create(Node.SuffixOp); - call.* = Node.SuffixOp{ + call.* = .{ .lhs = .{ .node = res }, - .op = Node.SuffixOp.Op{ - .Call = Node.SuffixOp.Op.Call{ + .op = .{ + .Call = .{ .params = params.list, .async_token = null, }, @@ -1264,7 +1264,7 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N if (try parseBuiltinCall(arena, it, tree)) |node| return node; if (eatToken(it, .CharLiteral)) |token| { const node = try arena.create(Node.CharLiteral); - node.* = Node.CharLiteral{ + node.* = .{ .token = token, }; return &node.base; @@ -1300,15 +1300,15 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N } if (eatToken(it, .Keyword_error)) |token| { const period = try expectToken(it, tree, .Period); - const identifier = try expectNode(arena, it, tree, parseIdentifier, AstError{ - .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index }, + const identifier = try expectNode(arena, it, tree, parseIdentifier, .{ + .ExpectedIdentifier = .{ .token = it.index }, }); const global_error_set = try createLiteral(arena, Node.ErrorType, token); const node = try arena.create(Node.InfixOp); node.* = .{ .op_token = period, .lhs = global_error_set, - .op = Node.InfixOp.Op.Period, + .op = .Period, .rhs = identifier, }; return &node.base; @@ -1358,7 +1358,7 @@ fn parseErrorSetDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const rbrace = try expectToken(it, tree, .RBrace); const node = try arena.create(Node.ErrorSetDecl); - node.* = Node.ErrorSetDecl{ + node.* = .{ .error_token = error_token, .decls = decls, .rbrace_token = rbrace, @@ -1369,13 +1369,13 @@ fn parseErrorSetDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node /// GroupedExpr <- LPAREN Expr RPAREN fn parseGroupedExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const lparen = eatToken(it, .LParen) orelse return null; - const expr = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const expr = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); const rparen = try expectToken(it, tree, .RParen); const node = try arena.create(Node.GroupedExpression); - node.* = Node.GroupedExpression{ + node.* = .{ .lparen = lparen, .expr = expr, .rparen = rparen, @@ -1435,8 +1435,8 @@ fn parseLoopTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node if (inline_token == null) return null; // If we've seen "inline", there should have been a "for" or "while" - try tree.errors.push(AstError{ - .ExpectedInlinable = AstError.ExpectedInlinable{ .token = it.index }, + try tree.errors.push(.{ + .ExpectedInlinable = .{ .token = it.index }, }); return error.ParseError; } @@ -1446,18 +1446,18 @@ fn parseForTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const node = (try parseForPrefix(arena, it, tree)) orelse return null; const for_prefix = node.cast(Node.For).?; - const type_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{ - .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index }, + const type_expr = try expectNode(arena, it, tree, parseTypeExpr, .{ + .ExpectedTypeExpr = .{ .token = it.index }, }); for_prefix.body = type_expr; if (eatToken(it, .Keyword_else)) |else_token| { - const else_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{ - .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index }, + const else_expr = try expectNode(arena, it, tree, parseTypeExpr, .{ + .ExpectedTypeExpr = .{ .token = it.index }, }); const else_node = try arena.create(Node.Else); - else_node.* = Node.Else{ + else_node.* = .{ .else_token = else_token, .payload = null, .body = else_expr, @@ -1474,20 +1474,20 @@ fn parseWhileTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Nod const node = (try parseWhilePrefix(arena, it, tree)) orelse return null; const while_prefix = node.cast(Node.While).?; - const type_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{ - .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index }, + const type_expr = try expectNode(arena, it, tree, parseTypeExpr, .{ + .ExpectedTypeExpr = .{ .token = it.index }, }); while_prefix.body = type_expr; if (eatToken(it, .Keyword_else)) |else_token| { const payload = try parsePayload(arena, it, tree); - const else_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{ - .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index }, + const else_expr = try expectNode(arena, it, tree, parseTypeExpr, .{ + .ExpectedTypeExpr = .{ .token = it.index }, }); const else_node = try arena.create(Node.Else); - else_node.* = Node.Else{ + else_node.* = .{ .else_token = else_token, .payload = null, .body = else_expr, @@ -1503,8 +1503,8 @@ fn parseWhileTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Nod fn parseSwitchExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const switch_token = eatToken(it, .Keyword_switch) orelse return null; _ = try expectToken(it, tree, .LParen); - const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const expr_node = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); _ = try expectToken(it, tree, .RParen); _ = try expectToken(it, tree, .LBrace); @@ -1512,7 +1512,7 @@ fn parseSwitchExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const rbrace = try expectToken(it, tree, .RBrace); const node = try arena.create(Node.Switch); - node.* = Node.Switch{ + node.* = .{ .switch_token = switch_token, .expr = expr_node, .cases = cases, @@ -1526,12 +1526,12 @@ fn parseAsmExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const asm_token = eatToken(it, .Keyword_asm) orelse return null; const volatile_token = eatToken(it, .Keyword_volatile); _ = try expectToken(it, tree, .LParen); - const template = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const template = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); const node = try arena.create(Node.Asm); - node.* = Node.Asm{ + node.* = .{ .asm_token = asm_token, .volatile_token = volatile_token, .template = template, @@ -1553,7 +1553,7 @@ fn parseAnonLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node // anon enum literal if (eatToken(it, .Identifier)) |name| { const node = try arena.create(Node.EnumLiteral); - node.* = Node.EnumLiteral{ + node.* = .{ .dot = dot, .name = name, }; @@ -1580,32 +1580,32 @@ fn parseAsmOutput(arena: *Allocator, it: *TokenIterator, tree: *Tree, asm_node: /// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN fn parseAsmOutputItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.AsmOutput { const lbracket = eatToken(it, .LBracket) orelse return null; - const name = try expectNode(arena, it, tree, parseIdentifier, AstError{ - .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index }, + const name = try expectNode(arena, it, tree, parseIdentifier, .{ + .ExpectedIdentifier = .{ .token = it.index }, }); _ = try expectToken(it, tree, .RBracket); - const constraint = try expectNode(arena, it, tree, parseStringLiteral, AstError{ - .ExpectedStringLiteral = AstError.ExpectedStringLiteral{ .token = it.index }, + const constraint = try expectNode(arena, it, tree, parseStringLiteral, .{ + .ExpectedStringLiteral = .{ .token = it.index }, }); _ = try expectToken(it, tree, .LParen); - const kind = blk: { + const kind: Node.AsmOutput.Kind = blk: { if (eatToken(it, .Arrow) != null) { - const return_ident = try expectNode(arena, it, tree, parseTypeExpr, AstError{ - .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index }, + const return_ident = try expectNode(arena, it, tree, parseTypeExpr, .{ + .ExpectedTypeExpr = .{ .token = it.index }, }); - break :blk Node.AsmOutput.Kind{ .Return = return_ident }; + break :blk .{ .Return = return_ident }; } - const variable = try expectNode(arena, it, tree, parseIdentifier, AstError{ - .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index }, + const variable = try expectNode(arena, it, tree, parseIdentifier, .{ + .ExpectedIdentifier = .{ .token = it.index }, }); - break :blk Node.AsmOutput.Kind{ .Variable = variable.cast(Node.Identifier).? }; + break :blk .{ .Variable = variable.cast(Node.Identifier).? }; }; const rparen = try expectToken(it, tree, .RParen); const node = try arena.create(Node.AsmOutput); - node.* = Node.AsmOutput{ + node.* = .{ .lbracket = lbracket, .symbolic_name = name, .constraint = constraint, @@ -1625,23 +1625,23 @@ fn parseAsmInput(arena: *Allocator, it: *TokenIterator, tree: *Tree, asm_node: * /// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN fn parseAsmInputItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.AsmInput { const lbracket = eatToken(it, .LBracket) orelse return null; - const name = try expectNode(arena, it, tree, parseIdentifier, AstError{ - .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index }, + const name = try expectNode(arena, it, tree, parseIdentifier, .{ + .ExpectedIdentifier = .{ .token = it.index }, }); _ = try expectToken(it, tree, .RBracket); - const constraint = try expectNode(arena, it, tree, parseStringLiteral, AstError{ - .ExpectedStringLiteral = AstError.ExpectedStringLiteral{ .token = it.index }, + const constraint = try expectNode(arena, it, tree, parseStringLiteral, .{ + .ExpectedStringLiteral = .{ .token = it.index }, }); _ = try expectToken(it, tree, .LParen); - const expr = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const expr = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); const rparen = try expectToken(it, tree, .RParen); const node = try arena.create(Node.AsmInput); - node.* = Node.AsmInput{ + node.* = .{ .lbracket = lbracket, .symbolic_name = name, .constraint = constraint, @@ -1664,8 +1664,8 @@ fn parseAsmClobbers(arena: *Allocator, it: *TokenIterator, tree: *Tree, asm_node /// BreakLabel <- COLON IDENTIFIER fn parseBreakLabel(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { _ = eatToken(it, .Colon) orelse return null; - return try expectNode(arena, it, tree, parseIdentifier, AstError{ - .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index }, + return try expectNode(arena, it, tree, parseIdentifier, .{ + .ExpectedIdentifier = .{ .token = it.index }, }); } @@ -1694,12 +1694,12 @@ fn parseFieldInit(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { putBackToken(it, period_token); return null; }; - const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const expr_node = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); const node = try arena.create(Node.FieldInitializer); - node.* = Node.FieldInitializer{ + node.* = .{ .period_token = period_token, .name_token = name_token, .expr = expr_node, @@ -1711,8 +1711,8 @@ fn parseFieldInit(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { fn parseWhileContinueExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { _ = eatToken(it, .Colon) orelse return null; _ = try expectToken(it, tree, .LParen); - const node = try expectNode(arena, it, tree, parseAssignExpr, AstError{ - .ExpectedExprOrAssignment = AstError.ExpectedExprOrAssignment{ .token = it.index }, + const node = try expectNode(arena, it, tree, parseAssignExpr, .{ + .ExpectedExprOrAssignment = .{ .token = it.index }, }); _ = try expectToken(it, tree, .RParen); return node; @@ -1722,8 +1722,8 @@ fn parseWhileContinueExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !? fn parseLinkSection(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { _ = eatToken(it, .Keyword_linksection) orelse return null; _ = try expectToken(it, tree, .LParen); - const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const expr_node = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); _ = try expectToken(it, tree, .RParen); return expr_node; @@ -1733,8 +1733,8 @@ fn parseLinkSection(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node fn parseCallconv(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { _ = eatToken(it, .Keyword_callconv) orelse return null; _ = try expectToken(it, tree, .LParen); - const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const expr_node = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); _ = try expectToken(it, tree, .RParen); return expr_node; @@ -1775,14 +1775,14 @@ fn parseParamDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { comptime_token == null and name_token == null and doc_comments == null) return null; - try tree.errors.push(AstError{ - .ExpectedParamType = AstError.ExpectedParamType{ .token = it.index }, + try tree.errors.push(.{ + .ExpectedParamType = .{ .token = it.index }, }); return error.ParseError; }; const param_decl = try arena.create(Node.ParamDecl); - param_decl.* = Node.ParamDecl{ + param_decl.* = .{ .doc_comments = doc_comments, .comptime_token = comptime_token, .noalias_token = noalias_token, @@ -1821,14 +1821,14 @@ const ParamType = union(enum) { fn parseIfPrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const if_token = eatToken(it, .Keyword_if) orelse return null; _ = try expectToken(it, tree, .LParen); - const condition = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const condition = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); _ = try expectToken(it, tree, .RParen); const payload = try parsePtrPayload(arena, it, tree); const node = try arena.create(Node.If); - node.* = Node.If{ + node.* = .{ .if_token = if_token, .condition = condition, .payload = payload, @@ -1843,8 +1843,8 @@ fn parseWhilePrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const while_token = eatToken(it, .Keyword_while) orelse return null; _ = try expectToken(it, tree, .LParen); - const condition = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const condition = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); _ = try expectToken(it, tree, .RParen); @@ -1852,7 +1852,7 @@ fn parseWhilePrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const continue_expr = try parseWhileContinueExpr(arena, it, tree); const node = try arena.create(Node.While); - node.* = Node.While{ + node.* = .{ .label = null, .inline_token = null, .while_token = while_token, @@ -1870,17 +1870,17 @@ fn parseForPrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const for_token = eatToken(it, .Keyword_for) orelse return null; _ = try expectToken(it, tree, .LParen); - const array_expr = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const array_expr = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); _ = try expectToken(it, tree, .RParen); - const payload = try expectNode(arena, it, tree, parsePtrIndexPayload, AstError{ - .ExpectedPayload = AstError.ExpectedPayload{ .token = it.index }, + const payload = try expectNode(arena, it, tree, parsePtrIndexPayload, .{ + .ExpectedPayload = .{ .token = it.index }, }); const node = try arena.create(Node.For); - node.* = Node.For{ + node.* = .{ .label = null, .inline_token = null, .for_token = for_token, @@ -1895,13 +1895,13 @@ fn parseForPrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { /// Payload <- PIPE IDENTIFIER PIPE fn parsePayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const lpipe = eatToken(it, .Pipe) orelse return null; - const identifier = try expectNode(arena, it, tree, parseIdentifier, AstError{ - .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index }, + const identifier = try expectNode(arena, it, tree, parseIdentifier, .{ + .ExpectedIdentifier = .{ .token = it.index }, }); const rpipe = try expectToken(it, tree, .Pipe); const node = try arena.create(Node.Payload); - node.* = Node.Payload{ + node.* = .{ .lpipe = lpipe, .error_symbol = identifier, .rpipe = rpipe, @@ -1913,13 +1913,13 @@ fn parsePayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { fn parsePtrPayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const lpipe = eatToken(it, .Pipe) orelse return null; const asterisk = eatToken(it, .Asterisk); - const identifier = try expectNode(arena, it, tree, parseIdentifier, AstError{ - .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index }, + const identifier = try expectNode(arena, it, tree, parseIdentifier, .{ + .ExpectedIdentifier = .{ .token = it.index }, }); const rpipe = try expectToken(it, tree, .Pipe); const node = try arena.create(Node.PointerPayload); - node.* = Node.PointerPayload{ + node.* = .{ .lpipe = lpipe, .ptr_token = asterisk, .value_symbol = identifier, @@ -1932,21 +1932,21 @@ fn parsePtrPayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { fn parsePtrIndexPayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const lpipe = eatToken(it, .Pipe) orelse return null; const asterisk = eatToken(it, .Asterisk); - const identifier = try expectNode(arena, it, tree, parseIdentifier, AstError{ - .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index }, + const identifier = try expectNode(arena, it, tree, parseIdentifier, .{ + .ExpectedIdentifier = .{ .token = it.index }, }); const index = if (eatToken(it, .Comma) == null) null else - try expectNode(arena, it, tree, parseIdentifier, AstError{ - .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index }, + try expectNode(arena, it, tree, parseIdentifier, .{ + .ExpectedIdentifier = .{ .token = it.index }, }); const rpipe = try expectToken(it, tree, .Pipe); const node = try arena.create(Node.PointerIndexPayload); - node.* = Node.PointerIndexPayload{ + node.* = .{ .lpipe = lpipe, .ptr_token = asterisk, .value_symbol = identifier, @@ -1961,8 +1961,8 @@ fn parseSwitchProng(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node const node = (try parseSwitchCase(arena, it, tree)) orelse return null; const arrow = try expectToken(it, tree, .EqualAngleBracketRight); const payload = try parsePtrPayload(arena, it, tree); - const expr = try expectNode(arena, it, tree, parseAssignExpr, AstError{ - .ExpectedExprOrAssignment = AstError.ExpectedExprOrAssignment{ .token = it.index }, + const expr = try expectNode(arena, it, tree, parseAssignExpr, .{ + .ExpectedExprOrAssignment = .{ .token = it.index }, }); const switch_case = node.cast(Node.SwitchCase).?; @@ -1987,14 +1987,14 @@ fn parseSwitchCase(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { } } else if (eatToken(it, .Keyword_else)) |else_token| { const else_node = try arena.create(Node.SwitchElse); - else_node.* = Node.SwitchElse{ + else_node.* = .{ .token = else_token, }; try list.push(&else_node.base); } else return null; const node = try arena.create(Node.SwitchCase); - node.* = Node.SwitchCase{ + node.* = .{ .items = list, .arrow_token = undefined, // set by caller .payload = null, @@ -2007,15 +2007,15 @@ fn parseSwitchCase(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { fn parseSwitchItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const expr = (try parseExpr(arena, it, tree)) orelse return null; if (eatToken(it, .Ellipsis3)) |token| { - const range_end = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const range_end = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); const node = try arena.create(Node.InfixOp); - node.* = Node.InfixOp{ + node.* = .{ .op_token = token, .lhs = expr, - .op = Node.InfixOp.Op{ .Range = {} }, + .op = .Range, .rhs = range_end, }; return &node.base; @@ -2039,24 +2039,22 @@ fn parseSwitchItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { /// / MINUSPERCENTEQUAL /// / EQUAL fn parseAssignOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { - const Op = Node.InfixOp.Op; - const token = nextToken(it); - const op = switch (token.ptr.id) { - .AsteriskEqual => Op{ .AssignMul = {} }, - .SlashEqual => Op{ .AssignDiv = {} }, - .PercentEqual => Op{ .AssignMod = {} }, - .PlusEqual => Op{ .AssignAdd = {} }, - .MinusEqual => Op{ .AssignSub = {} }, - .AngleBracketAngleBracketLeftEqual => Op{ .AssignBitShiftLeft = {} }, - .AngleBracketAngleBracketRightEqual => Op{ .AssignBitShiftRight = {} }, - .AmpersandEqual => Op{ .AssignBitAnd = {} }, - .CaretEqual => Op{ .AssignBitXor = {} }, - .PipeEqual => Op{ .AssignBitOr = {} }, - .AsteriskPercentEqual => Op{ .AssignMulWrap = {} }, - .PlusPercentEqual => Op{ .AssignAddWrap = {} }, - .MinusPercentEqual => Op{ .AssignSubWrap = {} }, - .Equal => Op{ .Assign = {} }, + const op: Node.InfixOp.Op = switch (token.ptr.id) { + .AsteriskEqual => .AssignMul, + .SlashEqual => .AssignDiv, + .PercentEqual => .AssignMod, + .PlusEqual => .AssignAdd, + .MinusEqual => .AssignSub, + .AngleBracketAngleBracketLeftEqual => .AssignBitShiftLeft, + .AngleBracketAngleBracketRightEqual => .AssignBitShiftRight, + .AmpersandEqual => .AssignBitAnd, + .CaretEqual => .AssignBitXor, + .PipeEqual => .AssignBitOr, + .AsteriskPercentEqual => .AssignMulWrap, + .PlusPercentEqual => .AssignAddWrap, + .MinusPercentEqual => .AssignSubWrap, + .Equal => .Assign, else => { putBackToken(it, token.index); return null; @@ -2064,7 +2062,7 @@ fn parseAssignOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { }; const node = try arena.create(Node.InfixOp); - node.* = Node.InfixOp{ + node.* = .{ .op_token = token.index, .lhs = undefined, // set by caller .op = op, @@ -2081,16 +2079,14 @@ fn parseAssignOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { /// / LARROWEQUAL /// / RARROWEQUAL fn parseCompareOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { - const ops = Node.InfixOp.Op; - const token = nextToken(it); - const op = switch (token.ptr.id) { - .EqualEqual => ops{ .EqualEqual = {} }, - .BangEqual => ops{ .BangEqual = {} }, - .AngleBracketLeft => ops{ .LessThan = {} }, - .AngleBracketRight => ops{ .GreaterThan = {} }, - .AngleBracketLeftEqual => ops{ .LessOrEqual = {} }, - .AngleBracketRightEqual => ops{ .GreaterOrEqual = {} }, + const op: Node.InfixOp.Op = switch (token.ptr.id) { + .EqualEqual => .EqualEqual, + .BangEqual => .BangEqual, + .AngleBracketLeft => .LessThan, + .AngleBracketRight => .GreaterThan, + .AngleBracketLeftEqual => .LessOrEqual, + .AngleBracketRightEqual => .GreaterOrEqual, else => { putBackToken(it, token.index); return null; @@ -2107,15 +2103,13 @@ fn parseCompareOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { /// / KEYWORD_orelse /// / KEYWORD_catch Payload? fn parseBitwiseOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { - const ops = Node.InfixOp.Op; - const token = nextToken(it); - const op = switch (token.ptr.id) { - .Ampersand => ops{ .BitAnd = {} }, - .Caret => ops{ .BitXor = {} }, - .Pipe => ops{ .BitOr = {} }, - .Keyword_orelse => ops{ .UnwrapOptional = {} }, - .Keyword_catch => ops{ .Catch = try parsePayload(arena, it, tree) }, + const op: Node.InfixOp.Op = switch (token.ptr.id) { + .Ampersand => .BitAnd, + .Caret => .BitXor, + .Pipe => .BitOr, + .Keyword_orelse => .UnwrapOptional, + .Keyword_catch => .{ .Catch = try parsePayload(arena, it, tree) }, else => { putBackToken(it, token.index); return null; @@ -2129,12 +2123,10 @@ fn parseBitwiseOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { /// <- LARROW2 /// / RARROW2 fn parseBitShiftOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { - const ops = Node.InfixOp.Op; - const token = nextToken(it); - const op = switch (token.ptr.id) { - .AngleBracketAngleBracketLeft => ops{ .BitShiftLeft = {} }, - .AngleBracketAngleBracketRight => ops{ .BitShiftRight = {} }, + const op: Node.InfixOp.Op = switch (token.ptr.id) { + .AngleBracketAngleBracketLeft => .BitShiftLeft, + .AngleBracketAngleBracketRight => .BitShiftRight, else => { putBackToken(it, token.index); return null; @@ -2151,15 +2143,13 @@ fn parseBitShiftOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { /// / PLUSPERCENT /// / MINUSPERCENT fn parseAdditionOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { - const ops = Node.InfixOp.Op; - const token = nextToken(it); - const op = switch (token.ptr.id) { - .Plus => ops{ .Add = {} }, - .Minus => ops{ .Sub = {} }, - .PlusPlus => ops{ .ArrayCat = {} }, - .PlusPercent => ops{ .AddWrap = {} }, - .MinusPercent => ops{ .SubWrap = {} }, + const op: Node.InfixOp.Op = switch (token.ptr.id) { + .Plus => .Add, + .Minus => .Sub, + .PlusPlus => .ArrayCat, + .PlusPercent => .AddWrap, + .MinusPercent => .SubWrap, else => { putBackToken(it, token.index); return null; @@ -2177,16 +2167,14 @@ fn parseAdditionOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { /// / ASTERISK2 /// / ASTERISKPERCENT fn parseMultiplyOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { - const ops = Node.InfixOp.Op; - const token = nextToken(it); - const op = switch (token.ptr.id) { - .PipePipe => ops{ .BoolOr = {} }, - .Asterisk => ops{ .Mul = {} }, - .Slash => ops{ .Div = {} }, - .Percent => ops{ .Mod = {} }, - .AsteriskAsterisk => ops{ .ArrayMult = {} }, - .AsteriskPercent => ops{ .MulWrap = {} }, + const op: Node.InfixOp.Op = switch (token.ptr.id) { + .PipePipe => .MergeErrorSets, + .Asterisk => .Mul, + .Slash => .Div, + .Percent => .Mod, + .AsteriskAsterisk => .ArrayMult, + .AsteriskPercent => .MulWrap, else => { putBackToken(it, token.index); return null; @@ -2205,17 +2193,15 @@ fn parseMultiplyOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { /// / KEYWORD_try /// / KEYWORD_await fn parsePrefixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { - const ops = Node.PrefixOp.Op; - const token = nextToken(it); - const op = switch (token.ptr.id) { - .Bang => ops{ .BoolNot = {} }, - .Minus => ops{ .Negation = {} }, - .Tilde => ops{ .BitNot = {} }, - .MinusPercent => ops{ .NegationWrap = {} }, - .Ampersand => ops{ .AddressOf = {} }, - .Keyword_try => ops{ .Try = {} }, - .Keyword_await => ops{ .Await = .{} }, + const op: Node.PrefixOp.Op = switch (token.ptr.id) { + .Bang => .BoolNot, + .Minus => .Negation, + .Tilde => .BitNot, + .MinusPercent => .NegationWrap, + .Ampersand => .AddressOf, + .Keyword_try => .Try, + .Keyword_await => .Await, else => { putBackToken(it, token.index); return null; @@ -2223,7 +2209,7 @@ fn parsePrefixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { }; const node = try arena.create(Node.PrefixOp); - node.* = Node.PrefixOp{ + node.* = .{ .op_token = token.index, .op = op, .rhs = undefined, // set by caller @@ -2246,9 +2232,9 @@ fn parsePrefixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { if (eatToken(it, .QuestionMark)) |token| { const node = try arena.create(Node.PrefixOp); - node.* = Node.PrefixOp{ + node.* = .{ .op_token = token, - .op = Node.PrefixOp.Op.OptionalType, + .op = .OptionalType, .rhs = undefined, // set by caller }; return &node.base; @@ -2264,7 +2250,7 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node return null; }; const node = try arena.create(Node.AnyFrameType); - node.* = Node.AnyFrameType{ + node.* = .{ .anyframe_token = token, .result = Node.AnyFrameType.Result{ .arrow_token = arrow, @@ -2286,18 +2272,18 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node while (true) { if (eatToken(it, .Keyword_align)) |align_token| { const lparen = try expectToken(it, tree, .LParen); - const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const expr_node = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); // Optional bit range const bit_range = if (eatToken(it, .Colon)) |_| bit_range_value: { - const range_start = try expectNode(arena, it, tree, parseIntegerLiteral, AstError{ - .ExpectedIntegerLiteral = AstError.ExpectedIntegerLiteral{ .token = it.index }, + const range_start = try expectNode(arena, it, tree, parseIntegerLiteral, .{ + .ExpectedIntegerLiteral = .{ .token = it.index }, }); _ = try expectToken(it, tree, .Colon); - const range_end = try expectNode(arena, it, tree, parseIntegerLiteral, AstError{ - .ExpectedIntegerLiteral = AstError.ExpectedIntegerLiteral{ .token = it.index }, + const range_end = try expectNode(arena, it, tree, parseIntegerLiteral, .{ + .ExpectedIntegerLiteral = .{ .token = it.index }, }); break :bit_range_value Node.PrefixOp.PtrInfo.Align.BitRange{ @@ -2340,8 +2326,8 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node while (true) { if (try parseByteAlign(arena, it, tree)) |align_expr| { if (slice_type.align_info != null) { - try tree.errors.push(AstError{ - .ExtraAlignQualifier = AstError.ExtraAlignQualifier{ .token = it.index }, + try tree.errors.push(.{ + .ExtraAlignQualifier = .{ .token = it.index }, }); return error.ParseError; } @@ -2353,8 +2339,8 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node } if (eatToken(it, .Keyword_const)) |const_token| { if (slice_type.const_token != null) { - try tree.errors.push(AstError{ - .ExtraConstQualifier = AstError.ExtraConstQualifier{ .token = it.index }, + try tree.errors.push(.{ + .ExtraConstQualifier = .{ .token = it.index }, }); return error.ParseError; } @@ -2363,8 +2349,8 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node } if (eatToken(it, .Keyword_volatile)) |volatile_token| { if (slice_type.volatile_token != null) { - try tree.errors.push(AstError{ - .ExtraVolatileQualifier = AstError.ExtraVolatileQualifier{ .token = it.index }, + try tree.errors.push(.{ + .ExtraVolatileQualifier = .{ .token = it.index }, }); return error.ParseError; } @@ -2373,8 +2359,8 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node } if (eatToken(it, .Keyword_allowzero)) |allowzero_token| { if (slice_type.allowzero_token != null) { - try tree.errors.push(AstError{ - .ExtraAllowZeroQualifier = AstError.ExtraAllowZeroQualifier{ .token = it.index }, + try tree.errors.push(.{ + .ExtraAllowZeroQualifier = .{ .token = it.index }, }); return error.ParseError; } @@ -2398,15 +2384,14 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node /// / DOTASTERISK /// / DOTQUESTIONMARK fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { - const Op = Node.SuffixOp.Op; const OpAndToken = struct { op: Node.SuffixOp.Op, token: TokenIndex, }; - const op_and_token = blk: { + const op_and_token: OpAndToken = blk: { if (eatToken(it, .LBracket)) |_| { - const index_expr = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const index_expr = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); if (eatToken(it, .Ellipsis2) != null) { @@ -2415,9 +2400,9 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { try parseExpr(arena, it, tree) else null; - break :blk OpAndToken{ - .op = Op{ - .Slice = Op.Slice{ + break :blk .{ + .op = .{ + .Slice = .{ .start = index_expr, .end = end_expr, .sentinel = sentinel, @@ -2427,14 +2412,14 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { }; } - break :blk OpAndToken{ - .op = Op{ .ArrayAccess = index_expr }, + break :blk .{ + .op = .{ .ArrayAccess = index_expr }, .token = try expectToken(it, tree, .RBracket), }; } if (eatToken(it, .PeriodAsterisk)) |period_asterisk| { - break :blk OpAndToken{ .op = Op{ .Deref = {} }, .token = period_asterisk }; + break :blk .{ .op = .Deref, .token = period_asterisk }; } if (eatToken(it, .Period)) |period| { @@ -2443,19 +2428,19 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { // Should there be an ast.Node.SuffixOp.FieldAccess variant? Or should // this grammar rule be altered? const node = try arena.create(Node.InfixOp); - node.* = Node.InfixOp{ + node.* = .{ .op_token = period, .lhs = undefined, // set by caller - .op = Node.InfixOp.Op.Period, + .op = .Period, .rhs = identifier, }; return &node.base; } if (eatToken(it, .QuestionMark)) |question_mark| { - break :blk OpAndToken{ .op = Op{ .UnwrapOptional = {} }, .token = question_mark }; + break :blk .{ .op = .UnwrapOptional, .token = question_mark }; } - try tree.errors.push(AstError{ - .ExpectedSuffixOp = AstError.ExpectedSuffixOp{ .token = it.index }, + try tree.errors.push(.{ + .ExpectedSuffixOp = .{ .token = it.index }, }); return null; } @@ -2464,7 +2449,7 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { }; const node = try arena.create(Node.SuffixOp); - node.* = Node.SuffixOp{ + node.* = .{ .lhs = undefined, // set by caller .op = op_and_token.op, .rtoken = op_and_token.token, @@ -2491,22 +2476,22 @@ fn parseArrayTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No const lbracket = eatToken(it, .LBracket) orelse return null; const expr = try parseExpr(arena, it, tree); const sentinel = if (eatToken(it, .Colon)) |_| - try expectNode(arena, it, tree, parseExpr, AstError{ + try expectNode(arena, it, tree, parseExpr, .{ .ExpectedExpr = .{ .token = it.index }, }) else null; const rbracket = try expectToken(it, tree, .RBracket); - const op = if (expr) |len_expr| - Node.PrefixOp.Op{ + const op: Node.PrefixOp.Op = if (expr) |len_expr| + .{ .ArrayType = .{ .len_expr = len_expr, .sentinel = sentinel, }, } else - Node.PrefixOp.Op{ + .{ .SliceType = Node.PrefixOp.PtrInfo{ .allowzero_token = null, .align_info = null, @@ -2517,7 +2502,7 @@ fn parseArrayTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No }; const node = try arena.create(Node.PrefixOp); - node.* = Node.PrefixOp{ + node.* = .{ .op_token = lbracket, .op = op, .rhs = undefined, // set by caller @@ -2533,7 +2518,7 @@ fn parseArrayTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No fn parsePtrTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { if (eatToken(it, .Asterisk)) |asterisk| { const sentinel = if (eatToken(it, .Colon)) |_| - try expectNode(arena, it, tree, parseExpr, AstError{ + try expectNode(arena, it, tree, parseExpr, .{ .ExpectedExpr = .{ .token = it.index }, }) else @@ -2549,17 +2534,17 @@ fn parsePtrTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node if (eatToken(it, .AsteriskAsterisk)) |double_asterisk| { const node = try arena.create(Node.PrefixOp); - node.* = Node.PrefixOp{ + node.* = .{ .op_token = double_asterisk, - .op = Node.PrefixOp.Op{ .PtrType = .{} }, + .op = .{ .PtrType = .{} }, .rhs = undefined, // set by caller }; // Special case for **, which is its own token const child = try arena.create(Node.PrefixOp); - child.* = Node.PrefixOp{ + child.* = .{ .op_token = double_asterisk, - .op = Node.PrefixOp.Op{ .PtrType = .{} }, + .op = .{ .PtrType = .{} }, .rhs = undefined, // set by caller }; node.rhs = &child.base; @@ -2586,7 +2571,7 @@ fn parsePtrTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node } } const sentinel = if (eatToken(it, .Colon)) |_| - try expectNode(arena, it, tree, parseExpr, AstError{ + try expectNode(arena, it, tree, parseExpr, .{ .ExpectedExpr = .{ .token = it.index }, }) else @@ -2629,8 +2614,8 @@ fn parseContainerDeclType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !? .Keyword_struct => Node.ContainerDecl.InitArg{ .None = {} }, .Keyword_enum => blk: { if (eatToken(it, .LParen) != null) { - const expr = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const expr = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); _ = try expectToken(it, tree, .RParen); break :blk Node.ContainerDecl.InitArg{ .Type = expr }; @@ -2641,8 +2626,8 @@ fn parseContainerDeclType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !? if (eatToken(it, .LParen) != null) { if (eatToken(it, .Keyword_enum) != null) { if (eatToken(it, .LParen) != null) { - const expr = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const expr = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); _ = try expectToken(it, tree, .RParen); _ = try expectToken(it, tree, .RParen); @@ -2651,8 +2636,8 @@ fn parseContainerDeclType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !? _ = try expectToken(it, tree, .RParen); break :blk Node.ContainerDecl.InitArg{ .Enum = null }; } - const expr = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const expr = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); _ = try expectToken(it, tree, .RParen); break :blk Node.ContainerDecl.InitArg{ .Type = expr }; @@ -2666,7 +2651,7 @@ fn parseContainerDeclType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !? }; const node = try arena.create(Node.ContainerDecl); - node.* = Node.ContainerDecl{ + node.* = .{ .layout_token = null, .kind_token = kind_token.index, .init_arg_expr = init_arg_expr, @@ -2681,8 +2666,8 @@ fn parseContainerDeclType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !? fn parseByteAlign(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { _ = eatToken(it, .Keyword_align) orelse return null; _ = try expectToken(it, tree, .LParen); - const expr = try expectNode(arena, it, tree, parseExpr, AstError{ - .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index }, + const expr = try expectNode(arena, it, tree, parseExpr, .{ + .ExpectedExpr = .{ .token = it.index }, }); _ = try expectToken(it, tree, .RParen); return expr; @@ -2738,7 +2723,7 @@ fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.InfixOp.Op) No pub fn parse(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*Node { const op_token = eatToken(it, token) orelse return null; const node = try arena.create(Node.InfixOp); - node.* = Node.InfixOp{ + node.* = .{ .op_token = op_token, .lhs = undefined, // set by caller .op = op, @@ -2754,13 +2739,13 @@ fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.InfixOp.Op) No fn parseBuiltinCall(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = eatToken(it, .Builtin) orelse return null; const params = (try parseFnCallArguments(arena, it, tree)) orelse { - try tree.errors.push(AstError{ - .ExpectedParamList = AstError.ExpectedParamList{ .token = it.index }, + try tree.errors.push(.{ + .ExpectedParamList = .{ .token = it.index }, }); return error.ParseError; }; const node = try arena.create(Node.BuiltinCall); - node.* = Node.BuiltinCall{ + node.* = .{ .builtin_token = token, .params = params.list, .rparen_token = params.rparen, @@ -2773,7 +2758,7 @@ fn parseErrorTag(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = eatToken(it, .Identifier) orelse return null; const node = try arena.create(Node.ErrorTag); - node.* = Node.ErrorTag{ + node.* = .{ .doc_comments = doc_comments, .name_token = token, }; @@ -2783,7 +2768,7 @@ fn parseErrorTag(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { fn parseIdentifier(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = eatToken(it, .Identifier) orelse return null; const node = try arena.create(Node.Identifier); - node.* = Node.Identifier{ + node.* = .{ .token = token, }; return &node.base; @@ -2792,7 +2777,7 @@ fn parseIdentifier(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { fn parseVarType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = eatToken(it, .Keyword_var) orelse return null; const node = try arena.create(Node.VarType); - node.* = Node.VarType{ + node.* = .{ .token = token, }; return &node.base; @@ -2810,7 +2795,7 @@ fn createLiteral(arena: *Allocator, comptime T: type, token: TokenIndex) !*Node fn parseStringLiteralSingle(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { if (eatToken(it, .StringLiteral)) |token| { const node = try arena.create(Node.StringLiteral); - node.* = Node.StringLiteral{ + node.* = .{ .token = token, }; return &node.base; @@ -2824,7 +2809,7 @@ fn parseStringLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Nod if (eatToken(it, .MultilineStringLiteralLine)) |first_line| { const node = try arena.create(Node.MultilineStringLiteral); - node.* = Node.MultilineStringLiteral{ + node.* = .{ .lines = Node.MultilineStringLiteral.LineList.init(arena), }; try node.lines.push(first_line); @@ -2840,7 +2825,7 @@ fn parseStringLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Nod fn parseIntegerLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = eatToken(it, .IntegerLiteral) orelse return null; const node = try arena.create(Node.IntegerLiteral); - node.* = Node.IntegerLiteral{ + node.* = .{ .token = token, }; return &node.base; @@ -2849,7 +2834,7 @@ fn parseIntegerLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No fn parseFloatLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = eatToken(it, .FloatLiteral) orelse return null; const node = try arena.create(Node.FloatLiteral); - node.* = Node.FloatLiteral{ + node.* = .{ .token = token, }; return &node.base; @@ -2858,9 +2843,9 @@ fn parseFloatLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node fn parseTry(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = eatToken(it, .Keyword_try) orelse return null; const node = try arena.create(Node.PrefixOp); - node.* = Node.PrefixOp{ + node.* = .{ .op_token = token, - .op = Node.PrefixOp.Op.Try, + .op = .Try, .rhs = undefined, // set by caller }; return &node.base; @@ -2869,7 +2854,7 @@ fn parseTry(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { fn parseUse(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { const token = eatToken(it, .Keyword_usingnamespace) orelse return null; const node = try arena.create(Node.Use); - node.* = Node.Use{ + node.* = .{ .doc_comments = null, .visib_token = null, .use_token = token, @@ -2884,17 +2869,17 @@ fn parseIf(arena: *Allocator, it: *TokenIterator, tree: *Tree, bodyParseFn: Node const node = (try parseIfPrefix(arena, it, tree)) orelse return null; const if_prefix = node.cast(Node.If).?; - if_prefix.body = try expectNode(arena, it, tree, bodyParseFn, AstError{ - .InvalidToken = AstError.InvalidToken{ .token = it.index }, + if_prefix.body = try expectNode(arena, it, tree, bodyParseFn, .{ + .InvalidToken = .{ .token = it.index }, }); const else_token = eatToken(it, .Keyword_else) orelse return node; const payload = try parsePayload(arena, it, tree); - const else_expr = try expectNode(arena, it, tree, bodyParseFn, AstError{ - .InvalidToken = AstError.InvalidToken{ .token = it.index }, + const else_expr = try expectNode(arena, it, tree, bodyParseFn, .{ + .InvalidToken = .{ .token = it.index }, }); const else_node = try arena.create(Node.Else); - else_node.* = Node.Else{ + else_node.* = .{ .else_token = else_token, .payload = payload, .body = else_expr, @@ -2914,7 +2899,7 @@ fn parseDocComment(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.D if (lines.len == 0) return null; const node = try arena.create(Node.DocComment); - node.* = Node.DocComment{ + node.* = .{ .lines = lines, }; return node; @@ -2925,7 +2910,7 @@ fn parseAppendedDocComment(arena: *Allocator, it: *TokenIterator, tree: *Tree, a const comment_token = eatToken(it, .DocComment) orelse return null; if (tree.tokensOnSameLine(after_token, comment_token)) { const node = try arena.create(Node.DocComment); - node.* = Node.DocComment{ + node.* = .{ .lines = Node.DocComment.LineList.init(arena), }; try node.lines.push(comment_token); @@ -2974,14 +2959,14 @@ fn parsePrefixOpExpr( switch (rightmost_op.id) { .PrefixOp => { const prefix_op = rightmost_op.cast(Node.PrefixOp).?; - prefix_op.rhs = try expectNode(arena, it, tree, childParseFn, AstError{ - .InvalidToken = AstError.InvalidToken{ .token = it.index }, + prefix_op.rhs = try expectNode(arena, it, tree, childParseFn, .{ + .InvalidToken = .{ .token = it.index }, }); }, .AnyFrameType => { const prom = rightmost_op.cast(Node.AnyFrameType).?; - prom.result.?.return_type = try expectNode(arena, it, tree, childParseFn, AstError{ - .InvalidToken = AstError.InvalidToken{ .token = it.index }, + prom.result.?.return_type = try expectNode(arena, it, tree, childParseFn, .{ + .InvalidToken = .{ .token = it.index }, }); }, else => unreachable, @@ -3010,8 +2995,8 @@ fn parseBinOpExpr( var res = (try childParseFn(arena, it, tree)) orelse return null; while (try opParseFn(arena, it, tree)) |node| { - const right = try expectNode(arena, it, tree, childParseFn, AstError{ - .InvalidToken = AstError.InvalidToken{ .token = it.index }, + const right = try expectNode(arena, it, tree, childParseFn, .{ + .InvalidToken = .{ .token = it.index }, }); const left = res; res = node; @@ -3031,7 +3016,7 @@ fn parseBinOpExpr( fn createInfixOp(arena: *Allocator, index: TokenIndex, op: Node.InfixOp.Op) !*Node { const node = try arena.create(Node.InfixOp); - node.* = Node.InfixOp{ + node.* = .{ .op_token = index, .lhs = undefined, // set by caller .op = op, @@ -3051,8 +3036,8 @@ fn eatAnnotatedToken(it: *TokenIterator, id: Token.Id) ?AnnotatedToken { fn expectToken(it: *TokenIterator, tree: *Tree, id: Token.Id) Error!TokenIndex { const token = nextToken(it); if (token.ptr.id != id) { - try tree.errors.push(AstError{ - .ExpectedToken = AstError.ExpectedToken{ .token = token.index, .expected_id = id }, + try tree.errors.push(.{ + .ExpectedToken = .{ .token = token.index, .expected_id = id }, }); return error.ParseError; } diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index d00568e49f..e1fe07a57c 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -1509,6 +1509,8 @@ test "zig fmt: error set declaration" { \\const Error = error{OutOfMemory}; \\const Error = error{}; \\ + \\const Error = error{ OutOfMemory, OutOfTime }; + \\ ); } diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 1a221bd5b3..23dc9e02ac 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -583,7 +583,6 @@ fn renderExpression( }, .Try, - .Cancel, .Resume, => { try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space); @@ -1269,25 +1268,51 @@ fn renderExpression( } try renderToken(tree, stream, err_set_decl.error_token, indent, start_col, Space.None); // error - try renderToken(tree, stream, lbrace, indent, start_col, Space.Newline); // { - const new_indent = indent + indent_delta; - var it = err_set_decl.decls.iterator(0); - while (it.next()) |node| { - try stream.writeByteNTimes(' ', new_indent); + const src_has_trailing_comma = blk: { + const maybe_comma = tree.prevToken(err_set_decl.rbrace_token); + break :blk tree.tokens.at(maybe_comma).id == .Comma; + }; - if (it.peek()) |next_node| { - try renderExpression(allocator, stream, tree, new_indent, start_col, node.*, Space.None); - try renderToken(tree, stream, tree.nextToken(node.*.lastToken()), new_indent, start_col, Space.Newline); // , + if (src_has_trailing_comma) { + try renderToken(tree, stream, lbrace, indent, start_col, Space.Newline); // { + const new_indent = indent + indent_delta; - try renderExtraNewline(tree, stream, start_col, next_node.*); - } else { - try renderExpression(allocator, stream, tree, new_indent, start_col, node.*, Space.Comma); + var it = err_set_decl.decls.iterator(0); + while (it.next()) |node| { + try stream.writeByteNTimes(' ', new_indent); + + if (it.peek()) |next_node| { + try renderExpression(allocator, stream, tree, new_indent, start_col, node.*, Space.None); + try renderToken(tree, stream, tree.nextToken(node.*.lastToken()), new_indent, start_col, Space.Newline); // , + + try renderExtraNewline(tree, stream, start_col, next_node.*); + } else { + try renderExpression(allocator, stream, tree, new_indent, start_col, node.*, Space.Comma); + } } - } - try stream.writeByteNTimes(' ', indent); - return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // } + try stream.writeByteNTimes(' ', indent); + return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // } + } else { + try renderToken(tree, stream, lbrace, indent, start_col, Space.Space); // { + + var it = err_set_decl.decls.iterator(0); + while (it.next()) |node| { + if (it.peek()) |next_node| { + try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.None); + + const comma_token = tree.nextToken(node.*.lastToken()); + assert(tree.tokens.at(comma_token).id == .Comma); + try renderToken(tree, stream, comma_token, indent, start_col, Space.Space); // , + try renderExtraNewline(tree, stream, start_col, next_node.*); + } else { + try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.Space); + } + } + + return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // } + } }, .ErrorTag => { @@ -1590,8 +1615,7 @@ fn renderExpression( } } else { var it = switch_case.items.iterator(0); - while (true) { - const node = it.next().?; + while (it.next()) |node| { if (it.peek()) |next_node| { try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.None); @@ -1602,7 +1626,6 @@ fn renderExpression( } else { try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.Comma); try stream.writeByteNTimes(' ', indent); - break; } } } diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 88b3022bb2..5c47b68838 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -754,7 +754,7 @@ pub const NativeTargetInfo = struct { const rpath_list = mem.toSliceConst(u8, @ptrCast([*:0]u8, strtab[rpoff..].ptr)); var it = mem.tokenize(rpath_list, ":"); while (it.next()) |rpath| { - var dir = fs.cwd().openDirList(rpath) catch |err| switch (err) { + var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) { error.NameTooLong => unreachable, error.InvalidUtf8 => unreachable, error.BadPathName => unreachable, diff --git a/src-self-hosted/c_int.zig b/src-self-hosted/c_int.zig index 1ee27c7596..e61a5bf657 100644 --- a/src-self-hosted/c_int.zig +++ b/src-self-hosted/c_int.zig @@ -69,9 +69,9 @@ pub const CInt = struct { }; pub fn sizeInBits(cint: CInt, self: Target) u32 { - const arch = self.getArch(); + const arch = self.cpu.arch; switch (self.os.tag) { - .freestanding, .other => switch (self.getArch()) { + .freestanding, .other => switch (self.cpu.arch) { .msp430 => switch (cint.id) { .Short, .UShort, @@ -94,7 +94,7 @@ pub const CInt = struct { => return 32, .Long, .ULong, - => return self.getArchPtrBitWidth(), + => return self.cpu.arch.ptrBitWidth(), .LongLong, .ULongLong, => return 64, @@ -114,7 +114,7 @@ pub const CInt = struct { => return 32, .Long, .ULong, - => return self.getArchPtrBitWidth(), + => return self.cpu.arch.ptrBitWidth(), .LongLong, .ULongLong, => return 64, diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 73b4acd6cc..03e71c102d 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -95,7 +95,7 @@ pub const ZigCompiler = struct { pub fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation { if (self.native_libc.start()) |ptr| return ptr; - try self.native_libc.data.findNative(self.allocator); + self.native_libc.data = try LibCInstallation.findNative(.{ .allocator = self.allocator }); self.native_libc.resolve(); return &self.native_libc.data; } @@ -126,7 +126,7 @@ pub const Compilation = struct { name: Buffer, llvm_triple: Buffer, root_src_path: ?[]const u8, - target: Target, + target: std.Target, llvm_target: *llvm.Target, build_mode: builtin.Mode, zig_lib_dir: []const u8, @@ -338,7 +338,7 @@ pub const Compilation = struct { zig_compiler: *ZigCompiler, name: []const u8, root_src_path: ?[]const u8, - target: Target, + target: std.zig.CrossTarget, kind: Kind, build_mode: builtin.Mode, is_static: bool, @@ -370,13 +370,18 @@ pub const Compilation = struct { zig_compiler: *ZigCompiler, name: []const u8, root_src_path: ?[]const u8, - target: Target, + cross_target: std.zig.CrossTarget, kind: Kind, build_mode: builtin.Mode, is_static: bool, zig_lib_dir: []const u8, ) !void { const allocator = zig_compiler.allocator; + + // TODO merge this line with stage2.zig crossTargetToTarget + const target_info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target); + const target = target_info.target; + var comp = Compilation{ .arena_allocator = std.heap.ArenaAllocator.init(allocator), .zig_compiler = zig_compiler, @@ -419,7 +424,7 @@ pub const Compilation = struct { .target_machine = undefined, .target_data_ref = undefined, .target_layout_str = undefined, - .target_ptr_bits = target.getArchPtrBitWidth(), + .target_ptr_bits = target.cpu.arch.ptrBitWidth(), .root_package = undefined, .std_package = undefined, @@ -440,7 +445,7 @@ pub const Compilation = struct { } comp.name = try Buffer.init(comp.arena(), name); - comp.llvm_triple = try util.getTriple(comp.arena(), target); + comp.llvm_triple = try util.getLLVMTriple(comp.arena(), target); comp.llvm_target = try util.llvmTargetFromTriple(comp.llvm_triple); comp.zig_std_dir = try fs.path.join(comp.arena(), &[_][]const u8{ zig_lib_dir, "std" }); @@ -455,10 +460,8 @@ pub const Compilation = struct { var target_specific_cpu_features: ?[*:0]u8 = null; defer llvm.DisposeMessage(target_specific_cpu_args); defer llvm.DisposeMessage(target_specific_cpu_features); - if (target == Target.Native) { - target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory; - target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory; - } + + // TODO detect native CPU & features here comp.target_machine = llvm.CreateTargetMachine( comp.llvm_target, @@ -517,8 +520,7 @@ pub const Compilation = struct { if (comp.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| { - // TODO evented I/O? - fs.deleteTree(tmp_dir) catch {}; + fs.cwd().deleteTree(tmp_dir) catch {}; } else |_| {}; } @@ -1122,7 +1124,9 @@ pub const Compilation = struct { self.libc_link_lib = link_lib; // get a head start on looking for the native libc - if (self.target == Target.Native and self.override_libc == null) { + // TODO this is missing a bunch of logic related to whether the target is native + // and whether we can build libc + if (self.override_libc == null) { try self.deinit_group.call(startFindingNativeLibC, .{self}); } } diff --git a/src-self-hosted/errmsg.zig b/src-self-hosted/errmsg.zig index c1ebf0058e..606c8c4b3a 100644 --- a/src-self-hosted/errmsg.zig +++ b/src-self-hosted/errmsg.zig @@ -164,8 +164,7 @@ pub const Msg = struct { const realpath_copy = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath); errdefer comp.gpa().free(realpath_copy); - var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; - try parse_error.render(&tree_scope.tree.tokens, out_stream); + try parse_error.render(&tree_scope.tree.tokens, text_buf.outStream()); const msg = try comp.gpa().create(Msg); msg.* = Msg{ @@ -204,8 +203,7 @@ pub const Msg = struct { const realpath_copy = try mem.dupe(allocator, u8, realpath); errdefer allocator.free(realpath_copy); - var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; - try parse_error.render(&tree.tokens, out_stream); + try parse_error.render(&tree.tokens, text_buf.outStream()); const msg = try allocator.create(Msg); msg.* = Msg{ @@ -272,7 +270,7 @@ pub const Msg = struct { }); try stream.writeByteNTimes(' ', start_loc.column); try stream.writeByteNTimes('~', last_token.end - first_token.start); - try stream.write("\n"); + try stream.writeAll("\n"); } pub fn printToFile(msg: *const Msg, file: fs.File, color: Color) !void { @@ -281,7 +279,6 @@ pub const Msg = struct { .On => true, .Off => false, }; - var stream = &file.outStream().stream; - return msg.printToStream(stream, color_on); + return msg.printToStream(file.outStream(), color_on); } }; diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 2e65962d41..7453f6fbd7 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -1099,7 +1099,6 @@ pub const Builder = struct { .Await => return error.Unimplemented, .BitNot => return error.Unimplemented, .BoolNot => return error.Unimplemented, - .Cancel => return error.Unimplemented, .OptionalType => return error.Unimplemented, .Negation => return error.Unimplemented, .NegationWrap => return error.Unimplemented, @@ -1188,6 +1187,7 @@ pub const Builder = struct { .ParamDecl => return error.Unimplemented, .FieldInitializer => return error.Unimplemented, .EnumLiteral => return error.Unimplemented, + .Noasync => return error.Unimplemented, } } diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index c07ef03b51..996f705060 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -280,7 +280,7 @@ pub const LibCInstallation = struct { // search in reverse order const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1); const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " "); - var search_dir = fs.cwd().openDirList(search_path) catch |err| switch (err) { + var search_dir = fs.cwd().openDir(search_path, .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.NoDevice, @@ -335,7 +335,7 @@ pub const LibCInstallation = struct { const stream = result_buf.outStream(); try stream.print("{}\\Include\\{}\\ucrt", .{ search.path, search.version }); - var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) { + var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.NoDevice, @@ -382,7 +382,7 @@ pub const LibCInstallation = struct { const stream = result_buf.outStream(); try stream.print("{}\\Lib\\{}\\ucrt\\{}", .{ search.path, search.version, arch_sub_dir }); - var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) { + var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.NoDevice, @@ -437,7 +437,7 @@ pub const LibCInstallation = struct { const stream = result_buf.outStream(); try stream.print("{}\\Lib\\{}\\um\\{}", .{ search.path, search.version, arch_sub_dir }); - var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) { + var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.NoDevice, @@ -475,7 +475,7 @@ pub const LibCInstallation = struct { try result_buf.append("\\include"); - var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) { + var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.NoDevice, diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 1efa15574a..e67b307097 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -56,12 +56,13 @@ pub fn link(comp: *Compilation) !void { if (comp.haveLibC()) { // TODO https://github.com/ziglang/zig/issues/3190 var libc = ctx.comp.override_libc orelse blk: { - switch (comp.target) { - Target.Native => { - break :blk comp.zig_compiler.getNativeLibC() catch return error.LibCRequiredButNotProvidedOrFound; - }, - else => return error.LibCRequiredButNotProvidedOrFound, - } + @panic("this code has bitrotted"); + //switch (comp.target) { + // Target.Native => { + // break :blk comp.zig_compiler.getNativeLibC() catch return error.LibCRequiredButNotProvidedOrFound; + // }, + // else => return error.LibCRequiredButNotProvidedOrFound, + //} }; ctx.libc = libc; } @@ -155,11 +156,11 @@ fn constructLinkerArgsElf(ctx: *Context) !void { //bool shared = !g->is_static && is_lib; //Buf *soname = nullptr; if (ctx.comp.is_static) { - if (util.isArmOrThumb(ctx.comp.target)) { - try ctx.args.append("-Bstatic"); - } else { - try ctx.args.append("-static"); - } + //if (util.isArmOrThumb(ctx.comp.target)) { + // try ctx.args.append("-Bstatic"); + //} else { + // try ctx.args.append("-static"); + //} } //} else if (shared) { // lj->args.append("-shared"); @@ -176,29 +177,24 @@ fn constructLinkerArgsElf(ctx: *Context) !void { if (ctx.link_in_crt) { const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o"; - const crtbegino = if (ctx.comp.is_static) "crtbeginT.o" else "crtbegin.o"; - try addPathJoin(ctx, ctx.libc.lib_dir.?, crt1o); - try addPathJoin(ctx, ctx.libc.lib_dir.?, "crti.o"); - try addPathJoin(ctx, ctx.libc.static_lib_dir.?, crtbegino); + try addPathJoin(ctx, ctx.libc.crt_dir.?, crt1o); + try addPathJoin(ctx, ctx.libc.crt_dir.?, "crti.o"); } if (ctx.comp.haveLibC()) { try ctx.args.append("-L"); // TODO addNullByte should probably return [:0]u8 - try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr)); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.crt_dir.?)).ptr)); - try ctx.args.append("-L"); - try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr)); - - if (!ctx.comp.is_static) { - const dl = blk: { - if (ctx.libc.dynamic_linker_path) |dl| break :blk dl; - if (util.getDynamicLinkerPath(ctx.comp.target)) |dl| break :blk dl; - return error.LibCMissingDynamicLinker; - }; - try ctx.args.append("-dynamic-linker"); - try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr)); - } + //if (!ctx.comp.is_static) { + // const dl = blk: { + // //if (ctx.libc.dynamic_linker_path) |dl| break :blk dl; + // //if (util.getDynamicLinkerPath(ctx.comp.target)) |dl| break :blk dl; + // return error.LibCMissingDynamicLinker; + // }; + // try ctx.args.append("-dynamic-linker"); + // try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr)); + //} } //if (shared) { @@ -265,13 +261,12 @@ fn constructLinkerArgsElf(ctx: *Context) !void { // crt end if (ctx.link_in_crt) { - try addPathJoin(ctx, ctx.libc.static_lib_dir.?, "crtend.o"); - try addPathJoin(ctx, ctx.libc.lib_dir.?, "crtn.o"); + try addPathJoin(ctx, ctx.libc.crt_dir.?, "crtn.o"); } - if (ctx.comp.target != Target.Native) { - try ctx.args.append("--allow-shlib-undefined"); - } + //if (ctx.comp.target != Target.Native) { + // try ctx.args.append("--allow-shlib-undefined"); + //} } fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void { @@ -287,7 +282,7 @@ fn constructLinkerArgsCoff(ctx: *Context) !void { try ctx.args.append("-DEBUG"); } - switch (ctx.comp.target.getArch()) { + switch (ctx.comp.target.cpu.arch) { .i386 => try ctx.args.append("-MACHINE:X86"), .x86_64 => try ctx.args.append("-MACHINE:X64"), .aarch64 => try ctx.args.append("-MACHINE:ARM"), @@ -302,7 +297,7 @@ fn constructLinkerArgsCoff(ctx: *Context) !void { if (ctx.comp.haveLibC()) { try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.msvc_lib_dir.?})).ptr)); try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.kernel32_lib_dir.?})).ptr)); - try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.lib_dir.?})).ptr)); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.crt_dir.?})).ptr)); } if (ctx.link_in_crt) { @@ -417,7 +412,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void { } }, .IPhoneOS => { - if (ctx.comp.target.getArch() == .aarch64) { + if (ctx.comp.target.cpu.arch == .aarch64) { // iOS does not need any crt1 files for arm64 } else if (platform.versionLessThan(3, 1)) { try ctx.args.append("-lcrt1.o"); @@ -435,28 +430,29 @@ fn constructLinkerArgsMachO(ctx: *Context) !void { } try addFnObjects(ctx); - if (ctx.comp.target == Target.Native) { - for (ctx.comp.link_libs_list.toSliceConst()) |lib| { - if (mem.eql(u8, lib.name, "c")) { - // on Darwin, libSystem has libc in it, but also you have to use it - // to make syscalls because the syscall numbers are not documented - // and change between versions. - // so we always link against libSystem - try ctx.args.append("-lSystem"); - } else { - if (mem.indexOfScalar(u8, lib.name, '/') == null) { - const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", .{lib.name}); - try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr)); - } else { - const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name); - try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr)); - } - } - } - } else { - try ctx.args.append("-undefined"); - try ctx.args.append("dynamic_lookup"); - } + // TODO + //if (ctx.comp.target == Target.Native) { + // for (ctx.comp.link_libs_list.toSliceConst()) |lib| { + // if (mem.eql(u8, lib.name, "c")) { + // // on Darwin, libSystem has libc in it, but also you have to use it + // // to make syscalls because the syscall numbers are not documented + // // and change between versions. + // // so we always link against libSystem + // try ctx.args.append("-lSystem"); + // } else { + // if (mem.indexOfScalar(u8, lib.name, '/') == null) { + // const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", .{lib.name}); + // try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr)); + // } else { + // const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name); + // try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr)); + // } + // } + // } + //} else { + // try ctx.args.append("-undefined"); + // try ctx.args.append("dynamic_lookup"); + //} if (platform.kind == .MacOS) { if (platform.versionLessThan(10, 5)) { diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 20e39bd2a1..f5faa26615 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -18,10 +18,6 @@ const Target = std.Target; const errmsg = @import("errmsg.zig"); const LibCInstallation = @import("libc_installation.zig").LibCInstallation; -var stderr_file: fs.File = undefined; -var stderr: *io.OutStream(fs.File.WriteError) = undefined; -var stdout: *io.OutStream(fs.File.WriteError) = undefined; - pub const io_mode = .evented; pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB @@ -51,17 +47,14 @@ const Command = struct { pub fn main() !void { const allocator = std.heap.c_allocator; - stdout = &std.io.getStdOut().outStream().stream; - - stderr_file = std.io.getStdErr(); - stderr = &stderr_file.outStream().stream; + const stderr = io.getStdErr().outStream(); const args = try process.argsAlloc(allocator); defer process.argsFree(allocator, args); if (args.len <= 1) { - try stderr.write("expected command argument\n\n"); - try stderr.write(usage); + try stderr.writeAll("expected command argument\n\n"); + try stderr.writeAll(usage); process.exit(1); } @@ -78,8 +71,8 @@ pub fn main() !void { } else if (mem.eql(u8, cmd, "libc")) { return cmdLibC(allocator, cmd_args); } else if (mem.eql(u8, cmd, "targets")) { - const info = try std.zig.system.NativeTargetInfo.detect(allocator); - defer info.deinit(allocator); + const info = try std.zig.system.NativeTargetInfo.detect(allocator, .{}); + const stdout = io.getStdOut().outStream(); return @import("print_targets.zig").cmdTargets(allocator, cmd_args, stdout, info.target); } else if (mem.eql(u8, cmd, "version")) { return cmdVersion(allocator, cmd_args); @@ -91,7 +84,7 @@ pub fn main() !void { return cmdInternal(allocator, cmd_args); } else { try stderr.print("unknown command: {}\n\n", .{args[1]}); - try stderr.write(usage); + try stderr.writeAll(usage); process.exit(1); } } @@ -156,6 +149,8 @@ const usage_build_generic = ; fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Compilation.Kind) !void { + const stderr = io.getStdErr().outStream(); + var color: errmsg.Color = .Auto; var build_mode: std.builtin.Mode = .Debug; var emit_bin = true; @@ -208,11 +203,11 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co const arg = args[i]; if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "--help")) { - try stdout.write(usage_build_generic); + try io.getStdOut().writeAll(usage_build_generic); process.exit(0); } else if (mem.eql(u8, arg, "--color")) { if (i + 1 >= args.len) { - try stderr.write("expected [auto|on|off] after --color\n"); + try stderr.writeAll("expected [auto|on|off] after --color\n"); process.exit(1); } i += 1; @@ -229,7 +224,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co } } else if (mem.eql(u8, arg, "--mode")) { if (i + 1 >= args.len) { - try stderr.write("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n"); + try stderr.writeAll("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n"); process.exit(1); } i += 1; @@ -248,49 +243,49 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co } } else if (mem.eql(u8, arg, "--name")) { if (i + 1 >= args.len) { - try stderr.write("expected parameter after --name\n"); + try stderr.writeAll("expected parameter after --name\n"); process.exit(1); } i += 1; provided_name = args[i]; } else if (mem.eql(u8, arg, "--ver-major")) { if (i + 1 >= args.len) { - try stderr.write("expected parameter after --ver-major\n"); + try stderr.writeAll("expected parameter after --ver-major\n"); process.exit(1); } i += 1; version.major = try std.fmt.parseInt(u32, args[i], 10); } else if (mem.eql(u8, arg, "--ver-minor")) { if (i + 1 >= args.len) { - try stderr.write("expected parameter after --ver-minor\n"); + try stderr.writeAll("expected parameter after --ver-minor\n"); process.exit(1); } i += 1; version.minor = try std.fmt.parseInt(u32, args[i], 10); } else if (mem.eql(u8, arg, "--ver-patch")) { if (i + 1 >= args.len) { - try stderr.write("expected parameter after --ver-patch\n"); + try stderr.writeAll("expected parameter after --ver-patch\n"); process.exit(1); } i += 1; version.patch = try std.fmt.parseInt(u32, args[i], 10); } else if (mem.eql(u8, arg, "--linker-script")) { if (i + 1 >= args.len) { - try stderr.write("expected parameter after --linker-script\n"); + try stderr.writeAll("expected parameter after --linker-script\n"); process.exit(1); } i += 1; linker_script = args[i]; } else if (mem.eql(u8, arg, "--libc")) { if (i + 1 >= args.len) { - try stderr.write("expected parameter after --libc\n"); + try stderr.writeAll("expected parameter after --libc\n"); process.exit(1); } i += 1; libc_arg = args[i]; } else if (mem.eql(u8, arg, "-mllvm")) { if (i + 1 >= args.len) { - try stderr.write("expected parameter after -mllvm\n"); + try stderr.writeAll("expected parameter after -mllvm\n"); process.exit(1); } i += 1; @@ -300,14 +295,14 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co try mllvm_flags.append(args[i]); } else if (mem.eql(u8, arg, "-mmacosx-version-min")) { if (i + 1 >= args.len) { - try stderr.write("expected parameter after -mmacosx-version-min\n"); + try stderr.writeAll("expected parameter after -mmacosx-version-min\n"); process.exit(1); } i += 1; macosx_version_min = args[i]; } else if (mem.eql(u8, arg, "-mios-version-min")) { if (i + 1 >= args.len) { - try stderr.write("expected parameter after -mios-version-min\n"); + try stderr.writeAll("expected parameter after -mios-version-min\n"); process.exit(1); } i += 1; @@ -348,7 +343,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co linker_rdynamic = true; } else if (mem.eql(u8, arg, "--pkg-begin")) { if (i + 2 >= args.len) { - try stderr.write("expected [name] [path] after --pkg-begin\n"); + try stderr.writeAll("expected [name] [path] after --pkg-begin\n"); process.exit(1); } i += 1; @@ -363,7 +358,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co if (cur_pkg.parent) |parent| { cur_pkg = parent; } else { - try stderr.write("encountered --pkg-end with no matching --pkg-begin\n"); + try stderr.writeAll("encountered --pkg-end with no matching --pkg-begin\n"); process.exit(1); } } else if (mem.startsWith(u8, arg, "-l")) { @@ -411,18 +406,18 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co var it = mem.separate(basename, "."); break :blk it.next() orelse basename; } else { - try stderr.write("--name [name] not provided and unable to infer\n"); + try stderr.writeAll("--name [name] not provided and unable to infer\n"); process.exit(1); } }; if (root_src_file == null and link_objects.len == 0 and assembly_files.len == 0) { - try stderr.write("Expected source file argument or at least one --object or --assembly argument\n"); + try stderr.writeAll("Expected source file argument or at least one --object or --assembly argument\n"); process.exit(1); } if (out_type == Compilation.Kind.Obj and link_objects.len != 0) { - try stderr.write("When building an object file, --object arguments are invalid\n"); + try stderr.writeAll("When building an object file, --object arguments are invalid\n"); process.exit(1); } @@ -440,7 +435,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co &zig_compiler, root_name, root_src_file, - Target.Native, + .{}, out_type, build_mode, !is_dynamic, @@ -478,7 +473,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co comp.linker_rdynamic = linker_rdynamic; if (macosx_version_min != null and ios_version_min != null) { - try stderr.write("-mmacosx-version-min and -mios-version-min options not allowed together\n"); + try stderr.writeAll("-mmacosx-version-min and -mios-version-min options not allowed together\n"); process.exit(1); } @@ -501,6 +496,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co } fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { + const stderr_file = io.getStdErr(); + const stderr = stderr_file.outStream(); var count: usize = 0; while (!comp.cancelled) { const build_event = comp.events.get(); @@ -551,7 +548,8 @@ const Fmt = struct { }; fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void { - libc.parse(allocator, libc_paths_file, stderr) catch |err| { + const stderr = io.getStdErr().outStream(); + libc.* = LibCInstallation.parse(allocator, libc_paths_file, stderr) catch |err| { stderr.print("Unable to parse libc path file '{}': {}.\n" ++ "Try running `zig libc` to see an example for the native target.\n", .{ libc_paths_file, @@ -562,6 +560,7 @@ fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_fil } fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void { + const stderr = io.getStdErr().outStream(); switch (args.len) { 0 => {}, 1 => { @@ -582,10 +581,12 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void { stderr.print("unable to find libc: {}\n", .{@errorName(err)}) catch {}; process.exit(1); }; - libc.render(stdout) catch process.exit(1); + libc.render(io.getStdOut().outStream()) catch process.exit(1); } fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { + const stderr_file = io.getStdErr(); + const stderr = stderr_file.outStream(); var color: errmsg.Color = .Auto; var stdin_flag: bool = false; var check_flag: bool = false; @@ -597,11 +598,12 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { const arg = args[i]; if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "--help")) { - try stdout.write(usage_fmt); + const stdout = io.getStdOut().outStream(); + try stdout.writeAll(usage_fmt); process.exit(0); } else if (mem.eql(u8, arg, "--color")) { if (i + 1 >= args.len) { - try stderr.write("expected [auto|on|off] after --color\n"); + try stderr.writeAll("expected [auto|on|off] after --color\n"); process.exit(1); } i += 1; @@ -632,14 +634,13 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { if (stdin_flag) { if (input_files.len != 0) { - try stderr.write("cannot use --stdin with positional arguments\n"); + try stderr.writeAll("cannot use --stdin with positional arguments\n"); process.exit(1); } - var stdin_file = io.getStdIn(); - var stdin = stdin_file.inStream(); + const stdin = io.getStdIn().inStream(); - const source_code = try stdin.stream.readAllAlloc(allocator, max_src_size); + const source_code = try stdin.readAllAlloc(allocator, max_src_size); defer allocator.free(source_code); const tree = std.zig.parse(allocator, source_code) catch |err| { @@ -653,7 +654,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, tree, ""); defer msg.destroy(); - try msg.printToFile(stderr_file, color); + try msg.printToFile(io.getStdErr(), color); } if (tree.errors.len != 0) { process.exit(1); @@ -664,12 +665,13 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { process.exit(code); } + const stdout = io.getStdOut().outStream(); _ = try std.zig.render(allocator, stdout, tree); return; } if (input_files.len == 0) { - try stderr.write("expected at least one source file argument\n"); + try stderr.writeAll("expected at least one source file argument\n"); process.exit(1); } @@ -713,6 +715,9 @@ const FmtError = error{ } || fs.File.OpenError; async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void { + const stderr_file = io.getStdErr(); + const stderr = stderr_file.outStream(); + const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref); defer fmt.allocator.free(file_path); @@ -729,7 +734,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro max_src_size, ) catch |err| switch (err) { error.IsDir, error.AccessDenied => { - var dir = try fs.cwd().openDirList(file_path); + var dir = try fs.cwd().openDir(file_path, .{ .iterate = true }); defer dir.close(); var group = event.Group(FmtError!void).init(fmt.allocator); @@ -791,11 +796,13 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro } fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void { + const stdout = io.getStdOut().outStream(); try stdout.print("{}\n", .{c.ZIG_VERSION_STRING}); } fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void { - try stdout.write(usage); + const stdout = io.getStdOut(); + try stdout.writeAll(usage); } pub const info_zen = @@ -816,7 +823,7 @@ pub const info_zen = ; fn cmdZen(allocator: *Allocator, args: []const []const u8) !void { - try stdout.write(info_zen); + try io.getStdOut().writeAll(info_zen); } const usage_internal = @@ -829,8 +836,9 @@ const usage_internal = ; fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void { + const stderr = io.getStdErr().outStream(); if (args.len == 0) { - try stderr.write(usage_internal); + try stderr.writeAll(usage_internal); process.exit(1); } @@ -849,10 +857,11 @@ fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void { } try stderr.print("unknown sub command: {}\n\n", .{args[0]}); - try stderr.write(usage_internal); + try stderr.writeAll(usage_internal); } fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void { + const stdout = io.getStdOut().outStream(); try stdout.print( \\ZIG_CMAKE_BINARY_DIR {} \\ZIG_CXX_COMPILER {} diff --git a/src-self-hosted/print_targets.zig b/src-self-hosted/print_targets.zig index ad506425d2..997cfcfddc 100644 --- a/src-self-hosted/print_targets.zig +++ b/src-self-hosted/print_targets.zig @@ -72,7 +72,7 @@ pub fn cmdTargets( }; defer allocator.free(zig_lib_dir); - var dir = try std.fs.cwd().openDirList(zig_lib_dir); + var dir = try std.fs.cwd().openDir(zig_lib_dir, .{}); defer dir.close(); const vers_txt = try dir.readFileAlloc(allocator, "libc/glibc/vers.txt", 10 * 1024); diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 38dfbaf072..a1d741bc9b 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -128,7 +128,7 @@ export fn stage2_translate_c( args_end: [*]?[*]const u8, resources_path: [*:0]const u8, ) Error { - var errors = @as([*]translate_c.ClangErrMsg, undefined)[0..0]; + var errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{}; out_ast.* = translate_c.translate(std.heap.c_allocator, args_begin, args_end, &errors, resources_path) catch |err| switch (err) { error.SemanticAnalyzeFail => { out_errors_ptr.* = errors.ptr; @@ -319,7 +319,7 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void { const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) { error.IsDir, error.AccessDenied => { // TODO make event based (and dir.next()) - var dir = try fs.cwd().openDirList(file_path); + var dir = try fs.cwd().openDir(file_path, .{ .iterate = true }); defer dir.close(); var dir_it = dir.iterate(); diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index e87164c9fb..b146d6607a 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -57,11 +57,11 @@ pub const TestContext = struct { errdefer allocator.free(self.zig_lib_dir); try std.fs.cwd().makePath(tmp_dir_name); - errdefer std.fs.deleteTree(tmp_dir_name) catch {}; + errdefer std.fs.cwd().deleteTree(tmp_dir_name) catch {}; } fn deinit(self: *TestContext) void { - std.fs.deleteTree(tmp_dir_name) catch {}; + std.fs.cwd().deleteTree(tmp_dir_name) catch {}; allocator.free(self.zig_lib_dir); self.zig_compiler.deinit(); } diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 51dcb8ff97..3235ac90c0 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -1744,20 +1744,18 @@ fn writeEscapedString(buf: []u8, s: []const u8) void { // Returns either a string literal or a slice of `buf`. fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 { return switch (c) { - '\"' => "\\\""[0..], - '\'' => "\\'"[0..], - '\\' => "\\\\"[0..], - '\n' => "\\n"[0..], - '\r' => "\\r"[0..], - '\t' => "\\t"[0..], - else => { - // Handle the remaining escapes Zig doesn't support by turning them - // into their respective hex representation - if (std.ascii.isCntrl(c)) - return std.fmt.bufPrint(char_buf[0..], "\\x{x:0<2}", .{c}) catch unreachable - else - return std.fmt.bufPrint(char_buf[0..], "{c}", .{c}) catch unreachable; - }, + '\"' => "\\\"", + '\'' => "\\'", + '\\' => "\\\\", + '\n' => "\\n", + '\r' => "\\r", + '\t' => "\\t", + // Handle the remaining escapes Zig doesn't support by turning them + // into their respective hex representation + else => if (std.ascii.isCntrl(c)) + std.fmt.bufPrint(char_buf, "\\x{x:0<2}", .{c}) catch unreachable + else + std.fmt.bufPrint(char_buf, "{c}", .{c}) catch unreachable, }; } diff --git a/src-self-hosted/util.zig b/src-self-hosted/util.zig index 2a7bf4d9cc..ec68823ebd 100644 --- a/src-self-hosted/util.zig +++ b/src-self-hosted/util.zig @@ -3,8 +3,7 @@ const Target = std.Target; const llvm = @import("llvm.zig"); pub fn getDarwinArchString(self: Target) [:0]const u8 { - const arch = self.getArch(); - switch (arch) { + switch (self.cpu.arch) { .aarch64 => return "arm64", .thumb, .arm, @@ -34,3 +33,15 @@ pub fn initializeAllTargets() void { llvm.InitializeAllAsmPrinters(); llvm.InitializeAllAsmParsers(); } + +pub fn getLLVMTriple(allocator: *std.mem.Allocator, target: std.Target) !std.Buffer { + var result = try std.Buffer.initSize(allocator, 0); + errdefer result.deinit(); + + try result.outStream().print( + "{}-unknown-{}-{}", + .{ @tagName(target.cpu.arch), @tagName(target.os.tag), @tagName(target.abi) }, + ); + + return result; +} diff --git a/src/all_types.hpp b/src/all_types.hpp index e58ecf3dbb..43d62a60ff 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -231,6 +231,7 @@ enum ConstPtrSpecial { // The pointer is a reference to a single object. ConstPtrSpecialRef, // The pointer points to an element in an underlying array. + // Not to be confused with ConstPtrSpecialSubArray. ConstPtrSpecialBaseArray, // The pointer points to a field in an underlying struct. ConstPtrSpecialBaseStruct, @@ -257,6 +258,10 @@ enum ConstPtrSpecial { // types to be the same, so all optionals of pointer types use x_ptr // instead of x_optional. ConstPtrSpecialNull, + // The pointer points to a sub-array (not an individual element). + // Not to be confused with ConstPtrSpecialBaseArray. However, it uses the same + // union payload struct (base_array). + ConstPtrSpecialSubArray, }; enum ConstPtrMut { @@ -3705,6 +3710,7 @@ struct IrInstGenSlice { IrInstGen *start; IrInstGen *end; IrInstGen *result_loc; + ZigValue *sentinel; bool safety_check_on; }; diff --git a/src/analyze.cpp b/src/analyze.cpp index 33d28269b9..77d3f33331 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -780,6 +780,8 @@ ZigType *get_error_union_type(CodeGen *g, ZigType *err_set_type, ZigType *payloa } ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, ZigValue *sentinel) { + Error err; + TypeId type_id = {}; type_id.id = ZigTypeIdArray; type_id.data.array.codegen = g; @@ -791,7 +793,11 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, Zi return existing_entry->value; } - assert(type_is_resolved(child_type, ResolveStatusSizeKnown)); + size_t full_array_size = array_size + ((sentinel != nullptr) ? 1 : 0); + + if (full_array_size != 0 && (err = type_resolve(g, child_type, ResolveStatusSizeKnown))) { + codegen_report_errors_and_exit(g); + } ZigType *entry = new_type_table_entry(ZigTypeIdArray); @@ -803,15 +809,8 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, Zi } buf_appendf(&entry->name, "]%s", buf_ptr(&child_type->name)); - size_t full_array_size; - if (array_size == 0) { - full_array_size = 0; - } else { - full_array_size = array_size + ((sentinel != nullptr) ? 1 : 0); - } - entry->size_in_bits = child_type->size_in_bits * full_array_size; - entry->abi_align = child_type->abi_align; + entry->abi_align = (full_array_size == 0) ? 0 : child_type->abi_align; entry->abi_size = child_type->abi_size * full_array_size; entry->data.array.child_type = child_type; @@ -1197,7 +1196,8 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent LazyValueArrayType *lazy_array_type = reinterpret_cast(type_val->data.x_lazy); - if (lazy_array_type->length < 1) { + // The sentinel counts as an extra element + if (lazy_array_type->length == 0 && lazy_array_type->sentinel == nullptr) { *is_zero_bits = true; return ErrorNone; } @@ -1452,7 +1452,7 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV case LazyValueIdArrayType: { LazyValueArrayType *lazy_array_type = reinterpret_cast(type_val->data.x_lazy); - if (lazy_array_type->length < 1) + if (lazy_array_type->length == 0) return OnePossibleValueYes; return type_val_resolve_has_one_possible_value(g, lazy_array_type->elem_type->value); } @@ -4488,7 +4488,14 @@ static uint32_t get_async_frame_align_bytes(CodeGen *g) { } uint32_t get_ptr_align(CodeGen *g, ZigType *type) { - ZigType *ptr_type = get_src_ptr_type(type); + ZigType *ptr_type; + if (type->id == ZigTypeIdStruct) { + assert(type->data.structure.special == StructSpecialSlice); + TypeStructField *ptr_field = type->data.structure.fields[slice_ptr_index]; + ptr_type = resolve_struct_field_type(g, ptr_field); + } else { + ptr_type = get_src_ptr_type(type); + } if (ptr_type->id == ZigTypeIdPointer) { return (ptr_type->data.pointer.explicit_alignment == 0) ? get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment; @@ -4505,8 +4512,15 @@ uint32_t get_ptr_align(CodeGen *g, ZigType *type) { } } -bool get_ptr_const(ZigType *type) { - ZigType *ptr_type = get_src_ptr_type(type); +bool get_ptr_const(CodeGen *g, ZigType *type) { + ZigType *ptr_type; + if (type->id == ZigTypeIdStruct) { + assert(type->data.structure.special == StructSpecialSlice); + TypeStructField *ptr_field = type->data.structure.fields[slice_ptr_index]; + ptr_type = resolve_struct_field_type(g, ptr_field); + } else { + ptr_type = get_src_ptr_type(type); + } if (ptr_type->id == ZigTypeIdPointer) { return ptr_type->data.pointer.is_const; } else if (ptr_type->id == ZigTypeIdFn) { @@ -5282,6 +5296,11 @@ static uint32_t hash_const_val_ptr(ZigValue *const_val) { hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val); hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index); return hash_val; + case ConstPtrSpecialSubArray: + hash_val += (uint32_t)2643358777; + hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val); + hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index); + return hash_val; case ConstPtrSpecialBaseStruct: hash_val += (uint32_t)3518317043; hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val); @@ -5811,18 +5830,13 @@ ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) { // The elements array cannot be left unpopulated ZigType *array_type = result->type; ZigType *elem_type = array_type->data.array.child_type; - ZigValue *sentinel_value = array_type->data.array.sentinel; - const size_t elem_count = array_type->data.array.len + (sentinel_value != nullptr); + const size_t elem_count = array_type->data.array.len; result->data.x_array.data.s_none.elements = g->pass1_arena->allocate(elem_count); for (size_t i = 0; i < elem_count; i += 1) { ZigValue *elem_val = &result->data.x_array.data.s_none.elements[i]; copy_const_val(g, elem_val, get_the_one_possible_value(g, elem_type)); } - if (sentinel_value != nullptr) { - ZigValue *last_elem_val = &result->data.x_array.data.s_none.elements[elem_count - 1]; - copy_const_val(g, last_elem_val, sentinel_value); - } } else if (result->type->id == ZigTypeIdPointer) { result->data.x_ptr.special = ConstPtrSpecialRef; result->data.x_ptr.data.ref.pointee = get_the_one_possible_value(g, result->type->data.pointer.child_type); @@ -6753,6 +6767,7 @@ bool const_values_equal_ptr(ZigValue *a, ZigValue *b) { return false; return true; case ConstPtrSpecialBaseArray: + case ConstPtrSpecialSubArray: if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val) { return false; } @@ -7010,6 +7025,7 @@ static void render_const_val_ptr(CodeGen *g, Buf *buf, ZigValue *const_val, ZigT render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr)); return; case ConstPtrSpecialBaseArray: + case ConstPtrSpecialSubArray: buf_appendf(buf, "*"); // TODO we need a source node for const_ptr_pointee because it can generate compile errors render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr)); diff --git a/src/analyze.hpp b/src/analyze.hpp index c7cbab4b8c..b6404b1882 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -76,7 +76,7 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node, bool all ZigType *get_src_ptr_type(ZigType *type); uint32_t get_ptr_align(CodeGen *g, ZigType *type); -bool get_ptr_const(ZigType *type); +bool get_ptr_const(CodeGen *g, ZigType *type); ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry); ZigType *container_ref_type(ZigType *type_entry); bool type_is_complete(ZigType *type_entry); diff --git a/src/codegen.cpp b/src/codegen.cpp index 44add5d1e6..40256a9699 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5413,12 +5413,16 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI ZigType *array_type = array_ptr_type->data.pointer.child_type; LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); - LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); - bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); - ZigType *res_slice_ptr_type = instruction->base.value->type->data.structure.fields[slice_ptr_index]->type_entry; - ZigValue *sentinel = res_slice_ptr_type->data.pointer.sentinel; + ZigType *result_type = instruction->base.value->type; + if (!type_has_bits(g, result_type)) { + return nullptr; + } + + // This is not whether the result type has a sentinel, but whether there should be a sentinel check, + // e.g. if they used [a..b :s] syntax. + ZigValue *sentinel = instruction->sentinel; if (array_type->id == ZigTypeIdArray || (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) @@ -5453,6 +5457,8 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI } } if (!type_has_bits(g, array_type)) { + LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, ""); // TODO if runtime safety is on, store 0xaaaaaaa in ptr field @@ -5461,20 +5467,26 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI return tmp_struct_ptr; } - - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, ""); LLVMValueRef indices[] = { LLVMConstNull(g->builtin_types.entry_usize->llvm_type), start_val, }; LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + if (result_type->id == ZigTypeIdPointer) { + ir_assert(instruction->result_loc == nullptr, &instruction->base); + LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); + return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); + } else { + LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, ""); + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, ""); - LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, ""); + LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); - return tmp_struct_ptr; + return tmp_struct_ptr; + } } else if (array_type->id == ZigTypeIdPointer) { assert(array_type->data.pointer.ptr_len != PtrLenSingle); LLVMValueRef start_val = ir_llvm_value(g, instruction->start); @@ -5488,24 +5500,39 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI } } - if (type_has_bits(g, array_type)) { - size_t gen_ptr_index = instruction->base.value->type->data.structure.fields[slice_ptr_index]->gen_index; - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + if (!type_has_bits(g, array_type)) { + LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); + size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); + LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); + return tmp_struct_ptr; } - size_t gen_len_index = instruction->base.value->type->data.structure.fields[slice_len_index]->gen_index; + LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); + if (result_type->id == ZigTypeIdPointer) { + ir_assert(instruction->result_loc == nullptr, &instruction->base); + LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); + return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); + } + + LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); + + size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index; + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + + size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); gen_store_untyped(g, len_value, len_field_ptr, 0, false); return tmp_struct_ptr; + } else if (array_type->id == ZigTypeIdStruct) { assert(array_type->data.structure.special == StructSpecialSlice); assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); - assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(tmp_struct_ptr))) == LLVMStructTypeKind); size_t ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; assert(ptr_index != SIZE_MAX); @@ -5542,15 +5569,22 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI } } - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, ""); LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + if (result_type->id == ZigTypeIdPointer) { + ir_assert(instruction->result_loc == nullptr, &instruction->base); + LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); + return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); + } else { + LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, ""); + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, ""); - LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, ""); + LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, ""); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); - return tmp_struct_ptr; + return tmp_struct_ptr; + } } else { zig_unreachable(); } @@ -6635,7 +6669,6 @@ static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ZigValue *array_co }; return LLVMConstInBoundsGEP(base_ptr, indices, 2); } else { - assert(parent->id == ConstParentIdScalar); return base_ptr; } } @@ -6785,6 +6818,22 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Zig used_bits += packed_bits_size; } } + + if (type_entry->data.array.sentinel != nullptr) { + ZigValue *elem_val = type_entry->data.array.sentinel; + LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, elem_val); + + if (is_big_endian) { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, packed_bits_size, false); + val = LLVMConstShl(val, shift_amt); + val = LLVMConstOr(val, child_val); + } else { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false); + LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt); + val = LLVMConstOr(val, child_val_shifted); + used_bits += packed_bits_size; + } + } return val; } case ZigTypeIdVector: @@ -6847,24 +6896,16 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ZigValue *const_val, const cha return const_val->llvm_value; } case ConstPtrSpecialBaseArray: + case ConstPtrSpecialSubArray: { ZigValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val; assert(array_const_val->type->id == ZigTypeIdArray); if (!type_has_bits(g, array_const_val->type)) { - if (array_const_val->type->data.array.sentinel != nullptr) { - ZigValue *pointee = array_const_val->type->data.array.sentinel; - render_const_val(g, pointee, ""); - render_const_val_global(g, pointee, ""); - const_val->llvm_value = LLVMConstBitCast(pointee->llvm_global, - get_llvm_type(g, const_val->type)); - return const_val->llvm_value; - } else { - // make this a null pointer - ZigType *usize = g->builtin_types.entry_usize; - const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), - get_llvm_type(g, const_val->type)); - return const_val->llvm_value; - } + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), + get_llvm_type(g, const_val->type)); + return const_val->llvm_value; } size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val, elem_index); @@ -9644,6 +9685,21 @@ Error create_c_object_cache(CodeGen *g, CacheHash **out_cache_hash, bool verbose return ErrorNone; } +static bool need_llvm_module(CodeGen *g) { + return buf_len(&g->main_pkg->root_src_path) != 0; +} + +// before gen_c_objects +static bool main_output_dir_is_just_one_c_object_pre(CodeGen *g) { + return g->enable_cache && g->c_source_files.length == 1 && !need_llvm_module(g) && + g->out_type == OutTypeObj && g->link_objects.length == 0; +} + +// after gen_c_objects +static bool main_output_dir_is_just_one_c_object_post(CodeGen *g) { + return g->enable_cache && g->link_objects.length == 1 && !need_llvm_module(g) && g->out_type == OutTypeObj; +} + // returns true if it was a cache miss static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { Error err; @@ -9661,7 +9717,12 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { buf_len(c_source_basename), 0); Buf *final_o_basename = buf_alloc(); - os_path_extname(c_source_basename, final_o_basename, nullptr); + // We special case when doing build-obj for just one C file + if (main_output_dir_is_just_one_c_object_pre(g)) { + buf_init_from_buf(final_o_basename, g->root_out_name); + } else { + os_path_extname(c_source_basename, final_o_basename, nullptr); + } buf_append_str(final_o_basename, target_o_file_ext(g->zig_target)); CacheHash *cache_hash; @@ -10461,10 +10522,6 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { return ErrorNone; } -static bool need_llvm_module(CodeGen *g) { - return buf_len(&g->main_pkg->root_src_path) != 0; -} - static void resolve_out_paths(CodeGen *g) { assert(g->output_dir != nullptr); assert(g->root_out_name != nullptr); @@ -10476,10 +10533,6 @@ static void resolve_out_paths(CodeGen *g) { case OutTypeUnknown: zig_unreachable(); case OutTypeObj: - if (g->enable_cache && g->link_objects.length == 1 && !need_llvm_module(g)) { - buf_init_from_buf(&g->bin_file_output_path, g->link_objects.at(0)); - return; - } if (need_llvm_module(g) && g->link_objects.length != 0 && !g->enable_cache && buf_eql_buf(o_basename, out_basename)) { @@ -10574,6 +10627,16 @@ static void output_type_information(CodeGen *g) { } } +static void init_output_dir(CodeGen *g, Buf *digest) { + if (main_output_dir_is_just_one_c_object_post(g)) { + g->output_dir = buf_alloc(); + os_path_dirname(g->link_objects.at(0), g->output_dir); + } else { + g->output_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s", + buf_ptr(g->cache_dir), buf_ptr(digest)); + } +} + void codegen_build_and_link(CodeGen *g) { Error err; assert(g->out_type != OutTypeUnknown); @@ -10616,8 +10679,7 @@ void codegen_build_and_link(CodeGen *g) { } if (g->enable_cache && buf_len(&digest) != 0) { - g->output_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s", - buf_ptr(g->cache_dir), buf_ptr(&digest)); + init_output_dir(g, &digest); resolve_out_paths(g); } else { if (need_llvm_module(g)) { @@ -10638,8 +10700,7 @@ void codegen_build_and_link(CodeGen *g) { exit(1); } } - g->output_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s", - buf_ptr(g->cache_dir), buf_ptr(&digest)); + init_output_dir(g, &digest); if ((err = os_make_path(g->output_dir))) { fprintf(stderr, "Unable to create output directory: %s\n", err_str(err)); diff --git a/src/ir.cpp b/src/ir.cpp index 72e2a3fd98..fa80c6b117 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -784,14 +784,32 @@ static ZigValue *const_ptr_pointee_unchecked_no_isf(CodeGen *g, ZigValue *const_ break; case ConstPtrSpecialBaseArray: { ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val; - if (const_val->data.x_ptr.data.base_array.elem_index == array_val->type->data.array.len) { + size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; + if (elem_index == array_val->type->data.array.len) { result = array_val->type->data.array.sentinel; } else { expand_undef_array(g, array_val); - result = &array_val->data.x_array.data.s_none.elements[const_val->data.x_ptr.data.base_array.elem_index]; + result = &array_val->data.x_array.data.s_none.elements[elem_index]; } break; } + case ConstPtrSpecialSubArray: { + ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val; + size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; + + // TODO handle sentinel terminated arrays + expand_undef_array(g, array_val); + result = g->pass1_arena->create(); + result->special = array_val->special; + result->type = get_array_type(g, array_val->type->data.array.child_type, + array_val->type->data.array.len - elem_index, nullptr); + result->data.x_array.special = ConstArraySpecialNone; + result->data.x_array.data.s_none.elements = &array_val->data.x_array.data.s_none.elements[elem_index]; + result->parent.id = ConstParentIdArray; + result->parent.data.p_array.array_val = array_val; + result->parent.data.p_array.elem_index = elem_index; + break; + } case ConstPtrSpecialBaseStruct: { ZigValue *struct_val = const_val->data.x_ptr.data.base_struct.struct_val; expand_undef_struct(g, struct_val); @@ -849,11 +867,6 @@ static bool is_slice(ZigType *type) { return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialSlice; } -static bool slice_is_const(ZigType *type) { - assert(is_slice(type)); - return type->data.structure.fields[slice_ptr_index]->type_entry->data.pointer.is_const; -} - // This function returns true when you can change the type of a ZigValue and the // value remains meaningful. static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expected, ZigType *actual) { @@ -3719,7 +3732,8 @@ static IrInstSrc *ir_build_slice_src(IrBuilderSrc *irb, Scope *scope, AstNode *s } static IrInstGen *ir_build_slice_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *slice_type, - IrInstGen *ptr, IrInstGen *start, IrInstGen *end, bool safety_check_on, IrInstGen *result_loc) + IrInstGen *ptr, IrInstGen *start, IrInstGen *end, bool safety_check_on, IrInstGen *result_loc, + ZigValue *sentinel) { IrInstGenSlice *instruction = ir_build_inst_gen( &ira->new_irb, source_instruction->scope, source_instruction->source_node); @@ -3729,11 +3743,12 @@ static IrInstGen *ir_build_slice_gen(IrAnalyze *ira, IrInst *source_instruction, instruction->end = end; instruction->safety_check_on = safety_check_on; instruction->result_loc = result_loc; + instruction->sentinel = sentinel; ir_ref_inst_gen(ptr, ira->new_irb.current_basic_block); ir_ref_inst_gen(start, ira->new_irb.current_basic_block); - if (end) ir_ref_inst_gen(end, ira->new_irb.current_basic_block); - ir_ref_inst_gen(result_loc, ira->new_irb.current_basic_block); + if (end != nullptr) ir_ref_inst_gen(end, ira->new_irb.current_basic_block); + if (result_loc != nullptr) ir_ref_inst_gen(result_loc, ira->new_irb.current_basic_block); return &instruction->base; } @@ -12644,41 +12659,80 @@ static IrInstGen *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInst* sourc Error err; assert(array_ptr->value->type->id == ZigTypeIdPointer); + assert(array_ptr->value->type->data.pointer.child_type->id == ZigTypeIdArray); + + ZigType *array_type = array_ptr->value->type->data.pointer.child_type; + size_t array_len = array_type->data.array.len; + + // A zero-sized array can be casted regardless of the destination alignment, or + // whether the pointer is undefined, and the result is always comptime known. + // TODO However, this is exposing a result location bug that I failed to solve on the first try. + // If you want to try to fix the bug, uncomment this block and get the tests passing. + //if (array_len == 0 && array_type->data.array.sentinel == nullptr) { + // ZigValue *undef_array = ira->codegen->pass1_arena->create(); + // undef_array->special = ConstValSpecialUndef; + // undef_array->type = array_type; + + // IrInstGen *result = ir_const(ira, source_instr, wanted_type); + // init_const_slice(ira->codegen, result->value, undef_array, 0, 0, false); + // result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutComptimeConst; + // result->value->type = wanted_type; + // return result; + //} if ((err = type_resolve(ira->codegen, array_ptr->value->type, ResolveStatusAlignmentKnown))) { return ira->codegen->invalid_inst_gen; } - assert(array_ptr->value->type->data.pointer.child_type->id == ZigTypeIdArray); - - const size_t array_len = array_ptr->value->type->data.pointer.child_type->data.array.len; - - // A zero-sized array can always be casted irregardless of the destination - // alignment if (array_len != 0) { wanted_type = adjust_slice_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, array_ptr->value->type)); } if (instr_is_comptime(array_ptr)) { - ZigValue *array_ptr_val = ir_resolve_const(ira, array_ptr, UndefBad); + UndefAllowed undef_allowed = (array_len == 0) ? UndefOk : UndefBad; + ZigValue *array_ptr_val = ir_resolve_const(ira, array_ptr, undef_allowed); if (array_ptr_val == nullptr) return ira->codegen->invalid_inst_gen; - ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, array_ptr_val, source_instr->source_node); - if (pointee == nullptr) - return ira->codegen->invalid_inst_gen; - if (pointee->special != ConstValSpecialRuntime) { - assert(array_ptr_val->type->id == ZigTypeIdPointer); - ZigType *array_type = array_ptr_val->type->data.pointer.child_type; - assert(is_slice(wanted_type)); - bool is_const = wanted_type->data.structure.fields[slice_ptr_index]->type_entry->data.pointer.is_const; + ir_assert(is_slice(wanted_type), source_instr); + if (array_ptr_val->special == ConstValSpecialUndef) { + ZigValue *undef_array = ira->codegen->pass1_arena->create(); + undef_array->special = ConstValSpecialUndef; + undef_array->type = array_type; IrInstGen *result = ir_const(ira, source_instr, wanted_type); - init_const_slice(ira->codegen, result->value, pointee, 0, array_type->data.array.len, is_const); - result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; + init_const_slice(ira->codegen, result->value, undef_array, 0, 0, false); + result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutComptimeConst; result->value->type = wanted_type; return result; } + bool wanted_const = wanted_type->data.structure.fields[slice_ptr_index]->type_entry->data.pointer.is_const; + // Optimization to avoid creating unnecessary ZigValue in const_ptr_pointee + if (array_ptr_val->data.x_ptr.special == ConstPtrSpecialSubArray) { + ZigValue *array_val = array_ptr_val->data.x_ptr.data.base_array.array_val; + if (array_val->special != ConstValSpecialRuntime) { + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + init_const_slice(ira->codegen, result->value, array_val, + array_ptr_val->data.x_ptr.data.base_array.elem_index, + array_type->data.array.len, wanted_const); + result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; + result->value->type = wanted_type; + return result; + } + } else { + ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, array_ptr_val, source_instr->source_node); + if (pointee == nullptr) + return ira->codegen->invalid_inst_gen; + if (pointee->special != ConstValSpecialRuntime) { + assert(array_ptr_val->type->id == ZigTypeIdPointer); + + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + init_const_slice(ira->codegen, result->value, pointee, 0, array_type->data.array.len, wanted_const); + result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; + result->value->type = wanted_type; + return result; + } + } } if (result_loc == nullptr) result_loc = no_result_loc(); @@ -14548,7 +14602,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr, return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } - // *[N]T to ?[]const T + // *[N]T to ?[]T if (wanted_type->id == ZigTypeIdOptional && is_slice(wanted_type->data.maybe.child_type) && actual_type->id == ZigTypeIdPointer && @@ -19884,6 +19938,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf), pointee); if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) return err; + buf_deinit(&buf); return ErrorNone; } @@ -19903,6 +19958,31 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source dst_size, buf_ptr(&pointee->type->name), src_size)); return ErrorSemanticAnalyzeFail; } + case ConstPtrSpecialSubArray: { + ZigValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val; + assert(array_val->type->id == ZigTypeIdArray); + if (array_val->data.x_array.special != ConstArraySpecialNone) + zig_panic("TODO"); + if (dst_size > src_size) { + size_t elem_index = ptr_val->data.x_ptr.data.base_array.elem_index; + opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from %s at index %" ZIG_PRI_usize " which is %" ZIG_PRI_usize " bytes", + dst_size, buf_ptr(&array_val->type->name), elem_index, src_size)); + return ErrorSemanticAnalyzeFail; + } + size_t elem_size = src_size; + size_t elem_count = (dst_size % elem_size == 0) ? (dst_size / elem_size) : (dst_size / elem_size + 1); + Buf buf = BUF_INIT; + buf_resize(&buf, elem_count * elem_size); + for (size_t i = 0; i < elem_count; i += 1) { + ZigValue *elem_val = &array_val->data.x_array.data.s_none.elements[i]; + buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf) + (i * elem_size), elem_val); + } + if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) + return err; + buf_deinit(&buf); + return ErrorNone; + } case ConstPtrSpecialBaseArray: { ZigValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val; assert(array_val->type->id == ZigTypeIdArray); @@ -19926,6 +20006,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source } if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) return err; + buf_deinit(&buf); return ErrorNone; } case ConstPtrSpecialBaseStruct: @@ -20505,6 +20586,44 @@ static ZigType *adjust_ptr_allow_zero(CodeGen *g, ZigType *ptr_type, bool allow_ allow_zero); } +static Error compute_elem_align(IrAnalyze *ira, ZigType *elem_type, uint32_t base_ptr_align, + uint64_t elem_index, uint32_t *result) +{ + Error err; + + if (base_ptr_align == 0) { + *result = 0; + return ErrorNone; + } + + // figure out the largest alignment possible + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) + return err; + + uint64_t elem_size = type_size(ira->codegen, elem_type); + uint64_t abi_align = get_abi_alignment(ira->codegen, elem_type); + uint64_t ptr_align = base_ptr_align; + + uint64_t chosen_align = abi_align; + if (ptr_align >= abi_align) { + while (ptr_align > abi_align) { + if ((elem_index * elem_size) % ptr_align == 0) { + chosen_align = ptr_align; + break; + } + ptr_align >>= 1; + } + } else if (elem_size >= ptr_align && elem_size % ptr_align == 0) { + chosen_align = ptr_align; + } else { + // can't get here because guaranteed elem_size >= abi_align + zig_unreachable(); + } + + *result = chosen_align; + return ErrorNone; +} + static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemPtr *elem_ptr_instruction) { Error err; IrInstGen *array_ptr = elem_ptr_instruction->array_ptr->child; @@ -20545,11 +20664,6 @@ static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemP } if (array_type->id == ZigTypeIdArray) { - if (array_type->data.array.len == 0) { - ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, - buf_sprintf("index 0 outside array of size 0")); - return ira->codegen->invalid_inst_gen; - } ZigType *child_type = array_type->data.array.child_type; if (ptr_type->data.pointer.host_int_bytes == 0) { return_type = get_pointer_to_type_extra(ira->codegen, child_type, @@ -20648,29 +20762,11 @@ static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemP get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, (uint32_t)index, nullptr, nullptr); } else if (return_type->data.pointer.explicit_alignment != 0) { - // figure out the largest alignment possible - - if ((err = type_resolve(ira->codegen, return_type->data.pointer.child_type, ResolveStatusSizeKnown))) + uint32_t chosen_align; + if ((err = compute_elem_align(ira, return_type->data.pointer.child_type, + return_type->data.pointer.explicit_alignment, index, &chosen_align))) + { return ira->codegen->invalid_inst_gen; - - uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type); - uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type); - uint64_t ptr_align = get_ptr_align(ira->codegen, return_type); - - uint64_t chosen_align = abi_align; - if (ptr_align >= abi_align) { - while (ptr_align > abi_align) { - if ((index * elem_size) % ptr_align == 0) { - chosen_align = ptr_align; - break; - } - ptr_align >>= 1; - } - } else if (elem_size >= ptr_align && elem_size % ptr_align == 0) { - chosen_align = ptr_align; - } else { - // can't get here because guaranteed elem_size >= abi_align - zig_unreachable(); } return_type = adjust_ptr_align(ira->codegen, return_type, chosen_align); } @@ -20791,6 +20887,7 @@ static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemP } break; case ConstPtrSpecialBaseArray: + case ConstPtrSpecialSubArray: { size_t offset = array_ptr_val->data.x_ptr.data.base_array.elem_index; new_index = offset + index; @@ -20861,6 +20958,7 @@ static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemP out_val->data.x_ptr.special = ConstPtrSpecialRef; out_val->data.x_ptr.data.ref.pointee = ptr_field->data.x_ptr.data.ref.pointee; break; + case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: { size_t offset = ptr_field->data.x_ptr.data.base_array.elem_index; @@ -25412,11 +25510,22 @@ static IrInstGen *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstSrcE static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { Error err; - ZigType *ptr_type = get_src_ptr_type(ty); + ZigType *ptr_type; + if (is_slice(ty)) { + TypeStructField *ptr_field = ty->data.structure.fields[slice_ptr_index]; + ptr_type = resolve_struct_field_type(ira->codegen, ptr_field); + } else { + ptr_type = get_src_ptr_type(ty); + } assert(ptr_type != nullptr); if (ptr_type->id == ZigTypeIdPointer) { if ((err = type_resolve(ira->codegen, ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) return err; + } else if (is_slice(ptr_type)) { + TypeStructField *ptr_field = ptr_type->data.structure.fields[slice_ptr_index]; + ZigType *slice_ptr_type = resolve_struct_field_type(ira->codegen, ptr_field); + if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return err; } *result_align = get_ptr_align(ira->codegen, ty); @@ -25871,6 +25980,7 @@ static IrInstGen *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstSrcMemset start = 0; bound_end = 1; break; + case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: { ZigValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; @@ -26004,6 +26114,7 @@ static IrInstGen *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstSrcMemcpy dest_start = 0; dest_end = 1; break; + case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: { ZigValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; @@ -26047,6 +26158,7 @@ static IrInstGen *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstSrcMemcpy src_start = 0; src_end = 1; break; + case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: { ZigValue *array_val = src_ptr_val->data.x_ptr.data.base_array.array_val; @@ -26090,7 +26202,19 @@ static IrInstGen *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstSrcMemcpy return ir_build_memcpy_gen(ira, &instruction->base.base, casted_dest_ptr, casted_src_ptr, casted_count); } +static ZigType *get_result_loc_type(IrAnalyze *ira, ResultLoc *result_loc) { + if (result_loc == nullptr) return nullptr; + + if (result_loc->id == ResultLocIdCast) { + return ir_resolve_type(ira, result_loc->source_instruction->child); + } + + return nullptr; +} + static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *instruction) { + Error err; + IrInstGen *ptr_ptr = instruction->ptr->child; if (type_is_invalid(ptr_ptr->value->type)) return ira->codegen->invalid_inst_gen; @@ -26120,6 +26244,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i end = nullptr; } + ZigValue *slice_sentinel_val = nullptr; ZigType *non_sentinel_slice_ptr_type; ZigType *elem_type; @@ -26170,6 +26295,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i } } else if (is_slice(array_type)) { ZigType *maybe_sentineled_slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; + slice_sentinel_val = maybe_sentineled_slice_ptr_type->data.pointer.sentinel; non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr); elem_type = non_sentinel_slice_ptr_type->data.pointer.child_type; } else { @@ -26178,7 +26304,6 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i return ira->codegen->invalid_inst_gen; } - ZigType *return_type; ZigValue *sentinel_val = nullptr; if (instruction->sentinel) { IrInstGen *uncasted_sentinel = instruction->sentinel->child; @@ -26190,11 +26315,76 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); if (sentinel_val == nullptr) return ira->codegen->invalid_inst_gen; - ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, sentinel_val); + } + + ZigType *child_array_type = (array_type->id == ZigTypeIdPointer && + array_type->data.pointer.ptr_len == PtrLenSingle) ? array_type->data.pointer.child_type : array_type; + + ZigType *return_type; + + // If start index and end index are both comptime known, then the result type is a pointer to array + // not a slice. However, if the start or end index is a lazy value, and the result location is a slice, + // then the pointer-to-array would be casted to a slice anyway. So, we preserve the laziness of these + // values by making the return type a slice. + ZigType *res_loc_type = get_result_loc_type(ira, instruction->result_loc); + bool result_loc_is_slice = (res_loc_type != nullptr && is_slice(res_loc_type)); + bool end_is_known = !result_loc_is_slice && + ((end != nullptr && value_is_comptime(end->value)) || + (end == nullptr && child_array_type->id == ZigTypeIdArray)); + + ZigValue *array_sentinel = sentinel_val; + if (end_is_known) { + uint64_t end_scalar; + if (end != nullptr) { + ZigValue *end_val = ir_resolve_const(ira, end, UndefBad); + if (!end_val) + return ira->codegen->invalid_inst_gen; + end_scalar = bigint_as_u64(&end_val->data.x_bigint); + } else { + end_scalar = child_array_type->data.array.len; + } + array_sentinel = (child_array_type->id == ZigTypeIdArray && end_scalar == child_array_type->data.array.len) + ? child_array_type->data.array.sentinel : sentinel_val; + + if (value_is_comptime(casted_start->value)) { + ZigValue *start_val = ir_resolve_const(ira, casted_start, UndefBad); + if (!start_val) + return ira->codegen->invalid_inst_gen; + + uint64_t start_scalar = bigint_as_u64(&start_val->data.x_bigint); + + if (start_scalar > end_scalar) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds slice")); + return ira->codegen->invalid_inst_gen; + } + + uint32_t base_ptr_align = non_sentinel_slice_ptr_type->data.pointer.explicit_alignment; + uint32_t ptr_byte_alignment = 0; + if (end_scalar > start_scalar) { + if ((err = compute_elem_align(ira, elem_type, base_ptr_align, start_scalar, &ptr_byte_alignment))) + return ira->codegen->invalid_inst_gen; + } + + ZigType *return_array_type = get_array_type(ira->codegen, elem_type, end_scalar - start_scalar, + array_sentinel); + return_type = get_pointer_to_type_extra(ira->codegen, return_array_type, + non_sentinel_slice_ptr_type->data.pointer.is_const, + non_sentinel_slice_ptr_type->data.pointer.is_volatile, + PtrLenSingle, ptr_byte_alignment, 0, 0, false); + goto done_with_return_type; + } + } else if (array_sentinel == nullptr && end == nullptr) { + array_sentinel = slice_sentinel_val; + } + if (array_sentinel != nullptr) { + // TODO deal with non-abi-alignment here + ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, array_sentinel); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else { + // TODO deal with non-abi-alignment here return_type = get_slice_type(ira->codegen, non_sentinel_slice_ptr_type); } +done_with_return_type: if (instr_is_comptime(ptr_ptr) && value_is_comptime(casted_start->value) && @@ -26205,12 +26395,8 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i size_t abs_offset; size_t rel_end; bool ptr_is_undef = false; - if (array_type->id == ZigTypeIdArray || - (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) - { + if (child_array_type->id == ZigTypeIdArray) { if (array_type->id == ZigTypeIdPointer) { - ZigType *child_array_type = array_type->data.pointer.child_type; - assert(child_array_type->id == ZigTypeIdArray); parent_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); if (parent_ptr == nullptr) return ira->codegen->invalid_inst_gen; @@ -26221,6 +26407,10 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i abs_offset = 0; rel_end = SIZE_MAX; ptr_is_undef = true; + } else if (parent_ptr->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { + array_val = nullptr; + abs_offset = 0; + rel_end = SIZE_MAX; } else { array_val = const_ptr_pointee(ira, ira->codegen, parent_ptr, instruction->base.base.source_node); if (array_val == nullptr) @@ -26263,6 +26453,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i rel_end = 1; } break; + case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: array_val = parent_ptr->data.x_ptr.data.base_array.array_val; abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index; @@ -26313,6 +26504,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i abs_offset = SIZE_MAX; rel_end = 1; break; + case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: array_val = parent_ptr->data.x_ptr.data.base_array.array_val; abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index; @@ -26373,15 +26565,28 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i } IrInstGen *result = ir_const(ira, &instruction->base.base, return_type); - ZigValue *out_val = result->value; - out_val->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, 2); - ZigValue *ptr_val = out_val->data.x_struct.fields[slice_ptr_index]; + ZigValue *ptr_val; + if (return_type->id == ZigTypeIdPointer) { + // pointer to array + ptr_val = result->value; + } else { + // slice + result->value->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, 2); + ptr_val = result->value->data.x_struct.fields[slice_ptr_index]; + + ZigValue *len_val = result->value->data.x_struct.fields[slice_len_index]; + init_const_usize(ira->codegen, len_val, end_scalar - start_scalar); + } + + bool return_type_is_const = non_sentinel_slice_ptr_type->data.pointer.is_const; if (array_val) { size_t index = abs_offset + start_scalar; - bool is_const = slice_is_const(return_type); - init_const_ptr_array(ira->codegen, ptr_val, array_val, index, is_const, PtrLenUnknown); + init_const_ptr_array(ira->codegen, ptr_val, array_val, index, return_type_is_const, PtrLenUnknown); + if (return_type->id == ZigTypeIdPointer) { + ptr_val->data.x_ptr.special = ConstPtrSpecialSubArray; + } if (array_type->id == ZigTypeIdArray) { ptr_val->data.x_ptr.mut = ptr_ptr->value->data.x_ptr.mut; } else if (is_slice(array_type)) { @@ -26391,16 +26596,17 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i } } else if (ptr_is_undef) { ptr_val->type = get_pointer_to_type(ira->codegen, parent_ptr->type->data.pointer.child_type, - slice_is_const(return_type)); + return_type_is_const); ptr_val->special = ConstValSpecialUndef; } else switch (parent_ptr->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: zig_unreachable(); case ConstPtrSpecialRef: - init_const_ptr_ref(ira->codegen, ptr_val, - parent_ptr->data.x_ptr.data.ref.pointee, slice_is_const(return_type)); + init_const_ptr_ref(ira->codegen, ptr_val, parent_ptr->data.x_ptr.data.ref.pointee, + return_type_is_const); break; + case ConstPtrSpecialSubArray: case ConstPtrSpecialBaseArray: zig_unreachable(); case ConstPtrSpecialBaseStruct: @@ -26415,7 +26621,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i init_const_ptr_hard_coded_addr(ira->codegen, ptr_val, parent_ptr->type->data.pointer.child_type, parent_ptr->data.x_ptr.data.hard_coded_addr.addr + start_scalar, - slice_is_const(return_type)); + return_type_is_const); break; case ConstPtrSpecialFunction: zig_panic("TODO"); @@ -26423,26 +26629,11 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i zig_panic("TODO"); } - ZigValue *len_val = out_val->data.x_struct.fields[slice_len_index]; - init_const_usize(ira->codegen, len_val, end_scalar - start_scalar); - + // In the case of pointer-to-array, we must restore this because above it overwrites ptr_val->type + result->value->type = return_type; return result; } - IrInstGen *result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, - return_type, nullptr, true, true); - if (result_loc != nullptr) { - if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { - return result_loc; - } - IrInstGen *dummy_value = ir_const(ira, &instruction->base.base, return_type); - dummy_value->value->special = ConstValSpecialRuntime; - IrInstGen *dummy_result = ir_implicit_cast2(ira, &instruction->base.base, - dummy_value, result_loc->value->type->data.pointer.child_type); - if (type_is_invalid(dummy_result->value->type)) - return ira->codegen->invalid_inst_gen; - } - if (generate_non_null_assert) { IrInstGen *ptr_val = ir_get_deref(ira, &instruction->base.base, ptr_ptr, nullptr); @@ -26452,8 +26643,26 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i ir_build_assert_non_null(ira, &instruction->base.base, ptr_val); } + IrInstGen *result_loc = nullptr; + + if (return_type->id != ZigTypeIdPointer) { + result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, + return_type, nullptr, true, true); + if (result_loc != nullptr) { + if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { + return result_loc; + } + IrInstGen *dummy_value = ir_const(ira, &instruction->base.base, return_type); + dummy_value->value->special = ConstValSpecialRuntime; + IrInstGen *dummy_result = ir_implicit_cast2(ira, &instruction->base.base, + dummy_value, result_loc->value->type->data.pointer.child_type); + if (type_is_invalid(dummy_result->value->type)) + return ira->codegen->invalid_inst_gen; + } + } + return ir_build_slice_gen(ira, &instruction->base.base, return_type, ptr_ptr, - casted_start, end, instruction->safety_check_on, result_loc); + casted_start, end, instruction->safety_check_on, result_loc, sentinel_val); } static IrInstGen *ir_analyze_instruction_has_field(IrAnalyze *ira, IrInstSrcHasField *instruction) { @@ -27479,10 +27688,18 @@ static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrIn // We have a check for zero bits later so we use get_src_ptr_type to // validate src_type and dest_type. - ZigType *src_ptr_type = get_src_ptr_type(src_type); - if (src_ptr_type == nullptr) { - ir_add_error(ira, ptr_src, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name))); - return ira->codegen->invalid_inst_gen; + ZigType *if_slice_ptr_type; + if (is_slice(src_type)) { + TypeStructField *ptr_field = src_type->data.structure.fields[slice_ptr_index]; + if_slice_ptr_type = resolve_struct_field_type(ira->codegen, ptr_field); + } else { + if_slice_ptr_type = src_type; + + ZigType *src_ptr_type = get_src_ptr_type(src_type); + if (src_ptr_type == nullptr) { + ir_add_error(ira, ptr_src, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name))); + return ira->codegen->invalid_inst_gen; + } } ZigType *dest_ptr_type = get_src_ptr_type(dest_type); @@ -27492,7 +27709,7 @@ static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrIn return ira->codegen->invalid_inst_gen; } - if (get_ptr_const(src_type) && !get_ptr_const(dest_type)) { + if (get_ptr_const(ira->codegen, src_type) && !get_ptr_const(ira->codegen, dest_type)) { ir_add_error(ira, source_instr, buf_sprintf("cast discards const qualifier")); return ira->codegen->invalid_inst_gen; } @@ -27510,7 +27727,10 @@ static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrIn if ((err = type_resolve(ira->codegen, src_type, ResolveStatusZeroBitsKnown))) return ira->codegen->invalid_inst_gen; - if (type_has_bits(ira->codegen, dest_type) && !type_has_bits(ira->codegen, src_type) && safety_check_on) { + if (safety_check_on && + type_has_bits(ira->codegen, dest_type) && + !type_has_bits(ira->codegen, if_slice_ptr_type)) + { ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("'%s' and '%s' do not have the same in-memory representation", buf_ptr(&src_type->name), buf_ptr(&dest_type->name))); @@ -27521,6 +27741,14 @@ static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrIn return ira->codegen->invalid_inst_gen; } + // For slices, follow the `ptr` field. + if (is_slice(src_type)) { + TypeStructField *ptr_field = src_type->data.structure.fields[slice_ptr_index]; + IrInstGen *ptr_ref = ir_get_ref(ira, source_instr, ptr, true, false); + IrInstGen *ptr_ptr = ir_analyze_struct_field_ptr(ira, source_instr, ptr_field, ptr_ref, src_type, false); + ptr = ir_get_deref(ira, source_instr, ptr_ptr, nullptr); + } + if (instr_is_comptime(ptr)) { bool dest_allows_addr_zero = ptr_allows_addr_zero(dest_type); UndefAllowed is_undef_allowed = dest_allows_addr_zero ? UndefOk : UndefBad; @@ -27624,6 +27852,9 @@ static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ZigValue buf_write_value_bytes(codegen, &buf[buf_i], elem); buf_i += type_size(codegen, elem->type); } + if (val->type->id == ZigTypeIdArray && val->type->data.array.sentinel != nullptr) { + buf_write_value_bytes(codegen, &buf[buf_i], val->type->data.array.sentinel); + } } static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ZigValue *val) { diff --git a/src/link.cpp b/src/link.cpp index 45a9eae335..4e6227d270 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -566,6 +566,7 @@ static const char *build_libc_object(CodeGen *parent_gen, const char *name, CFil Stage2ProgressNode *progress_node) { CodeGen *child_gen = create_child_codegen(parent_gen, nullptr, OutTypeObj, nullptr, name, progress_node); + child_gen->root_out_name = buf_create_from_str(name); ZigList c_source_files = {0}; c_source_files.append(c_file); child_gen->c_source_files = c_source_files; diff --git a/src/main.cpp b/src/main.cpp index e610135397..522a16c7a7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1291,6 +1291,7 @@ static int main0(int argc, char **argv) { if (g->enable_cache) { #if defined(ZIG_OS_WINDOWS) buf_replace(&g->bin_file_output_path, '/', '\\'); + buf_replace(g->output_dir, '/', '\\'); #endif if (final_output_dir_step != nullptr) { Buf *dest_basename = buf_alloc(); @@ -1304,7 +1305,7 @@ static int main0(int argc, char **argv) { return main_exit(root_progress_node, EXIT_FAILURE); } } else { - if (printf("%s\n", buf_ptr(&g->bin_file_output_path)) < 0) + if (printf("%s\n", buf_ptr(g->output_dir)) < 0) return main_exit(root_progress_node, EXIT_FAILURE); } } diff --git a/test/cli.zig b/test/cli.zig index 9bc4a21c90..4c067d16ae 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -36,7 +36,7 @@ pub fn main() !void { testMissingOutputPath, }; for (test_fns) |testFn| { - try fs.deleteTree(dir_path); + try fs.cwd().deleteTree(dir_path); try fs.cwd().makeDir(dir_path); try testFn(zig_exe, dir_path); } diff --git a/test/compare_output.zig b/test/compare_output.zig index 1a0179c4c2..46c475e046 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -292,7 +292,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\pub export fn main() c_int { \\ var array = [_]u32{ 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; \\ - \\ c.qsort(@ptrCast(?*c_void, array[0..].ptr), @intCast(c_ulong, array.len), @sizeOf(i32), compare_fn); + \\ c.qsort(@ptrCast(?*c_void, &array), @intCast(c_ulong, array.len), @sizeOf(i32), compare_fn); \\ \\ for (array) |item, i| { \\ if (item != i) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f894a152a7..e3e1462173 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -103,18 +103,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:3:23: error: pointer to size 0 type has no address", }); - cases.addTest("slice to pointer conversion mismatch", - \\pub fn bytesAsSlice(bytes: var) [*]align(1) const u16 { - \\ return @ptrCast([*]align(1) const u16, bytes.ptr)[0..1]; - \\} - \\test "bytesAsSlice" { - \\ const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF }; - \\ const slice = bytesAsSlice(bytes[0..]); - \\} - , &[_][]const u8{ - "tmp.zig:2:54: error: expected type '[*]align(1) const u16', found '[]align(1) const u16'", - }); - cases.addTest("access invalid @typeInfo decl", \\const A = B; \\test "Crash" { @@ -1918,8 +1906,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add("reading past end of pointer casted array", \\comptime { \\ const array: [4]u8 = "aoeu".*; - \\ const slice = array[1..]; - \\ const int_ptr = @ptrCast(*const u24, slice.ptr); + \\ const sub_array = array[1..]; + \\ const int_ptr = @ptrCast(*const u24, sub_array); \\ const deref = int_ptr.*; \\} , &[_][]const u8{ diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 5047bfd0d0..b8ab47ddac 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -69,7 +69,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} \\pub fn main() void { \\ var buf: [4]u8 = undefined; - \\ const ptr = buf[0..].ptr; + \\ const ptr: [*]u8 = &buf; \\ const slice = ptr[0..3 :0]; \\} ); diff --git a/test/stage1/behavior/align.zig b/test/stage1/behavior/align.zig index 5a5138d567..e9b7e0f1d6 100644 --- a/test/stage1/behavior/align.zig +++ b/test/stage1/behavior/align.zig @@ -5,10 +5,17 @@ const builtin = @import("builtin"); var foo: u8 align(4) = 100; test "global variable alignment" { - expect(@TypeOf(&foo).alignment == 4); - expect(@TypeOf(&foo) == *align(4) u8); - const slice = @as(*[1]u8, &foo)[0..]; - expect(@TypeOf(slice) == []align(4) u8); + comptime expect(@TypeOf(&foo).alignment == 4); + comptime expect(@TypeOf(&foo) == *align(4) u8); + { + const slice = @as(*[1]u8, &foo)[0..]; + comptime expect(@TypeOf(slice) == *align(4) [1]u8); + } + { + var runtime_zero: usize = 0; + const slice = @as(*[1]u8, &foo)[runtime_zero..]; + comptime expect(@TypeOf(slice) == []align(4) u8); + } } fn derp() align(@sizeOf(usize) * 2) i32 { @@ -171,18 +178,19 @@ test "runtime known array index has best alignment possible" { // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2 var smaller align(2) = [_]u32{ 1, 2, 3, 4 }; - comptime expect(@TypeOf(smaller[0..]) == []align(2) u32); - comptime expect(@TypeOf(smaller[0..].ptr) == [*]align(2) u32); - testIndex(smaller[0..].ptr, 0, *align(2) u32); - testIndex(smaller[0..].ptr, 1, *align(2) u32); - testIndex(smaller[0..].ptr, 2, *align(2) u32); - testIndex(smaller[0..].ptr, 3, *align(2) u32); + var runtime_zero: usize = 0; + comptime expect(@TypeOf(smaller[runtime_zero..]) == []align(2) u32); + comptime expect(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32); + testIndex(smaller[runtime_zero..].ptr, 0, *align(2) u32); + testIndex(smaller[runtime_zero..].ptr, 1, *align(2) u32); + testIndex(smaller[runtime_zero..].ptr, 2, *align(2) u32); + testIndex(smaller[runtime_zero..].ptr, 3, *align(2) u32); // has to use ABI alignment because index known at runtime only - testIndex2(array[0..].ptr, 0, *u8); - testIndex2(array[0..].ptr, 1, *u8); - testIndex2(array[0..].ptr, 2, *u8); - testIndex2(array[0..].ptr, 3, *u8); + testIndex2(array[runtime_zero..].ptr, 0, *u8); + testIndex2(array[runtime_zero..].ptr, 1, *u8); + testIndex2(array[runtime_zero..].ptr, 2, *u8); + testIndex2(array[runtime_zero..].ptr, 3, *u8); } fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) void { comptime expect(@TypeOf(&smaller[index]) == T); diff --git a/test/stage1/behavior/array.zig b/test/stage1/behavior/array.zig index da56864cc9..792c0e70d1 100644 --- a/test/stage1/behavior/array.zig +++ b/test/stage1/behavior/array.zig @@ -28,6 +28,24 @@ fn getArrayLen(a: []const u32) usize { return a.len; } +test "array with sentinels" { + const S = struct { + fn doTheTest(is_ct: bool) void { + var zero_sized: [0:0xde]u8 = [_:0xde]u8{}; + expectEqual(@as(u8, 0xde), zero_sized[0]); + // Disabled at runtime because of + // https://github.com/ziglang/zig/issues/4372 + if (is_ct) { + var reinterpreted = @ptrCast(*[1]u8, &zero_sized); + expectEqual(@as(u8, 0xde), reinterpreted[0]); + } + } + }; + + S.doTheTest(false); + comptime S.doTheTest(true); +} + test "void arrays" { var array: [4]void = undefined; array[0] = void{}; @@ -376,3 +394,23 @@ test "type deduction for array subscript expression" { S.doTheTest(); comptime S.doTheTest(); } + +test "sentinel element count towards the ABI size calculation" { + const S = struct { + fn doTheTest() void { + const T = packed struct { + fill_pre: u8 = 0x55, + data: [0:0]u8 = undefined, + fill_post: u8 = 0xAA, + }; + var x = T{}; + var as_slice = mem.asBytes(&x); + expectEqual(@as(usize, 3), as_slice.len); + expectEqual(@as(u8, 0x55), as_slice[0]); + expectEqual(@as(u8, 0xAA), as_slice[2]); + } + }; + + S.doTheTest(); + comptime S.doTheTest(); +} diff --git a/test/stage1/behavior/cast.zig b/test/stage1/behavior/cast.zig index 04b2e97e8c..91e947e97d 100644 --- a/test/stage1/behavior/cast.zig +++ b/test/stage1/behavior/cast.zig @@ -431,7 +431,8 @@ fn incrementVoidPtrValue(value: ?*c_void) void { test "implicit cast from [*]T to ?*c_void" { var a = [_]u8{ 3, 2, 1 }; - incrementVoidPtrArray(a[0..].ptr, 3); + var runtime_zero: usize = 0; + incrementVoidPtrArray(a[runtime_zero..].ptr, 3); expect(std.mem.eql(u8, &a, &[_]u8{ 4, 3, 2 })); } diff --git a/test/stage1/behavior/eval.zig b/test/stage1/behavior/eval.zig index 55ace6198c..2af34eaf7e 100644 --- a/test/stage1/behavior/eval.zig +++ b/test/stage1/behavior/eval.zig @@ -524,7 +524,7 @@ test "comptime slice of slice preserves comptime var" { test "comptime slice of pointer preserves comptime var" { comptime { var buff: [10]u8 = undefined; - var a = buff[0..].ptr; + var a = @ptrCast([*]u8, &buff); a[0..1][0] = 1; expect(buff[0..][0..][0] == 1); } diff --git a/test/stage1/behavior/misc.zig b/test/stage1/behavior/misc.zig index 44a8334b5d..51ee6f3a4f 100644 --- a/test/stage1/behavior/misc.zig +++ b/test/stage1/behavior/misc.zig @@ -102,8 +102,8 @@ test "memcpy and memset intrinsics" { var foo: [20]u8 = undefined; var bar: [20]u8 = undefined; - @memset(foo[0..].ptr, 'A', foo.len); - @memcpy(bar[0..].ptr, foo[0..].ptr, bar.len); + @memset(&foo, 'A', foo.len); + @memcpy(&bar, &foo, bar.len); if (bar[11] != 'A') unreachable; } @@ -565,12 +565,16 @@ test "volatile load and store" { expect(ptr.* == 1235); } -test "slice string literal has type []const u8" { +test "slice string literal has correct type" { comptime { - expect(@TypeOf("aoeu"[0..]) == []const u8); + expect(@TypeOf("aoeu"[0..]) == *const [4:0]u8); const array = [_]i32{ 1, 2, 3, 4 }; - expect(@TypeOf(array[0..]) == []const i32); + expect(@TypeOf(array[0..]) == *const [4]i32); } + var runtime_zero: usize = 0; + comptime expect(@TypeOf("aoeu"[runtime_zero..]) == [:0]const u8); + const array = [_]i32{ 1, 2, 3, 4 }; + comptime expect(@TypeOf(array[runtime_zero..]) == []const i32); } test "pointer child field" { diff --git a/test/stage1/behavior/pointers.zig b/test/stage1/behavior/pointers.zig index bcc1d62df3..fbce9731f0 100644 --- a/test/stage1/behavior/pointers.zig +++ b/test/stage1/behavior/pointers.zig @@ -159,12 +159,13 @@ test "allowzero pointer and slice" { var opt_ptr: ?[*]allowzero i32 = ptr; expect(opt_ptr != null); expect(@ptrToInt(ptr) == 0); - var slice = ptr[0..10]; - expect(@TypeOf(slice) == []allowzero i32); + var runtime_zero: usize = 0; + var slice = ptr[runtime_zero..10]; + comptime expect(@TypeOf(slice) == []allowzero i32); expect(@ptrToInt(&slice[5]) == 20); - expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero); - expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero); + comptime expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero); + comptime expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero); } test "assign null directly to C pointer and test null equality" { diff --git a/test/stage1/behavior/ptrcast.zig b/test/stage1/behavior/ptrcast.zig index 1c1cf251a0..3925325e61 100644 --- a/test/stage1/behavior/ptrcast.zig +++ b/test/stage1/behavior/ptrcast.zig @@ -13,7 +13,7 @@ fn testReinterpretBytesAsInteger() void { builtin.Endian.Little => 0xab785634, builtin.Endian.Big => 0x345678ab, }; - expect(@ptrCast(*align(1) const u32, bytes[1..5].ptr).* == expected); + expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected); } test "reinterpret bytes of an array into an extern struct" { diff --git a/test/stage1/behavior/slice.zig b/test/stage1/behavior/slice.zig index 9dd57f474e..d58132cb08 100644 --- a/test/stage1/behavior/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -7,10 +7,10 @@ const mem = std.mem; const x = @intToPtr([*]i32, 0x1000)[0..0x500]; const y = x[0x100..]; test "compile time slice of pointer to hard coded address" { - expect(@ptrToInt(x.ptr) == 0x1000); + expect(@ptrToInt(x) == 0x1000); expect(x.len == 0x500); - expect(@ptrToInt(y.ptr) == 0x1100); + expect(@ptrToInt(y) == 0x1100); expect(y.len == 0x400); } @@ -47,7 +47,9 @@ test "C pointer slice access" { var buf: [10]u32 = [1]u32{42} ** 10; const c_ptr = @ptrCast([*c]const u32, &buf); - comptime expectEqual([]const u32, @TypeOf(c_ptr[0..1])); + var runtime_zero: usize = 0; + comptime expectEqual([]const u32, @TypeOf(c_ptr[runtime_zero..1])); + comptime expectEqual(*const [1]u32, @TypeOf(c_ptr[0..1])); for (c_ptr[0..5]) |*cl| { expectEqual(@as(u32, 42), cl.*); @@ -107,7 +109,9 @@ test "obtaining a null terminated slice" { const ptr2 = buf[0..runtime_len :0]; // ptr2 is a null-terminated slice comptime expect(@TypeOf(ptr2) == [:0]u8); - comptime expect(@TypeOf(ptr2[0..2]) == []u8); + comptime expect(@TypeOf(ptr2[0..2]) == *[2]u8); + var runtime_zero: usize = 0; + comptime expect(@TypeOf(ptr2[runtime_zero..2]) == []u8); } test "empty array to slice" { @@ -126,3 +130,150 @@ test "empty array to slice" { S.doTheTest(); comptime S.doTheTest(); } + +test "@ptrCast slice to pointer" { + const S = struct { + fn doTheTest() void { + var array align(@alignOf(u16)) = [5]u8{ 0xff, 0xff, 0xff, 0xff, 0xff }; + var slice: []u8 = &array; + var ptr = @ptrCast(*u16, slice); + expect(ptr.* == 65535); + } + }; + + S.doTheTest(); + comptime S.doTheTest(); +} + +test "slice syntax resulting in pointer-to-array" { + const S = struct { + fn doTheTest() void { + testArray(); + testArrayZ(); + testArray0(); + testArrayAlign(); + testPointer(); + testPointerZ(); + testPointer0(); + testPointerAlign(); + testSlice(); + testSliceZ(); + testSlice0(); + testSliceAlign(); + } + + fn testArray() void { + var array = [5]u8{ 1, 2, 3, 4, 5 }; + var slice = array[1..3]; + comptime expect(@TypeOf(slice) == *[2]u8); + expect(slice[0] == 2); + expect(slice[1] == 3); + } + + fn testArrayZ() void { + var array = [5:0]u8{ 1, 2, 3, 4, 5 }; + comptime expect(@TypeOf(array[1..3]) == *[2]u8); + comptime expect(@TypeOf(array[1..5]) == *[4:0]u8); + comptime expect(@TypeOf(array[1..]) == *[4:0]u8); + comptime expect(@TypeOf(array[1..3 :4]) == *[2:4]u8); + } + + fn testArray0() void { + { + var array = [0]u8{}; + var slice = array[0..0]; + comptime expect(@TypeOf(slice) == *[0]u8); + } + { + var array = [0:0]u8{}; + var slice = array[0..0]; + comptime expect(@TypeOf(slice) == *[0:0]u8); + expect(slice[0] == 0); + } + } + + fn testArrayAlign() void { + var array align(4) = [5]u8{ 1, 2, 3, 4, 5 }; + var slice = array[4..5]; + comptime expect(@TypeOf(slice) == *align(4) [1]u8); + expect(slice[0] == 5); + comptime expect(@TypeOf(array[0..2]) == *align(4) [2]u8); + } + + fn testPointer() void { + var array = [5]u8{ 1, 2, 3, 4, 5 }; + var pointer: [*]u8 = &array; + var slice = pointer[1..3]; + comptime expect(@TypeOf(slice) == *[2]u8); + expect(slice[0] == 2); + expect(slice[1] == 3); + } + + fn testPointerZ() void { + var array = [5:0]u8{ 1, 2, 3, 4, 5 }; + var pointer: [*:0]u8 = &array; + comptime expect(@TypeOf(pointer[1..3]) == *[2]u8); + comptime expect(@TypeOf(pointer[1..3 :4]) == *[2:4]u8); + } + + fn testPointer0() void { + var pointer: [*]u0 = &[1]u0{0}; + var slice = pointer[0..1]; + comptime expect(@TypeOf(slice) == *[1]u0); + expect(slice[0] == 0); + } + + fn testPointerAlign() void { + var array align(4) = [5]u8{ 1, 2, 3, 4, 5 }; + var pointer: [*]align(4) u8 = &array; + var slice = pointer[4..5]; + comptime expect(@TypeOf(slice) == *align(4) [1]u8); + expect(slice[0] == 5); + comptime expect(@TypeOf(pointer[0..2]) == *align(4) [2]u8); + } + + fn testSlice() void { + var array = [5]u8{ 1, 2, 3, 4, 5 }; + var src_slice: []u8 = &array; + var slice = src_slice[1..3]; + comptime expect(@TypeOf(slice) == *[2]u8); + expect(slice[0] == 2); + expect(slice[1] == 3); + } + + fn testSliceZ() void { + var array = [5:0]u8{ 1, 2, 3, 4, 5 }; + var slice: [:0]u8 = &array; + comptime expect(@TypeOf(slice[1..3]) == *[2]u8); + comptime expect(@TypeOf(slice[1..]) == [:0]u8); + comptime expect(@TypeOf(slice[1..3 :4]) == *[2:4]u8); + } + + fn testSlice0() void { + { + var array = [0]u8{}; + var src_slice: []u8 = &array; + var slice = src_slice[0..0]; + comptime expect(@TypeOf(slice) == *[0]u8); + } + { + var array = [0:0]u8{}; + var src_slice: [:0]u8 = &array; + var slice = src_slice[0..0]; + comptime expect(@TypeOf(slice) == *[0]u8); + } + } + + fn testSliceAlign() void { + var array align(4) = [5]u8{ 1, 2, 3, 4, 5 }; + var src_slice: []align(4) u8 = &array; + var slice = src_slice[4..5]; + comptime expect(@TypeOf(slice) == *align(4) [1]u8); + expect(slice[0] == 5); + comptime expect(@TypeOf(src_slice[0..2]) == *align(4) [2]u8); + } + }; + + S.doTheTest(); + comptime S.doTheTest(); +} diff --git a/test/stage1/behavior/struct.zig b/test/stage1/behavior/struct.zig index ecec7fe5d6..0365991ed3 100644 --- a/test/stage1/behavior/struct.zig +++ b/test/stage1/behavior/struct.zig @@ -409,8 +409,8 @@ const Bitfields = packed struct { test "native bit field understands endianness" { var all: u64 = 0x7765443322221111; var bytes: [8]u8 = undefined; - @memcpy(bytes[0..].ptr, @ptrCast([*]u8, &all), 8); - var bitfields = @ptrCast(*Bitfields, bytes[0..].ptr).*; + @memcpy(&bytes, @ptrCast([*]u8, &all), 8); + var bitfields = @ptrCast(*Bitfields, &bytes).*; expect(bitfields.f1 == 0x1111); expect(bitfields.f2 == 0x2222);