commit 53b5aa812bd9c0229054121a1c196c9b18994d64 (tree)
parent 75bda408cd69f3d3b0cdb00caa26eb8cbeab5f3e
Author: Andrew Kelley <andrew@ziglang.org>
Date: Thu, 19 Mar 2020 22:19:24 -0400
Merge remote-tracking branch 'origin/master' into llvm10
Diffstat:
78 files changed, 2332 insertions(+), 1436 deletions(-)
diff --git 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
@@ -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
@@ -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#}
<p>This is one reason we prefer slices to pointers.</p>
{#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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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();
-}
-
-/// 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();
+/// 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);
}
-/// 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);
- }
-
- /// Deprecated; call `openDirListC` directly.
- pub fn openDirC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir {
- return self.openDirListC(sub_path_c);
- }
+ 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,
- /// 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.
- ///
- /// Asserts that the path parameter has no null bytes.
- pub fn openDirTraverse(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.openDirTraverseW(&sub_path_w);
- }
-
- const sub_path_c = try os.toPosixPath(sub_path);
- return self.openDirTraverseC(&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 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.
+ /// 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 openDirList(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.openDirListW(&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.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);
+ 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_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC);
}
}
- /// 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);
- } else {
- return self.openDirFlagsC(sub_path_c, 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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| {
- if (maybe_label) |label| {
- return label.lastToken();
- }
- },
- Kind.Continue => |maybe_label| {
+ .Break,
+ .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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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, "<stdin>");
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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<LazyValueArrayType *>(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<LazyValueArrayType *>(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<ZigValue>(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
@@ -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
@@ -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
@@ -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<ZigValue>();
+ 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<IrInstGenSlice>(
&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<ZigValue>();
+ // 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<ZigValue>();
+ 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
@@ -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<CFile *> 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);