authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-19 22:19:24-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-03-19 22:19:24-04:00
log53b5aa812bd9c0229054121a1c196c9b18994d64
tree3c2961c8b70690351a25d3cee22fd52bbb11a60f
parent75bda408cd69f3d3b0cdb00caa26eb8cbeab5f3e
parent28a6c136e9dc9bcf3e04ab0aa38edc21918c78b9
signaturelock-open Commit is signed but in an unrecognized format.

Merge remote-tracking branch 'origin/master' into llvm10


78 files changed, 2332 insertions(+), 1436 deletions(-)

build.zig+9-5
......@@ -305,10 +305,14 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
305305 dependOnLib(b, exe, ctx.llvm);
306306
307307 if (exe.target.getOsTag() == .linux) {
308 try addCxxKnownPath(b, ctx, exe, "libstdc++.a",
309 \\Unable to determine path to libstdc++.a
310 \\On Fedora, install libstdc++-static and try again.
311 );
308 // First we try to static link against gcc libstdc++. If that doesn't work,
309 // we fall back to -lc++ and cross our fingers.
310 addCxxKnownPath(b, ctx, exe, "libstdc++.a", "") catch |err| switch (err) {
311 error.RequiredLibraryNotFound => {
312 exe.linkSystemLibrary("c++");
313 },
314 else => |e| return e,
315 };
312316
313317 exe.linkSystemLibrary("pthread");
314318 } else if (exe.target.isFreeBSD()) {
......@@ -327,7 +331,7 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
327331 // System compiler, not gcc.
328332 exe.linkSystemLibrary("c++");
329333 },
330 else => return err,
334 else => |e| return e,
331335 }
332336 }
333337
doc/docgen.zig+19-3
......@@ -48,7 +48,7 @@ pub fn main() !void {
4848 var toc = try genToc(allocator, &tokenizer);
4949
5050 try fs.cwd().makePath(tmp_dir_name);
51 defer fs.deleteTree(tmp_dir_name) catch {};
51 defer fs.cwd().deleteTree(tmp_dir_name) catch {};
5252
5353 try genHtml(allocator, &tokenizer, &toc, buffered_out_stream.outStream(), zig_exe);
5454 try buffered_out_stream.flush();
......@@ -1096,6 +1096,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
10961096 try build_args.append("-lc");
10971097 try out.print(" -lc", .{});
10981098 }
1099 const target = try std.zig.CrossTarget.parse(.{
1100 .arch_os_abi = code.target_str orelse "native",
1101 });
10991102 if (code.target_str) |triple| {
11001103 try build_args.appendSlice(&[_][]const u8{ "-target", triple });
11011104 if (!code.is_inline) {
......@@ -1150,7 +1153,15 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
11501153 }
11511154 }
11521155
1153 const path_to_exe = mem.trim(u8, exec_result.stdout, " \r\n");
1156 const path_to_exe_dir = mem.trim(u8, exec_result.stdout, " \r\n");
1157 const path_to_exe_basename = try std.fmt.allocPrint(allocator, "{}{}", .{
1158 code.name,
1159 target.exeFileExt(),
1160 });
1161 const path_to_exe = try fs.path.join(allocator, &[_][]const u8{
1162 path_to_exe_dir,
1163 path_to_exe_basename,
1164 });
11541165 const run_args = &[_][]const u8{path_to_exe};
11551166
11561167 var exited_with_signal = false;
......@@ -1486,7 +1497,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
14861497}
14871498
14881499fn exec(allocator: *mem.Allocator, env_map: *std.BufMap, args: []const []const u8) !ChildProcess.ExecResult {
1489 const result = try ChildProcess.exec(allocator, args, null, env_map, max_doc_file_size);
1500 const result = try ChildProcess.exec2(.{
1501 .allocator = allocator,
1502 .argv = args,
1503 .env_map = env_map,
1504 .max_output_bytes = max_doc_file_size,
1505 });
14901506 switch (result.term) {
14911507 .Exited => |exit_code| {
14921508 if (exit_code != 0) {
doc/langref.html.in+16-20
......@@ -2093,8 +2093,9 @@ var foo: u8 align(4) = 100;
20932093test "global variable alignment" {
20942094 assert(@TypeOf(&foo).alignment == 4);
20952095 assert(@TypeOf(&foo) == *align(4) u8);
2096 const slice = @as(*[1]u8, &foo)[0..];
2097 assert(@TypeOf(slice) == []align(4) u8);
2096 const as_pointer_to_array: *[1]u8 = &foo;
2097 const as_slice: []u8 = as_pointer_to_array;
2098 assert(@TypeOf(as_slice) == []align(4) u8);
20982099}
20992100
21002101fn derp() align(@sizeOf(usize) * 2) i32 { return 1234; }
......@@ -2187,7 +2188,8 @@ test "basic slices" {
21872188 // a slice is that the array's length is part of the type and known at
21882189 // compile-time, whereas the slice's length is known at runtime.
21892190 // Both can be accessed with the `len` field.
2190 const slice = array[0..array.len];
2191 var known_at_runtime_zero: usize = 0;
2192 const slice = array[known_at_runtime_zero..array.len];
21912193 assert(&slice[0] == &array[0]);
21922194 assert(slice.len == array.len);
21932195
......@@ -2207,13 +2209,15 @@ test "basic slices" {
22072209 {#code_end#}
22082210 <p>This is one reason we prefer slices to pointers.</p>
22092211 {#code_begin|test|slices#}
2210const assert = @import("std").debug.assert;
2211const mem = @import("std").mem;
2212const fmt = @import("std").fmt;
2212const std = @import("std");
2213const assert = std.debug.assert;
2214const mem = std.mem;
2215const fmt = std.fmt;
22132216
22142217test "using slices for strings" {
2215 // Zig has no concept of strings. String literals are arrays of u8, and
2216 // in general the string type is []u8 (slice of u8).
2218 // Zig has no concept of strings. String literals are const pointers to
2219 // arrays of u8, and by convention parameters that are "strings" are
2220 // expected to be UTF-8 encoded slices of u8.
22172221 // Here we coerce [5]u8 to []const u8
22182222 const hello: []const u8 = "hello";
22192223 const world: []const u8 = "世界";
......@@ -2222,7 +2226,7 @@ test "using slices for strings" {
22222226 // You can use slice syntax on an array to convert an array into a slice.
22232227 const all_together_slice = all_together[0..];
22242228 // String concatenation example.
2225 const hello_world = try fmt.bufPrint(all_together_slice, "{} {}", .{hello, world});
2229 const hello_world = try fmt.bufPrint(all_together_slice, "{} {}", .{ hello, world });
22262230
22272231 // Generally, you can use UTF-8 and not worry about whether something is a
22282232 // string. If you don't need to deal with individual characters, no need
......@@ -2239,23 +2243,15 @@ test "slice pointer" {
22392243 slice[2] = 3;
22402244 assert(slice[2] == 3);
22412245 // The slice is mutable because we sliced a mutable pointer.
2242 assert(@TypeOf(slice) == []u8);
2246 // Furthermore, it is actually a pointer to an array, since the start
2247 // and end indexes were both comptime-known.
2248 assert(@TypeOf(slice) == *[5]u8);
22432249
22442250 // You can also slice a slice:
22452251 const slice2 = slice[2..3];
22462252 assert(slice2.len == 1);
22472253 assert(slice2[0] == 3);
22482254}
2249
2250test "slice widening" {
2251 // Zig supports slice widening and slice narrowing. Cast a slice of u8
2252 // to a slice of anything else, and Zig will perform the length conversion.
2253 const array align(@alignOf(u32)) = [_]u8{ 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13 };
2254 const slice = mem.bytesAsSlice(u32, array[0..]);
2255 assert(slice.len == 2);
2256 assert(slice[0] == 0x12121212);
2257 assert(slice[1] == 0x13131313);
2258}
22592255 {#code_end#}
22602256 {#see_also|Pointers|for|Arrays#}
22612257
lib/std/build.zig+26-12
......@@ -377,7 +377,7 @@ pub const Builder = struct {
377377 if (self.verbose) {
378378 warn("rm {}\n", .{full_path});
379379 }
380 fs.deleteTree(full_path) catch {};
380 fs.cwd().deleteTree(full_path) catch {};
381381 }
382382
383383 // TODO remove empty directories
......@@ -847,7 +847,8 @@ pub const Builder = struct {
847847 if (self.verbose) {
848848 warn("cp {} {} ", .{ source_path, dest_path });
849849 }
850 const prev_status = try fs.updateFile(source_path, dest_path);
850 const cwd = fs.cwd();
851 const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
851852 if (self.verbose) switch (prev_status) {
852853 .stale => warn("# installed\n", .{}),
853854 .fresh => warn("# up-to-date\n", .{}),
......@@ -1157,8 +1158,14 @@ pub const LibExeObjStep = struct {
11571158
11581159 valgrind_support: ?bool = null,
11591160
1161 /// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
1162 /// file.
11601163 link_eh_frame_hdr: bool = false,
11611164
1165 /// Place every function in its own section so that unused ones may be
1166 /// safely garbage-collected during the linking phase.
1167 link_function_sections: bool = false,
1168
11621169 /// Uses system Wine installation to run cross compiled Windows build artifacts.
11631170 enable_wine: bool = false,
11641171
......@@ -1884,7 +1891,9 @@ pub const LibExeObjStep = struct {
18841891 if (self.link_eh_frame_hdr) {
18851892 try zig_args.append("--eh-frame-hdr");
18861893 }
1887
1894 if (self.link_function_sections) {
1895 try zig_args.append("-ffunction-sections");
1896 }
18881897 if (self.single_threaded) {
18891898 try zig_args.append("--single-threaded");
18901899 }
......@@ -2144,17 +2153,22 @@ pub const LibExeObjStep = struct {
21442153 try zig_args.append("--cache");
21452154 try zig_args.append("on");
21462155
2147 const output_path_nl = try builder.execFromStep(zig_args.toSliceConst(), &self.step);
2148 const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
2156 const output_dir_nl = try builder.execFromStep(zig_args.toSliceConst(), &self.step);
2157 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
21492158
21502159 if (self.output_dir) |output_dir| {
2151 const full_dest = try fs.path.join(builder.allocator, &[_][]const u8{
2152 output_dir,
2153 fs.path.basename(output_path),
2154 });
2155 try builder.updateFile(output_path, full_dest);
2160 var src_dir = try std.fs.cwd().openDir(build_output_dir, .{ .iterate = true });
2161 defer src_dir.close();
2162
2163 var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
2164 defer dest_dir.close();
2165
2166 var it = src_dir.iterate();
2167 while (try it.next()) |entry| {
2168 _ = try src_dir.updateFile(entry.name, dest_dir, entry.name, .{});
2169 }
21562170 } else {
2157 self.output_dir = fs.path.dirname(output_path).?;
2171 self.output_dir = build_output_dir;
21582172 }
21592173 }
21602174
......@@ -2352,7 +2366,7 @@ pub const RemoveDirStep = struct {
23522366 const self = @fieldParentPtr(RemoveDirStep, "step", step);
23532367
23542368 const full_path = self.builder.pathFromRoot(self.dir_path);
2355 fs.deleteTree(full_path) catch |err| {
2369 fs.cwd().deleteTree(full_path) catch |err| {
23562370 warn("Unable to remove {}: {}\n", .{ full_path, @errorName(err) });
23572371 return err;
23582372 };
lib/std/build/run.zig+3-1
......@@ -29,6 +29,8 @@ pub const RunStep = struct {
2929 stdout_action: StdIoAction = .inherit,
3030 stderr_action: StdIoAction = .inherit,
3131
32 stdin_behavior: std.ChildProcess.StdIo = .Inherit,
33
3234 expected_exit_code: u8 = 0,
3335
3436 pub const StdIoAction = union(enum) {
......@@ -159,7 +161,7 @@ pub const RunStep = struct {
159161 child.cwd = cwd;
160162 child.env_map = self.env_map orelse self.builder.env_map;
161163
162 child.stdin_behavior = .Ignore;
164 child.stdin_behavior = self.stdin_behavior;
163165 child.stdout_behavior = stdIoActionToBehavior(self.stdout_action);
164166 child.stderr_behavior = stdIoActionToBehavior(self.stderr_action);
165167
lib/std/build/write_file.zig+1-1
......@@ -78,7 +78,7 @@ pub const WriteFileStep = struct {
7878 warn("unable to make path {}: {}\n", .{ self.output_dir, @errorName(err) });
7979 return err;
8080 };
81 var dir = try fs.cwd().openDirTraverse(self.output_dir);
81 var dir = try fs.cwd().openDir(self.output_dir, .{});
8282 defer dir.close();
8383 for (self.files.toSliceConst()) |file| {
8484 dir.writeFile(file.basename, file.bytes) catch |err| {
lib/std/c.zig+1
......@@ -106,6 +106,7 @@ pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int;
106106pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: u32) c_int;
107107pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int;
108108pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int;
109pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, new: [*:0]const u8) c_int;
109110pub extern "c" fn chdir(path: [*:0]const u8) c_int;
110111pub extern "c" fn fchdir(fd: fd_t) c_int;
111112pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int;
lib/std/crypto/aes.zig+19-19
......@@ -15,10 +15,10 @@ fn rotw(w: u32) u32 {
1515
1616// Encrypt one block from src into dst, using the expanded key xk.
1717fn encryptBlock(xk: []const u32, dst: []u8, src: []const u8) void {
18 var s0 = mem.readIntSliceBig(u32, src[0..4]);
19 var s1 = mem.readIntSliceBig(u32, src[4..8]);
20 var s2 = mem.readIntSliceBig(u32, src[8..12]);
21 var s3 = mem.readIntSliceBig(u32, src[12..16]);
18 var s0 = mem.readIntBig(u32, src[0..4]);
19 var s1 = mem.readIntBig(u32, src[4..8]);
20 var s2 = mem.readIntBig(u32, src[8..12]);
21 var s3 = mem.readIntBig(u32, src[12..16]);
2222
2323 // First round just XORs input with key.
2424 s0 ^= xk[0];
......@@ -58,18 +58,18 @@ fn encryptBlock(xk: []const u32, dst: []u8, src: []const u8) void {
5858 s2 ^= xk[k + 2];
5959 s3 ^= xk[k + 3];
6060
61 mem.writeIntSliceBig(u32, dst[0..4], s0);
62 mem.writeIntSliceBig(u32, dst[4..8], s1);
63 mem.writeIntSliceBig(u32, dst[8..12], s2);
64 mem.writeIntSliceBig(u32, dst[12..16], s3);
61 mem.writeIntBig(u32, dst[0..4], s0);
62 mem.writeIntBig(u32, dst[4..8], s1);
63 mem.writeIntBig(u32, dst[8..12], s2);
64 mem.writeIntBig(u32, dst[12..16], s3);
6565}
6666
6767// Decrypt one block from src into dst, using the expanded key xk.
6868pub fn decryptBlock(xk: []const u32, dst: []u8, src: []const u8) void {
69 var s0 = mem.readIntSliceBig(u32, src[0..4]);
70 var s1 = mem.readIntSliceBig(u32, src[4..8]);
71 var s2 = mem.readIntSliceBig(u32, src[8..12]);
72 var s3 = mem.readIntSliceBig(u32, src[12..16]);
69 var s0 = mem.readIntBig(u32, src[0..4]);
70 var s1 = mem.readIntBig(u32, src[4..8]);
71 var s2 = mem.readIntBig(u32, src[8..12]);
72 var s3 = mem.readIntBig(u32, src[12..16]);
7373
7474 // First round just XORs input with key.
7575 s0 ^= xk[0];
......@@ -109,10 +109,10 @@ pub fn decryptBlock(xk: []const u32, dst: []u8, src: []const u8) void {
109109 s2 ^= xk[k + 2];
110110 s3 ^= xk[k + 3];
111111
112 mem.writeIntSliceBig(u32, dst[0..4], s0);
113 mem.writeIntSliceBig(u32, dst[4..8], s1);
114 mem.writeIntSliceBig(u32, dst[8..12], s2);
115 mem.writeIntSliceBig(u32, dst[12..16], s3);
112 mem.writeIntBig(u32, dst[0..4], s0);
113 mem.writeIntBig(u32, dst[4..8], s1);
114 mem.writeIntBig(u32, dst[8..12], s2);
115 mem.writeIntBig(u32, dst[12..16], s3);
116116}
117117
118118fn xorBytes(dst: []u8, a: []const u8, b: []const u8) usize {
......@@ -154,8 +154,8 @@ fn AES(comptime keysize: usize) type {
154154 var n: usize = 0;
155155 while (n < src.len) {
156156 ctx.encrypt(keystream[0..], ctrbuf[0..]);
157 var ctr_i = std.mem.readIntSliceBig(u128, ctrbuf[0..]);
158 std.mem.writeIntSliceBig(u128, ctrbuf[0..], ctr_i +% 1);
157 var ctr_i = std.mem.readIntBig(u128, ctrbuf[0..]);
158 std.mem.writeIntBig(u128, ctrbuf[0..], ctr_i +% 1);
159159
160160 n += xorBytes(dst[n..], src[n..], &keystream);
161161 }
......@@ -251,7 +251,7 @@ fn expandKey(key: []const u8, enc: []u32, dec: []u32) void {
251251 var i: usize = 0;
252252 var nk = key.len / 4;
253253 while (i < nk) : (i += 1) {
254 enc[i] = mem.readIntSliceBig(u32, key[4 * i .. 4 * i + 4]);
254 enc[i] = mem.readIntBig(u32, key[4 * i ..][0..4]);
255255 }
256256 while (i < enc.len) : (i += 1) {
257257 var t = enc[i - 1];
lib/std/crypto/blake2.zig+4-7
......@@ -123,8 +123,7 @@ fn Blake2s(comptime out_len: usize) type {
123123 const rr = d.h[0 .. out_len / 32];
124124
125125 for (rr) |s, j| {
126 // TODO https://github.com/ziglang/zig/issues/863
127 mem.writeIntSliceLittle(u32, out[4 * j .. 4 * j + 4], s);
126 mem.writeIntLittle(u32, out[4 * j ..][0..4], s);
128127 }
129128 }
130129
......@@ -135,8 +134,7 @@ fn Blake2s(comptime out_len: usize) type {
135134 var v: [16]u32 = undefined;
136135
137136 for (m) |*r, i| {
138 // TODO https://github.com/ziglang/zig/issues/863
139 r.* = mem.readIntSliceLittle(u32, b[4 * i .. 4 * i + 4]);
137 r.* = mem.readIntLittle(u32, b[4 * i ..][0..4]);
140138 }
141139
142140 var k: usize = 0;
......@@ -358,8 +356,7 @@ fn Blake2b(comptime out_len: usize) type {
358356 const rr = d.h[0 .. out_len / 64];
359357
360358 for (rr) |s, j| {
361 // TODO https://github.com/ziglang/zig/issues/863
362 mem.writeIntSliceLittle(u64, out[8 * j .. 8 * j + 8], s);
359 mem.writeIntLittle(u64, out[8 * j ..][0..8], s);
363360 }
364361 }
365362
......@@ -370,7 +367,7 @@ fn Blake2b(comptime out_len: usize) type {
370367 var v: [16]u64 = undefined;
371368
372369 for (m) |*r, i| {
373 r.* = mem.readIntSliceLittle(u64, b[8 * i .. 8 * i + 8]);
370 r.* = mem.readIntLittle(u64, b[8 * i ..][0..8]);
374371 }
375372
376373 var k: usize = 0;
lib/std/crypto/chacha20.zig+30-31
......@@ -61,8 +61,7 @@ fn salsa20_wordtobyte(out: []u8, input: [16]u32) void {
6161 }
6262
6363 for (x) |_, i| {
64 // TODO https://github.com/ziglang/zig/issues/863
65 mem.writeIntSliceLittle(u32, out[4 * i .. 4 * i + 4], x[i] +% input[i]);
64 mem.writeIntLittle(u32, out[4 * i ..][0..4], x[i] +% input[i]);
6665 }
6766}
6867
......@@ -73,10 +72,10 @@ fn chaCha20_internal(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) vo
7372
7473 const c = "expand 32-byte k";
7574 const constant_le = [_]u32{
76 mem.readIntSliceLittle(u32, c[0..4]),
77 mem.readIntSliceLittle(u32, c[4..8]),
78 mem.readIntSliceLittle(u32, c[8..12]),
79 mem.readIntSliceLittle(u32, c[12..16]),
75 mem.readIntLittle(u32, c[0..4]),
76 mem.readIntLittle(u32, c[4..8]),
77 mem.readIntLittle(u32, c[8..12]),
78 mem.readIntLittle(u32, c[12..16]),
8079 };
8180
8281 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:
120119 var k: [8]u32 = undefined;
121120 var c: [4]u32 = undefined;
122121
123 k[0] = mem.readIntSliceLittle(u32, key[0..4]);
124 k[1] = mem.readIntSliceLittle(u32, key[4..8]);
125 k[2] = mem.readIntSliceLittle(u32, key[8..12]);
126 k[3] = mem.readIntSliceLittle(u32, key[12..16]);
127 k[4] = mem.readIntSliceLittle(u32, key[16..20]);
128 k[5] = mem.readIntSliceLittle(u32, key[20..24]);
129 k[6] = mem.readIntSliceLittle(u32, key[24..28]);
130 k[7] = mem.readIntSliceLittle(u32, key[28..32]);
122 k[0] = mem.readIntLittle(u32, key[0..4]);
123 k[1] = mem.readIntLittle(u32, key[4..8]);
124 k[2] = mem.readIntLittle(u32, key[8..12]);
125 k[3] = mem.readIntLittle(u32, key[12..16]);
126 k[4] = mem.readIntLittle(u32, key[16..20]);
127 k[5] = mem.readIntLittle(u32, key[20..24]);
128 k[6] = mem.readIntLittle(u32, key[24..28]);
129 k[7] = mem.readIntLittle(u32, key[28..32]);
131130
132131 c[0] = counter;
133 c[1] = mem.readIntSliceLittle(u32, nonce[0..4]);
134 c[2] = mem.readIntSliceLittle(u32, nonce[4..8]);
135 c[3] = mem.readIntSliceLittle(u32, nonce[8..12]);
132 c[1] = mem.readIntLittle(u32, nonce[0..4]);
133 c[2] = mem.readIntLittle(u32, nonce[4..8]);
134 c[3] = mem.readIntLittle(u32, nonce[8..12]);
136135 chaCha20_internal(out, in, k, c);
137136}
138137
......@@ -147,19 +146,19 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]
147146 var k: [8]u32 = undefined;
148147 var c: [4]u32 = undefined;
149148
150 k[0] = mem.readIntSliceLittle(u32, key[0..4]);
151 k[1] = mem.readIntSliceLittle(u32, key[4..8]);
152 k[2] = mem.readIntSliceLittle(u32, key[8..12]);
153 k[3] = mem.readIntSliceLittle(u32, key[12..16]);
154 k[4] = mem.readIntSliceLittle(u32, key[16..20]);
155 k[5] = mem.readIntSliceLittle(u32, key[20..24]);
156 k[6] = mem.readIntSliceLittle(u32, key[24..28]);
157 k[7] = mem.readIntSliceLittle(u32, key[28..32]);
149 k[0] = mem.readIntLittle(u32, key[0..4]);
150 k[1] = mem.readIntLittle(u32, key[4..8]);
151 k[2] = mem.readIntLittle(u32, key[8..12]);
152 k[3] = mem.readIntLittle(u32, key[12..16]);
153 k[4] = mem.readIntLittle(u32, key[16..20]);
154 k[5] = mem.readIntLittle(u32, key[20..24]);
155 k[6] = mem.readIntLittle(u32, key[24..28]);
156 k[7] = mem.readIntLittle(u32, key[28..32]);
158157
159158 c[0] = @truncate(u32, counter);
160159 c[1] = @truncate(u32, counter >> 32);
161 c[2] = mem.readIntSliceLittle(u32, nonce[0..4]);
162 c[3] = mem.readIntSliceLittle(u32, nonce[4..8]);
160 c[2] = mem.readIntLittle(u32, nonce[0..4]);
161 c[3] = mem.readIntLittle(u32, nonce[4..8]);
163162
164163 const block_size = (1 << 6);
165164 // 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,
463462 mac.update(zeros[0..padding]);
464463 }
465464 var lens: [16]u8 = undefined;
466 mem.writeIntSliceLittle(u64, lens[0..8], data.len);
467 mem.writeIntSliceLittle(u64, lens[8..16], plaintext.len);
465 mem.writeIntLittle(u64, lens[0..8], data.len);
466 mem.writeIntLittle(u64, lens[8..16], plaintext.len);
468467 mac.update(lens[0..]);
469468 mac.final(dst[plaintext.len..]);
470469}
......@@ -500,8 +499,8 @@ pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []const u8, data: []const u8,
500499 mac.update(zeros[0..padding]);
501500 }
502501 var lens: [16]u8 = undefined;
503 mem.writeIntSliceLittle(u64, lens[0..8], data.len);
504 mem.writeIntSliceLittle(u64, lens[8..16], ciphertext.len);
502 mem.writeIntLittle(u64, lens[0..8], data.len);
503 mem.writeIntLittle(u64, lens[8..16], ciphertext.len);
505504 mac.update(lens[0..]);
506505 var computedTag: [16]u8 = undefined;
507506 mac.final(computedTag[0..]);
lib/std/crypto/md5.zig+1-2
......@@ -112,8 +112,7 @@ pub const Md5 = struct {
112112 d.round(d.buf[0..]);
113113
114114 for (d.s) |s, j| {
115 // TODO https://github.com/ziglang/zig/issues/863
116 mem.writeIntSliceLittle(u32, out[4 * j .. 4 * j + 4], s);
115 mem.writeIntLittle(u32, out[4 * j ..][0..4], s);
117116 }
118117 }
119118
lib/std/crypto/poly1305.zig+14-15
......@@ -3,11 +3,11 @@
33// https://monocypher.org/
44
55const std = @import("../std.zig");
6const builtin = @import("builtin");
6const builtin = std.builtin;
77
88const Endian = builtin.Endian;
9const readIntSliceLittle = std.mem.readIntSliceLittle;
10const writeIntSliceLittle = std.mem.writeIntSliceLittle;
9const readIntLittle = std.mem.readIntLittle;
10const writeIntLittle = std.mem.writeIntLittle;
1111
1212pub const Poly1305 = struct {
1313 const Self = @This();
......@@ -59,19 +59,19 @@ pub const Poly1305 = struct {
5959 {
6060 var i: usize = 0;
6161 while (i < 1) : (i += 1) {
62 ctx.r[0] = readIntSliceLittle(u32, key[0..4]) & 0x0fffffff;
62 ctx.r[0] = readIntLittle(u32, key[0..4]) & 0x0fffffff;
6363 }
6464 }
6565 {
6666 var i: usize = 1;
6767 while (i < 4) : (i += 1) {
68 ctx.r[i] = readIntSliceLittle(u32, key[i * 4 .. i * 4 + 4]) & 0x0ffffffc;
68 ctx.r[i] = readIntLittle(u32, key[i * 4 ..][0..4]) & 0x0ffffffc;
6969 }
7070 }
7171 {
7272 var i: usize = 0;
7373 while (i < 4) : (i += 1) {
74 ctx.pad[i] = readIntSliceLittle(u32, key[i * 4 + 16 .. i * 4 + 16 + 4]);
74 ctx.pad[i] = readIntLittle(u32, key[i * 4 + 16 ..][0..4]);
7575 }
7676 }
7777
......@@ -168,10 +168,10 @@ pub const Poly1305 = struct {
168168 const nb_blocks = nmsg.len >> 4;
169169 var i: usize = 0;
170170 while (i < nb_blocks) : (i += 1) {
171 ctx.c[0] = readIntSliceLittle(u32, nmsg[0..4]);
172 ctx.c[1] = readIntSliceLittle(u32, nmsg[4..8]);
173 ctx.c[2] = readIntSliceLittle(u32, nmsg[8..12]);
174 ctx.c[3] = readIntSliceLittle(u32, nmsg[12..16]);
171 ctx.c[0] = readIntLittle(u32, nmsg[0..4]);
172 ctx.c[1] = readIntLittle(u32, nmsg[4..8]);
173 ctx.c[2] = readIntLittle(u32, nmsg[8..12]);
174 ctx.c[3] = readIntLittle(u32, nmsg[12..16]);
175175 polyBlock(ctx);
176176 nmsg = nmsg[16..];
177177 }
......@@ -210,11 +210,10 @@ pub const Poly1305 = struct {
210210 const uu2 = (uu1 >> 32) + ctx.h[2] + ctx.pad[2]; // <= 2_00000000
211211 const uu3 = (uu2 >> 32) + ctx.h[3] + ctx.pad[3]; // <= 2_00000000
212212
213 // TODO https://github.com/ziglang/zig/issues/863
214 writeIntSliceLittle(u32, out[0..], @truncate(u32, uu0));
215 writeIntSliceLittle(u32, out[4..], @truncate(u32, uu1));
216 writeIntSliceLittle(u32, out[8..], @truncate(u32, uu2));
217 writeIntSliceLittle(u32, out[12..], @truncate(u32, uu3));
213 writeIntLittle(u32, out[0..4], @truncate(u32, uu0));
214 writeIntLittle(u32, out[4..8], @truncate(u32, uu1));
215 writeIntLittle(u32, out[8..12], @truncate(u32, uu2));
216 writeIntLittle(u32, out[12..16], @truncate(u32, uu3));
218217
219218 ctx.secureZero();
220219 }
lib/std/crypto/sha1.zig+1-2
......@@ -109,8 +109,7 @@ pub const Sha1 = struct {
109109 d.round(d.buf[0..]);
110110
111111 for (d.s) |s, j| {
112 // TODO https://github.com/ziglang/zig/issues/863
113 mem.writeIntSliceBig(u32, out[4 * j .. 4 * j + 4], s);
112 mem.writeIntBig(u32, out[4 * j ..][0..4], s);
114113 }
115114 }
116115
lib/std/crypto/sha2.zig+2-4
......@@ -167,8 +167,7 @@ fn Sha2_32(comptime params: Sha2Params32) type {
167167 const rr = d.s[0 .. params.out_len / 32];
168168
169169 for (rr) |s, j| {
170 // TODO https://github.com/ziglang/zig/issues/863
171 mem.writeIntSliceBig(u32, out[4 * j .. 4 * j + 4], s);
170 mem.writeIntBig(u32, out[4 * j ..][0..4], s);
172171 }
173172 }
174173
......@@ -509,8 +508,7 @@ fn Sha2_64(comptime params: Sha2Params64) type {
509508 const rr = d.s[0 .. params.out_len / 64];
510509
511510 for (rr) |s, j| {
512 // TODO https://github.com/ziglang/zig/issues/863
513 mem.writeIntSliceBig(u64, out[8 * j .. 8 * j + 8], s);
511 mem.writeIntBig(u64, out[8 * j ..][0..8], s);
514512 }
515513 }
516514
lib/std/crypto/sha3.zig+2-3
......@@ -120,7 +120,7 @@ fn keccak_f(comptime F: usize, d: []u8) void {
120120 var c = [_]u64{0} ** 5;
121121
122122 for (s) |*r, i| {
123 r.* = mem.readIntSliceLittle(u64, d[8 * i .. 8 * i + 8]);
123 r.* = mem.readIntLittle(u64, d[8 * i ..][0..8]);
124124 }
125125
126126 comptime var x: usize = 0;
......@@ -167,8 +167,7 @@ fn keccak_f(comptime F: usize, d: []u8) void {
167167 }
168168
169169 for (s) |r, i| {
170 // TODO https://github.com/ziglang/zig/issues/863
171 mem.writeIntSliceLittle(u64, d[8 * i .. 8 * i + 8], r);
170 mem.writeIntLittle(u64, d[8 * i ..][0..8], r);
172171 }
173172}
174173
lib/std/crypto/x25519.zig+20-21
......@@ -7,8 +7,8 @@ const builtin = @import("builtin");
77const fmt = std.fmt;
88
99const Endian = builtin.Endian;
10const readIntSliceLittle = std.mem.readIntSliceLittle;
11const writeIntSliceLittle = std.mem.writeIntSliceLittle;
10const readIntLittle = std.mem.readIntLittle;
11const writeIntLittle = std.mem.writeIntLittle;
1212
1313// Based on Supercop's ref10 implementation.
1414pub const X25519 = struct {
......@@ -255,16 +255,16 @@ const Fe = struct {
255255
256256 var t: [10]i64 = undefined;
257257
258 t[0] = readIntSliceLittle(u32, s[0..4]);
259 t[1] = @as(u32, readIntSliceLittle(u24, s[4..7])) << 6;
260 t[2] = @as(u32, readIntSliceLittle(u24, s[7..10])) << 5;
261 t[3] = @as(u32, readIntSliceLittle(u24, s[10..13])) << 3;
262 t[4] = @as(u32, readIntSliceLittle(u24, s[13..16])) << 2;
263 t[5] = readIntSliceLittle(u32, s[16..20]);
264 t[6] = @as(u32, readIntSliceLittle(u24, s[20..23])) << 7;
265 t[7] = @as(u32, readIntSliceLittle(u24, s[23..26])) << 5;
266 t[8] = @as(u32, readIntSliceLittle(u24, s[26..29])) << 4;
267 t[9] = (@as(u32, readIntSliceLittle(u24, s[29..32])) & 0x7fffff) << 2;
258 t[0] = readIntLittle(u32, s[0..4]);
259 t[1] = @as(u32, readIntLittle(u24, s[4..7])) << 6;
260 t[2] = @as(u32, readIntLittle(u24, s[7..10])) << 5;
261 t[3] = @as(u32, readIntLittle(u24, s[10..13])) << 3;
262 t[4] = @as(u32, readIntLittle(u24, s[13..16])) << 2;
263 t[5] = readIntLittle(u32, s[16..20]);
264 t[6] = @as(u32, readIntLittle(u24, s[20..23])) << 7;
265 t[7] = @as(u32, readIntLittle(u24, s[23..26])) << 5;
266 t[8] = @as(u32, readIntLittle(u24, s[26..29])) << 4;
267 t[9] = (@as(u32, readIntLittle(u24, s[29..32])) & 0x7fffff) << 2;
268268
269269 carry1(h, t[0..]);
270270 }
......@@ -544,15 +544,14 @@ const Fe = struct {
544544 ut[i] = @bitCast(u32, @intCast(i32, t[i]));
545545 }
546546
547 // TODO https://github.com/ziglang/zig/issues/863
548 writeIntSliceLittle(u32, s[0..4], (ut[0] >> 0) | (ut[1] << 26));
549 writeIntSliceLittle(u32, s[4..8], (ut[1] >> 6) | (ut[2] << 19));
550 writeIntSliceLittle(u32, s[8..12], (ut[2] >> 13) | (ut[3] << 13));
551 writeIntSliceLittle(u32, s[12..16], (ut[3] >> 19) | (ut[4] << 6));
552 writeIntSliceLittle(u32, s[16..20], (ut[5] >> 0) | (ut[6] << 25));
553 writeIntSliceLittle(u32, s[20..24], (ut[6] >> 7) | (ut[7] << 19));
554 writeIntSliceLittle(u32, s[24..28], (ut[7] >> 13) | (ut[8] << 12));
555 writeIntSliceLittle(u32, s[28..], (ut[8] >> 20) | (ut[9] << 6));
547 writeIntLittle(u32, s[0..4], (ut[0] >> 0) | (ut[1] << 26));
548 writeIntLittle(u32, s[4..8], (ut[1] >> 6) | (ut[2] << 19));
549 writeIntLittle(u32, s[8..12], (ut[2] >> 13) | (ut[3] << 13));
550 writeIntLittle(u32, s[12..16], (ut[3] >> 19) | (ut[4] << 6));
551 writeIntLittle(u32, s[16..20], (ut[5] >> 0) | (ut[6] << 25));
552 writeIntLittle(u32, s[20..24], (ut[6] >> 7) | (ut[7] << 19));
553 writeIntLittle(u32, s[24..28], (ut[7] >> 13) | (ut[8] << 12));
554 writeIntLittle(u32, s[28..32], (ut[8] >> 20) | (ut[9] << 6));
556555
557556 std.mem.secureZero(i64, t[0..]);
558557 }
lib/std/fmt.zig+2-1
......@@ -1223,7 +1223,8 @@ test "slice" {
12231223 try testFmt("slice: abc\n", "slice: {}\n", .{value});
12241224 }
12251225 {
1226 const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[0..0];
1226 var runtime_zero: usize = 0;
1227 const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[runtime_zero..runtime_zero];
12271228 try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value});
12281229 }
12291230
lib/std/fs.zig+231-294
......@@ -81,134 +81,74 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
8181 }
8282}
8383
84// TODO fix enum literal not casting to error union
85const PrevStatus = enum {
84pub const PrevStatus = enum {
8685 stale,
8786 fresh,
8887};
8988
90pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {
91 return updateFileMode(source_path, dest_path, null);
92}
89pub const CopyFileOptions = struct {
90 /// When this is `null` the mode is copied from the source file.
91 override_mode: ?File.Mode = null,
92};
9393
94/// Check the file size, mtime, and mode of `source_path` and `dest_path`. If they are equal, does nothing.
95/// Otherwise, atomically copies `source_path` to `dest_path`. The destination file gains the mtime,
96/// atime, and mode of the source file so that the next call to `updateFile` will not need a copy.
97/// Returns the previous status of the file before updating.
98/// If any of the directories do not exist for dest_path, they are created.
99/// TODO rework this to integrate with Dir
100pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus {
94/// Same as `Dir.updateFile`, except asserts that both `source_path` and `dest_path`
95/// are absolute. See `Dir.updateFile` for a function that operates on both
96/// absolute and relative paths.
97pub fn updateFileAbsolute(
98 source_path: []const u8,
99 dest_path: []const u8,
100 args: CopyFileOptions,
101) !PrevStatus {
102 assert(path.isAbsolute(source_path));
103 assert(path.isAbsolute(dest_path));
101104 const my_cwd = cwd();
102
103 var src_file = try my_cwd.openFile(source_path, .{});
104 defer src_file.close();
105
106 const src_stat = try src_file.stat();
107 check_dest_stat: {
108 const dest_stat = blk: {
109 var dest_file = my_cwd.openFile(dest_path, .{}) catch |err| switch (err) {
110 error.FileNotFound => break :check_dest_stat,
111 else => |e| return e,
112 };
113 defer dest_file.close();
114
115 break :blk try dest_file.stat();
116 };
117
118 if (src_stat.size == dest_stat.size and
119 src_stat.mtime == dest_stat.mtime and
120 src_stat.mode == dest_stat.mode)
121 {
122 return PrevStatus.fresh;
123 }
124 }
125 const actual_mode = mode orelse src_stat.mode;
126
127 if (path.dirname(dest_path)) |dirname| {
128 try cwd().makePath(dirname);
129 }
130
131 var atomic_file = try AtomicFile.init(dest_path, actual_mode);
132 defer atomic_file.deinit();
133
134 try atomic_file.file.writeFileAll(src_file, .{ .in_len = src_stat.size });
135 try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime);
136 try atomic_file.finish();
137 return PrevStatus.stale;
105 return Dir.updateFile(my_cwd, source_path, my_cwd, dest_path, args);
138106}
139107
140/// Guaranteed to be atomic.
141/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,
142/// there is a possibility of power loss or application termination leaving temporary files present
143/// in the same directory as dest_path.
144/// Destination file will have the same mode as the source file.
145/// TODO rework this to integrate with Dir
146pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
147 var in_file = try cwd().openFile(source_path, .{});
148 defer in_file.close();
149
150 const stat = try in_file.stat();
151
152 var atomic_file = try AtomicFile.init(dest_path, stat.mode);
153 defer atomic_file.deinit();
154
155 try atomic_file.file.writeFileAll(in_file, .{ .in_len = stat.size });
156 return atomic_file.finish();
157}
158
159/// Guaranteed to be atomic.
160/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,
161/// there is a possibility of power loss or application termination leaving temporary files present
162/// in the same directory as dest_path.
163/// TODO rework this to integrate with Dir
164pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
165 var in_file = try cwd().openFile(source_path, .{});
166 defer in_file.close();
167
168 var atomic_file = try AtomicFile.init(dest_path, mode);
169 defer atomic_file.deinit();
170
171 try atomic_file.file.writeFileAll(in_file, .{});
172 return atomic_file.finish();
108/// Same as `Dir.copyFile`, except asserts that both `source_path` and `dest_path`
109/// are absolute. See `Dir.copyFile` for a function that operates on both
110/// absolute and relative paths.
111pub fn copyFileAbsolute(source_path: []const u8, dest_path: []const u8, args: CopyFileOptions) !void {
112 assert(path.isAbsolute(source_path));
113 assert(path.isAbsolute(dest_path));
114 const my_cwd = cwd();
115 return Dir.copyFile(my_cwd, source_path, my_cwd, dest_path, args);
173116}
174117
175/// TODO update this API to avoid a getrandom syscall for every operation. It
176/// should accept a random interface.
177/// TODO rework this to integrate with Dir
118/// TODO update this API to avoid a getrandom syscall for every operation.
178119pub const AtomicFile = struct {
179120 file: File,
180 tmp_path_buf: [MAX_PATH_BYTES]u8,
121 tmp_path_buf: [MAX_PATH_BYTES - 1:0]u8,
181122 dest_path: []const u8,
182 finished: bool,
123 file_open: bool,
124 file_exists: bool,
125 dir: Dir,
183126
184127 const InitError = File.OpenError;
185128
186 /// dest_path must remain valid for the lifetime of AtomicFile
187 /// call finish to atomically replace dest_path with contents
188 pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile {
129 /// TODO rename this. Callers should go through Dir API
130 pub fn init2(dest_path: []const u8, mode: File.Mode, dir: Dir) InitError!AtomicFile {
189131 const dirname = path.dirname(dest_path);
190132 var rand_buf: [12]u8 = undefined;
191133 const dirname_component_len = if (dirname) |d| d.len + 1 else 0;
192134 const encoded_rand_len = comptime base64.Base64Encoder.calcSize(rand_buf.len);
193135 const tmp_path_len = dirname_component_len + encoded_rand_len;
194 var tmp_path_buf: [MAX_PATH_BYTES]u8 = undefined;
195 if (tmp_path_len >= tmp_path_buf.len) return error.NameTooLong;
136 var tmp_path_buf: [MAX_PATH_BYTES - 1:0]u8 = undefined;
137 if (tmp_path_len > tmp_path_buf.len) return error.NameTooLong;
196138
197 if (dirname) |dir| {
198 mem.copy(u8, tmp_path_buf[0..], dir);
199 tmp_path_buf[dir.len] = path.sep;
139 if (dirname) |dn| {
140 mem.copy(u8, tmp_path_buf[0..], dn);
141 tmp_path_buf[dn.len] = path.sep;
200142 }
201143
202144 tmp_path_buf[tmp_path_len] = 0;
203145 const tmp_path_slice = tmp_path_buf[0..tmp_path_len :0];
204146
205 const my_cwd = cwd();
206
207147 while (true) {
208148 try crypto.randomBytes(rand_buf[0..]);
209149 base64_encoder.encode(tmp_path_slice[dirname_component_len..tmp_path_len], &rand_buf);
210150
211 const file = my_cwd.createFileC(
151 const file = dir.createFileC(
212152 tmp_path_slice,
213153 .{ .mode = mode, .exclusive = true },
214154 ) catch |err| switch (err) {
......@@ -220,33 +160,46 @@ pub const AtomicFile = struct {
220160 .file = file,
221161 .tmp_path_buf = tmp_path_buf,
222162 .dest_path = dest_path,
223 .finished = false,
163 .file_open = true,
164 .file_exists = true,
165 .dir = dir,
224166 };
225167 }
226168 }
227169
170 /// Deprecated. Use `Dir.atomicFile`.
171 pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile {
172 return init2(dest_path, mode, cwd());
173 }
174
228175 /// always call deinit, even after successful finish()
229176 pub fn deinit(self: *AtomicFile) void {
230 if (!self.finished) {
177 if (self.file_open) {
231178 self.file.close();
232 cwd().deleteFileC(@ptrCast([*:0]u8, &self.tmp_path_buf)) catch {};
233 self.finished = true;
179 self.file_open = false;
180 }
181 if (self.file_exists) {
182 self.dir.deleteFileC(&self.tmp_path_buf) catch {};
183 self.file_exists = false;
234184 }
185 self.* = undefined;
235186 }
236187
237188 pub fn finish(self: *AtomicFile) !void {
238 assert(!self.finished);
189 assert(self.file_exists);
190 if (self.file_open) {
191 self.file.close();
192 self.file_open = false;
193 }
239194 if (std.Target.current.os.tag == .windows) {
240195 const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_path);
241 const tmp_path_w = try os.windows.cStrToPrefixedFileW(@ptrCast([*:0]u8, &self.tmp_path_buf));
242 self.file.close();
243 self.finished = true;
244 return os.renameW(&tmp_path_w, &dest_path_w);
196 const tmp_path_w = try os.windows.cStrToPrefixedFileW(&self.tmp_path_buf);
197 try os.renameatW(self.dir.fd, &tmp_path_w, self.dir.fd, &dest_path_w, os.windows.TRUE);
198 self.file_exists = false;
245199 } else {
246200 const dest_path_c = try os.toPosixPath(self.dest_path);
247 self.file.close();
248 self.finished = true;
249 return os.renameC(@ptrCast([*:0]u8, &self.tmp_path_buf), &dest_path_c);
201 try os.renameatZ(self.dir.fd, &self.tmp_path_buf, self.dir.fd, &dest_path_c);
202 self.file_exists = false;
250203 }
251204 }
252205};
......@@ -274,44 +227,21 @@ pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
274227 os.windows.CloseHandle(handle);
275228}
276229
277/// Returns `error.DirNotEmpty` if the directory is not empty.
278/// To delete a directory recursively, see `deleteTree`.
230/// Deprecated; use `Dir.deleteDir`.
279231pub fn deleteDir(dir_path: []const u8) !void {
280232 return os.rmdir(dir_path);
281233}
282234
283/// Same as `deleteDir` except the parameter is a null-terminated UTF8-encoded string.
235/// Deprecated; use `Dir.deleteDirC`.
284236pub fn deleteDirC(dir_path: [*:0]const u8) !void {
285237 return os.rmdirC(dir_path);
286238}
287239
288/// Same as `deleteDir` except the parameter is a null-terminated UTF16LE-encoded string.
240/// Deprecated; use `Dir.deleteDirW`.
289241pub fn deleteDirW(dir_path: [*:0]const u16) !void {
290242 return os.rmdirW(dir_path);
291243}
292244
293/// Removes a symlink, file, or directory.
294/// If `full_path` is relative, this is equivalent to `Dir.deleteTree` with the
295/// current working directory as the open directory handle.
296/// If `full_path` is absolute, this is equivalent to `Dir.deleteTree` with the
297/// base directory.
298pub fn deleteTree(full_path: []const u8) !void {
299 if (path.isAbsolute(full_path)) {
300 const dirname = path.dirname(full_path) orelse return error{
301 /// Attempt to remove the root file system path.
302 /// This error is unreachable if `full_path` is relative.
303 CannotDeleteRootDirectory,
304 }.CannotDeleteRootDirectory;
305
306 var dir = try cwd().openDirList(dirname);
307 defer dir.close();
308
309 return dir.deleteTree(path.basename(full_path));
310 } else {
311 return cwd().deleteTree(full_path);
312 }
313}
314
315245pub const Dir = struct {
316246 fd: os.fd_t,
317247
......@@ -368,7 +298,7 @@ pub const Dir = struct {
368298 if (rc == 0) return null;
369299 if (rc < 0) {
370300 switch (os.errno(rc)) {
371 os.EBADF => unreachable,
301 os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability
372302 os.EFAULT => unreachable,
373303 os.ENOTDIR => unreachable,
374304 os.EINVAL => unreachable,
......@@ -411,13 +341,13 @@ pub const Dir = struct {
411341 if (self.index >= self.end_index) {
412342 const rc = os.system.getdirentries(
413343 self.dir.fd,
414 self.buf[0..].ptr,
344 &self.buf,
415345 self.buf.len,
416346 &self.seek,
417347 );
418348 switch (os.errno(rc)) {
419349 0 => {},
420 os.EBADF => unreachable,
350 os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability
421351 os.EFAULT => unreachable,
422352 os.ENOTDIR => unreachable,
423353 os.EINVAL => unreachable,
......@@ -473,7 +403,7 @@ pub const Dir = struct {
473403 const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len);
474404 switch (os.linux.getErrno(rc)) {
475405 0 => {},
476 os.EBADF => unreachable,
406 os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability
477407 os.EFAULT => unreachable,
478408 os.ENOTDIR => unreachable,
479409 os.EINVAL => unreachable,
......@@ -547,7 +477,8 @@ pub const Dir = struct {
547477 self.end_index = io.Information;
548478 switch (rc) {
549479 .SUCCESS => {},
550 .ACCESS_DENIED => return error.AccessDenied,
480 .ACCESS_DENIED => return error.AccessDenied, // Double-check that the Dir was opened with iteration ability
481
551482 else => return w.unexpectedStatus(rc),
552483 }
553484 }
......@@ -625,16 +556,6 @@ pub const Dir = struct {
625556 DeviceBusy,
626557 } || os.UnexpectedError;
627558
628 /// Deprecated; call `cwd().openDirList` directly.
629 pub fn open(dir_path: []const u8) OpenError!Dir {
630 return cwd().openDirList(dir_path);
631 }
632
633 /// Deprecated; call `cwd().openDirListC` directly.
634 pub fn openC(dir_path_c: [*:0]const u8) OpenError!Dir {
635 return cwd().openDirListC(dir_path_c);
636 }
637
638559 pub fn close(self: *Dir) void {
639560 if (need_async_thread) {
640561 std.event.Loop.instance.?.close(self.fd);
......@@ -694,7 +615,10 @@ pub const Dir = struct {
694615 const access_mask = w.SYNCHRONIZE |
695616 (if (flags.read) @as(u32, w.GENERIC_READ) else 0) |
696617 (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0);
697 return self.openFileWindows(sub_path_w, access_mask, w.FILE_OPEN);
618 return @as(File, .{
619 .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, w.FILE_OPEN),
620 .io_mode = .blocking,
621 });
698622 }
699623
700624 /// Creates, opens, or overwrites a file with write access.
......@@ -739,7 +663,10 @@ pub const Dir = struct {
739663 @as(u32, w.FILE_OVERWRITE_IF)
740664 else
741665 @as(u32, w.FILE_OPEN_IF);
742 return self.openFileWindows(sub_path_w, access_mask, creation);
666 return @as(File, .{
667 .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, creation),
668 .io_mode = .blocking,
669 });
743670 }
744671
745672 /// Deprecated; call `openFile` directly.
......@@ -757,72 +684,6 @@ pub const Dir = struct {
757684 return self.openFileW(sub_path, .{});
758685 }
759686
760 pub fn openFileWindows(
761 self: Dir,
762 sub_path_w: [*:0]const u16,
763 access_mask: os.windows.ACCESS_MASK,
764 creation: os.windows.ULONG,
765 ) File.OpenError!File {
766 const w = os.windows;
767
768 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
769 return error.IsDir;
770 }
771 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
772 return error.IsDir;
773 }
774
775 var result = File{
776 .handle = undefined,
777 .io_mode = .blocking,
778 };
779
780 const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) {
781 error.Overflow => return error.NameTooLong,
782 };
783 var nt_name = w.UNICODE_STRING{
784 .Length = path_len_bytes,
785 .MaximumLength = path_len_bytes,
786 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
787 };
788 var attr = w.OBJECT_ATTRIBUTES{
789 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
790 .RootDirectory = if (path.isAbsoluteWindowsW(sub_path_w)) null else self.fd,
791 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
792 .ObjectName = &nt_name,
793 .SecurityDescriptor = null,
794 .SecurityQualityOfService = null,
795 };
796 var io: w.IO_STATUS_BLOCK = undefined;
797 const rc = w.ntdll.NtCreateFile(
798 &result.handle,
799 access_mask,
800 &attr,
801 &io,
802 null,
803 w.FILE_ATTRIBUTE_NORMAL,
804 w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,
805 creation,
806 w.FILE_NON_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT,
807 null,
808 0,
809 );
810 switch (rc) {
811 .SUCCESS => return result,
812 .OBJECT_NAME_INVALID => unreachable,
813 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
814 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
815 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
816 .INVALID_PARAMETER => unreachable,
817 .SHARING_VIOLATION => return error.SharingViolation,
818 .ACCESS_DENIED => return error.AccessDenied,
819 .PIPE_BUSY => return error.PipeBusy,
820 .OBJECT_PATH_SYNTAX_BAD => unreachable,
821 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
822 else => return w.unexpectedStatus(rc),
823 }
824 }
825
826687 pub fn makeDir(self: Dir, sub_path: []const u8) !void {
827688 try os.mkdirat(self.fd, sub_path, default_new_dir_mode);
828689 }
......@@ -881,77 +742,61 @@ pub const Dir = struct {
881742 try os.fchdir(self.fd);
882743 }
883744
884 /// Deprecated; call `openDirList` directly.
885 pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir {
886 return self.openDirList(sub_path);
887 }
888
889 /// Deprecated; call `openDirListC` directly.
890 pub fn openDirC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir {
891 return self.openDirListC(sub_path_c);
892 }
745 pub const OpenDirOptions = struct {
746 /// `true` means the opened directory can be used as the `Dir` parameter
747 /// for functions which operate based on an open directory handle. When `false`,
748 /// such operations are Illegal Behavior.
749 access_sub_paths: bool = true,
893750
894 /// Opens a directory at the given path with the ability to access subpaths
895 /// of the result. Calling `iterate` on the result is illegal behavior; to
896 /// list the contents of a directory, open it with `openDirList`.
897 ///
898 /// Call `close` on the result when done.
899 ///
900 /// Asserts that the path parameter has no null bytes.
901 pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir {
902 if (builtin.os.tag == .windows) {
903 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
904 return self.openDirTraverseW(&sub_path_w);
905 }
906
907 const sub_path_c = try os.toPosixPath(sub_path);
908 return self.openDirTraverseC(&sub_path_c);
909 }
751 /// `true` means the opened directory can be scanned for the files and sub-directories
752 /// of the result. It means the `iterate` function can be called.
753 iterate: bool = false,
754 };
910755
911 /// Opens a directory at the given path with the ability to access subpaths and list contents
912 /// of the result. If the ability to list contents is unneeded, `openDirTraverse` acts the
913 /// same and may be more efficient.
914 ///
915 /// Call `close` on the result when done.
756 /// Opens a directory at the given path. The directory is a system resource that remains
757 /// open until `close` is called on the result.
916758 ///
917759 /// Asserts that the path parameter has no null bytes.
918 pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir {
760 pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir {
919761 if (builtin.os.tag == .windows) {
920762 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
921 return self.openDirListW(&sub_path_w);
763 return self.openDirW(&sub_path_w, args);
764 } else {
765 const sub_path_c = try os.toPosixPath(sub_path);
766 return self.openDirC(&sub_path_c, args);
922767 }
923
924 const sub_path_c = try os.toPosixPath(sub_path);
925 return self.openDirListC(&sub_path_c);
926768 }
927769
928 /// Same as `openDirTraverse` except the parameter is null-terminated.
929 pub fn openDirTraverseC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir {
770 /// Same as `openDir` except the parameter is null-terminated.
771 pub fn openDirC(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir {
930772 if (builtin.os.tag == .windows) {
931773 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
932 return self.openDirTraverseW(&sub_path_w);
933 } else {
774 return self.openDirW(&sub_path_w, args);
775 } else if (!args.iterate) {
934776 const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0;
935 return self.openDirFlagsC(sub_path_c, os.O_RDONLY | os.O_CLOEXEC | O_PATH);
777 return self.openDirFlagsC(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC | O_PATH);
778 } else {
779 return self.openDirFlagsC(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC);
936780 }
937781 }
938782
939 /// Same as `openDirList` except the parameter is null-terminated.
940 pub fn openDirListC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir {
941 if (builtin.os.tag == .windows) {
942 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
943 return self.openDirListW(&sub_path_w);
944 } else {
945 return self.openDirFlagsC(sub_path_c, os.O_RDONLY | os.O_CLOEXEC);
946 }
783 /// Same as `openDir` except the path parameter is WTF-16 encoded, NT-prefixed.
784 /// This function asserts the target OS is Windows.
785 pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions) OpenError!Dir {
786 const w = os.windows;
787 // TODO remove some of these flags if args.access_sub_paths is false
788 const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |
789 w.SYNCHRONIZE | w.FILE_TRAVERSE;
790 const flags: u32 = if (args.iterate) base_flags | w.FILE_LIST_DIRECTORY else base_flags;
791 return self.openDirAccessMaskW(sub_path_w, flags);
947792 }
948793
794 /// `flags` must contain `os.O_DIRECTORY`.
949795 fn openDirFlagsC(self: Dir, sub_path_c: [*:0]const u8, flags: u32) OpenError!Dir {
950 const os_flags = flags | os.O_DIRECTORY;
951796 const result = if (need_async_thread)
952 std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, 0)
797 std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, flags, 0)
953798 else
954 os.openatC(self.fd, sub_path_c, os_flags, 0);
799 os.openatC(self.fd, sub_path_c, flags, 0);
955800 const fd = result catch |err| switch (err) {
956801 error.FileTooBig => unreachable, // can't happen for directories
957802 error.IsDir => unreachable, // we're providing O_DIRECTORY
......@@ -962,22 +807,6 @@ pub const Dir = struct {
962807 return Dir{ .fd = fd };
963808 }
964809
965 /// Same as `openDirTraverse` except the path parameter is UTF16LE, NT-prefixed.
966 /// This function is Windows-only.
967 pub fn openDirTraverseW(self: Dir, sub_path_w: [*:0]const u16) OpenError!Dir {
968 const w = os.windows;
969
970 return self.openDirAccessMaskW(sub_path_w, w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE);
971 }
972
973 /// Same as `openDirList` except the path parameter is UTF16LE, NT-prefixed.
974 /// This function is Windows-only.
975 pub fn openDirListW(self: Dir, sub_path_w: [*:0]const u16) OpenError!Dir {
976 const w = os.windows;
977
978 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);
979 }
980
981810 fn openDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32) OpenError!Dir {
982811 const w = os.windows;
983812
......@@ -1198,7 +1027,7 @@ pub const Dir = struct {
11981027 error.Unexpected,
11991028 => |e| return e,
12001029 }
1201 var dir = self.openDirList(sub_path) catch |err| switch (err) {
1030 var dir = self.openDir(sub_path, .{ .iterate = true }) catch |err| switch (err) {
12021031 error.NotDir => {
12031032 if (got_access_denied) {
12041033 return error.AccessDenied;
......@@ -1231,7 +1060,6 @@ pub const Dir = struct {
12311060
12321061 var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined;
12331062 var dir_name: []const u8 = sub_path;
1234 var parent_dir = self;
12351063
12361064 // Here we must avoid recursion, in order to provide O(1) memory guarantee of this function.
12371065 // 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 {
12631091 => |e| return e,
12641092 }
12651093
1266 const new_dir = dir.openDirList(entry.name) catch |err| switch (err) {
1094 const new_dir = dir.openDir(entry.name, .{ .iterate = true }) catch |err| switch (err) {
12671095 error.NotDir => {
12681096 if (got_access_denied) {
12691097 return error.AccessDenied;
......@@ -1370,9 +1198,96 @@ pub const Dir = struct {
13701198 pub fn accessW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) AccessError!void {
13711199 return os.faccessatW(self.fd, sub_path_w, 0, 0);
13721200 }
1201
1202 /// Check the file size, mtime, and mode of `source_path` and `dest_path`. If they are equal, does nothing.
1203 /// Otherwise, atomically copies `source_path` to `dest_path`. The destination file gains the mtime,
1204 /// atime, and mode of the source file so that the next call to `updateFile` will not need a copy.
1205 /// Returns the previous status of the file before updating.
1206 /// If any of the directories do not exist for dest_path, they are created.
1207 pub fn updateFile(
1208 source_dir: Dir,
1209 source_path: []const u8,
1210 dest_dir: Dir,
1211 dest_path: []const u8,
1212 options: CopyFileOptions,
1213 ) !PrevStatus {
1214 var src_file = try source_dir.openFile(source_path, .{});
1215 defer src_file.close();
1216
1217 const src_stat = try src_file.stat();
1218 const actual_mode = options.override_mode orelse src_stat.mode;
1219 check_dest_stat: {
1220 const dest_stat = blk: {
1221 var dest_file = dest_dir.openFile(dest_path, .{}) catch |err| switch (err) {
1222 error.FileNotFound => break :check_dest_stat,
1223 else => |e| return e,
1224 };
1225 defer dest_file.close();
1226
1227 break :blk try dest_file.stat();
1228 };
1229
1230 if (src_stat.size == dest_stat.size and
1231 src_stat.mtime == dest_stat.mtime and
1232 actual_mode == dest_stat.mode)
1233 {
1234 return PrevStatus.fresh;
1235 }
1236 }
1237
1238 if (path.dirname(dest_path)) |dirname| {
1239 try dest_dir.makePath(dirname);
1240 }
1241
1242 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = actual_mode });
1243 defer atomic_file.deinit();
1244
1245 try atomic_file.file.writeFileAll(src_file, .{ .in_len = src_stat.size });
1246 try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime);
1247 try atomic_file.finish();
1248 return PrevStatus.stale;
1249 }
1250
1251 /// Guaranteed to be atomic.
1252 /// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,
1253 /// there is a possibility of power loss or application termination leaving temporary files present
1254 /// in the same directory as dest_path.
1255 pub fn copyFile(
1256 source_dir: Dir,
1257 source_path: []const u8,
1258 dest_dir: Dir,
1259 dest_path: []const u8,
1260 options: CopyFileOptions,
1261 ) !void {
1262 var in_file = try source_dir.openFile(source_path, .{});
1263 defer in_file.close();
1264
1265 var size: ?u64 = null;
1266 const mode = options.override_mode orelse blk: {
1267 const stat = try in_file.stat();
1268 size = stat.size;
1269 break :blk stat.mode;
1270 };
1271
1272 var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });
1273 defer atomic_file.deinit();
1274
1275 try atomic_file.file.writeFileAll(in_file, .{ .in_len = size });
1276 return atomic_file.finish();
1277 }
1278
1279 pub const AtomicFileOptions = struct {
1280 mode: File.Mode = File.default_mode,
1281 };
1282
1283 /// `dest_path` must remain valid for the lifetime of `AtomicFile`.
1284 /// Call `AtomicFile.finish` to atomically replace `dest_path` with contents.
1285 pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions) !AtomicFile {
1286 return AtomicFile.init2(dest_path, options.mode, self);
1287 }
13731288};
13741289
1375/// Returns an handle to the current working directory that is open for traversal.
1290/// Returns an handle to the current working directory. It is not opened with iteration capability.
13761291/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
13771292/// On POSIX targets, this function is comptime-callable.
13781293pub fn cwd() Dir {
......@@ -1450,6 +1365,25 @@ pub fn deleteFileAbsoluteW(absolute_path_w: [*:0]const u16) DeleteFileError!void
14501365 return cwd().deleteFileW(absolute_path_w);
14511366}
14521367
1368/// Removes a symlink, file, or directory.
1369/// This is equivalent to `Dir.deleteTree` with the base directory.
1370/// Asserts that the path is absolute. See `Dir.deleteTree` for a function that
1371/// operates on both absolute and relative paths.
1372/// Asserts that the path parameter has no null bytes.
1373pub fn deleteTreeAbsolute(absolute_path: []const u8) !void {
1374 assert(path.isAbsolute(absolute_path));
1375 const dirname = path.dirname(absolute_path) orelse return error{
1376 /// Attempt to remove the root file system path.
1377 /// This error is unreachable if `absolute_path` is relative.
1378 CannotDeleteRootDirectory,
1379 }.CannotDeleteRootDirectory;
1380
1381 var dir = try cwd().openDir(dirname, .{});
1382 defer dir.close();
1383
1384 return dir.deleteTree(path.basename(absolute_path));
1385}
1386
14531387pub const Walker = struct {
14541388 stack: std.ArrayList(StackItem),
14551389 name_buffer: std.Buffer,
......@@ -1484,7 +1418,7 @@ pub const Walker = struct {
14841418 try self.name_buffer.appendByte(path.sep);
14851419 try self.name_buffer.append(base.name);
14861420 if (base.kind == .Directory) {
1487 var new_dir = top.dir_it.dir.openDirList(base.name) catch |err| switch (err) {
1421 var new_dir = top.dir_it.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) {
14881422 error.NameTooLong => unreachable, // no path sep in base.name
14891423 else => |e| return e,
14901424 };
......@@ -1522,7 +1456,7 @@ pub const Walker = struct {
15221456pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
15231457 assert(!mem.endsWith(u8, dir_path, path.sep_str));
15241458
1525 var dir = try cwd().openDirList(dir_path);
1459 var dir = try cwd().openDir(dir_path, .{ .iterate = true });
15261460 errdefer dir.close();
15271461
15281462 var name_buffer = try std.Buffer.init(allocator, dir_path);
......@@ -1541,13 +1475,12 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
15411475 return walker;
15421476}
15431477
1544/// Read value of a symbolic link.
1545/// The return value is a slice of buffer, from index `0`.
1478/// Deprecated; use `Dir.readLink`.
15461479pub fn readLink(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
15471480 return os.readlink(pathname, buffer);
15481481}
15491482
1550/// Same as `readLink`, except the parameter is null-terminated.
1483/// Deprecated; use `Dir.readLinkC`.
15511484pub fn readLinkC(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
15521485 return os.readlinkC(pathname_c, buffer);
15531486}
......@@ -1654,6 +1587,7 @@ pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const
16541587}
16551588
16561589/// `realpath`, except caller must free the returned memory.
1590/// TODO integrate with `Dir`
16571591pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
16581592 var buf: [MAX_PATH_BYTES]u8 = undefined;
16591593 return mem.dupe(allocator, u8, try os.realpath(pathname, &buf));
......@@ -1662,6 +1596,9 @@ pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
16621596test "" {
16631597 _ = makeDirAbsolute;
16641598 _ = makeDirAbsoluteZ;
1599 _ = copyFileAbsolute;
1600 _ = updateFileAbsolute;
1601 _ = Dir.copyFile;
16651602 _ = @import("fs/path.zig");
16661603 _ = @import("fs/file.zig");
16671604 _ = @import("fs/get_app_data_dir.zig");
lib/std/fs/watch.zig+1-1
......@@ -619,7 +619,7 @@ test "write a file, watch it, write it again" {
619619 if (true) return error.SkipZigTest;
620620
621621 try fs.cwd().makePath(test_tmp_dir);
622 defer os.deleteTree(test_tmp_dir) catch {};
622 defer fs.cwd().deleteTree(test_tmp_dir) catch {};
623623
624624 const allocator = std.heap.page_allocator;
625625 return testFsWatch(&allocator);
lib/std/hash/auto_hash.zig+8-4
......@@ -40,7 +40,9 @@ pub fn hashPointer(hasher: var, key: var, comptime strat: HashStrategy) void {
4040 .DeepRecursive => hashArray(hasher, key, .DeepRecursive),
4141 },
4242
43 .Many, .C, => switch (strat) {
43 .Many,
44 .C,
45 => switch (strat) {
4446 .Shallow => hash(hasher, @ptrToInt(key), .Shallow),
4547 else => @compileError(
4648 \\ unknown-length pointers and C pointers cannot be hashed deeply.
......@@ -236,9 +238,11 @@ test "hash slice shallow" {
236238 defer std.testing.allocator.destroy(array1);
237239 array1.* = [_]u32{ 1, 2, 3, 4, 5, 6 };
238240 const array2 = [_]u32{ 1, 2, 3, 4, 5, 6 };
239 const a = array1[0..];
240 const b = array2[0..];
241 const c = array1[0..3];
241 // TODO audit deep/shallow - maybe it has the wrong behavior with respect to array pointers and slices
242 var runtime_zero: usize = 0;
243 const a = array1[runtime_zero..];
244 const b = array2[runtime_zero..];
245 const c = array1[runtime_zero..3];
242246 testing.expect(testHashShallow(a) == testHashShallow(a));
243247 testing.expect(testHashShallow(a) != testHashShallow(array1));
244248 testing.expect(testHashShallow(a) != testHashShallow(b));
lib/std/hash/siphash.zig+3-3
......@@ -39,8 +39,8 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round
3939 pub fn init(key: []const u8) Self {
4040 assert(key.len >= 16);
4141
42 const k0 = mem.readIntSliceLittle(u64, key[0..8]);
43 const k1 = mem.readIntSliceLittle(u64, key[8..16]);
42 const k0 = mem.readIntLittle(u64, key[0..8]);
43 const k1 = mem.readIntLittle(u64, key[8..16]);
4444
4545 var d = Self{
4646 .v0 = k0 ^ 0x736f6d6570736575,
......@@ -111,7 +111,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round
111111 fn round(self: *Self, b: []const u8) void {
112112 assert(b.len == 8);
113113
114 const m = mem.readIntSliceLittle(u64, b[0..]);
114 const m = mem.readIntLittle(u64, b[0..8]);
115115 self.v3 ^= m;
116116
117117 // TODO this is a workaround, should be able to supply the value without a separate variable
lib/std/hash/wyhash.zig+1-1
......@@ -11,7 +11,7 @@ const primes = [_]u64{
1111
1212fn read_bytes(comptime bytes: u8, data: []const u8) u64 {
1313 const T = std.meta.IntType(false, 8 * bytes);
14 return mem.readIntSliceLittle(T, data[0..bytes]);
14 return mem.readIntLittle(T, data[0..bytes]);
1515}
1616
1717fn read_8bytes_swapped(data: []const u8) u64 {
lib/std/io/serialization.zig+5-1
......@@ -1,6 +1,10 @@
11const std = @import("../std.zig");
22const builtin = std.builtin;
33const io = std.io;
4const assert = std.debug.assert;
5const math = std.math;
6const meta = std.meta;
7const trait = meta.trait;
48
59pub const Packing = enum {
610 /// Pack data to byte alignment
......@@ -252,7 +256,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
252256 byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v);
253257 }
254258
255 try self.out_stream.write(&buffer);
259 try self.out_stream.writeAll(&buffer);
256260 }
257261
258262 /// Serializes the passed value into the stream
lib/std/json.zig+16-7
......@@ -2249,11 +2249,16 @@ pub const StringifyOptions = struct {
22492249 // TODO: allow picking if []u8 is string or array?
22502250};
22512251
2252pub const StringifyError = error{
2253 TooMuchData,
2254 DifferentData,
2255};
2256
22522257pub fn stringify(
22532258 value: var,
22542259 options: StringifyOptions,
22552260 out_stream: var,
2256) !void {
2261) StringifyError!void {
22572262 const T = @TypeOf(value);
22582263 switch (@typeInfo(T)) {
22592264 .Float, .ComptimeFloat => {
......@@ -2320,9 +2325,15 @@ pub fn stringify(
23202325 return;
23212326 },
23222327 .Pointer => |ptr_info| switch (ptr_info.size) {
2323 .One => {
2324 // TODO: avoid loops?
2325 return try stringify(value.*, options, out_stream);
2328 .One => switch (@typeInfo(ptr_info.child)) {
2329 .Array => {
2330 const Slice = []const std.meta.Elem(ptr_info.child);
2331 return stringify(@as(Slice, value), options, out_stream);
2332 },
2333 else => {
2334 // TODO: avoid loops?
2335 return stringify(value.*, options, out_stream);
2336 },
23262337 },
23272338 // TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972)
23282339 .Slice => {
......@@ -2381,9 +2392,7 @@ pub fn stringify(
23812392 },
23822393 else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),
23832394 },
2384 .Array => |info| {
2385 return try stringify(value[0..], options, out_stream);
2386 },
2395 .Array => return stringify(&value, options, out_stream),
23872396 else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),
23882397 }
23892398 unreachable;
lib/std/math/big/int.zig+2-2
......@@ -520,13 +520,13 @@ pub const Int = struct {
520520 comptime fmt: []const u8,
521521 options: std.fmt.FormatOptions,
522522 out_stream: var,
523 ) FmtError!void {
523 ) !void {
524524 self.assertWritable();
525525 // TODO look at fmt and support other bases
526526 // TODO support read-only fixed integers
527527 const str = self.toString(self.allocator.?, 10) catch @panic("TODO make this non allocating");
528528 defer self.allocator.?.free(str);
529 return out_stream.print(str);
529 return out_stream.writeAll(str);
530530 }
531531
532532 /// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
lib/std/mem.zig+137-75
......@@ -116,7 +116,7 @@ pub const Allocator = struct {
116116 pub fn allocSentinel(self: *Allocator, comptime Elem: type, n: usize, comptime sentinel: Elem) Error![:sentinel]Elem {
117117 var ptr = try self.alloc(Elem, n + 1);
118118 ptr[n] = sentinel;
119 return ptr[0 .. n :sentinel];
119 return ptr[0..n :sentinel];
120120 }
121121
122122 pub fn alignedAlloc(
......@@ -496,14 +496,14 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
496496 return true;
497497}
498498
499/// Deprecated. Use `span`.
499/// Deprecated. Use `spanZ`.
500500pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T {
501 return ptr[0..len(ptr) :0];
501 return ptr[0..lenZ(ptr) :0];
502502}
503503
504/// Deprecated. Use `span`.
504/// Deprecated. Use `spanZ`.
505505pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T {
506 return ptr[0..len(ptr) :0];
506 return ptr[0..lenZ(ptr) :0];
507507}
508508
509509/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and
......@@ -548,6 +548,9 @@ test "Span" {
548548/// returns a slice. If there is a sentinel on the input type, there will be a
549549/// sentinel on the output type. The constness of the output type matches
550550/// the constness of the input type.
551///
552/// When there is both a sentinel and an array length or slice length, the
553/// length value is used instead of the sentinel.
551554pub fn span(ptr: var) Span(@TypeOf(ptr)) {
552555 const Result = Span(@TypeOf(ptr));
553556 const l = len(ptr);
......@@ -560,20 +563,42 @@ pub fn span(ptr: var) Span(@TypeOf(ptr)) {
560563
561564test "span" {
562565 var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
563 const ptr = array[0..2 :3].ptr;
566 const ptr = @as([*:3]u16, array[0..2 :3]);
564567 testing.expect(eql(u16, span(ptr), &[_]u16{ 1, 2 }));
565568 testing.expect(eql(u16, span(&array), &[_]u16{ 1, 2, 3, 4, 5 }));
566569}
567570
571/// Same as `span`, except when there is both a sentinel and an array
572/// length or slice length, scans the memory for the sentinel value
573/// rather than using the length.
574pub fn spanZ(ptr: var) Span(@TypeOf(ptr)) {
575 const Result = Span(@TypeOf(ptr));
576 const l = lenZ(ptr);
577 if (@typeInfo(Result).Pointer.sentinel) |s| {
578 return ptr[0..l :s];
579 } else {
580 return ptr[0..l];
581 }
582}
583
584test "spanZ" {
585 var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
586 const ptr = @as([*:3]u16, array[0..2 :3]);
587 testing.expect(eql(u16, spanZ(ptr), &[_]u16{ 1, 2 }));
588 testing.expect(eql(u16, spanZ(&array), &[_]u16{ 1, 2, 3, 4, 5 }));
589}
590
568591/// Takes a pointer to an array, an array, a sentinel-terminated pointer,
569592/// or a slice, and returns the length.
593/// In the case of a sentinel-terminated array, it uses the array length.
594/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.
570595pub fn len(ptr: var) usize {
571596 return switch (@typeInfo(@TypeOf(ptr))) {
572597 .Array => |info| info.len,
573598 .Pointer => |info| switch (info.size) {
574599 .One => switch (@typeInfo(info.child)) {
575 .Array => |x| x.len,
576 else => @compileError("invalid type given to std.mem.length"),
600 .Array => ptr.len,
601 else => @compileError("invalid type given to std.mem.len"),
577602 },
578603 .Many => if (info.sentinel) |sentinel|
579604 indexOfSentinel(info.child, sentinel, ptr)
......@@ -582,7 +607,7 @@ pub fn len(ptr: var) usize {
582607 .C => indexOfSentinel(info.child, 0, ptr),
583608 .Slice => ptr.len,
584609 },
585 else => @compileError("invalid type given to std.mem.length"),
610 else => @compileError("invalid type given to std.mem.len"),
586611 };
587612}
588613
......@@ -594,9 +619,67 @@ test "len" {
594619 testing.expect(len(&array) == 5);
595620 testing.expect(len(array[0..3]) == 3);
596621 array[2] = 0;
597 const ptr = array[0..2 :0].ptr;
622 const ptr = @as([*:0]u16, array[0..2 :0]);
598623 testing.expect(len(ptr) == 2);
599624 }
625 {
626 var array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 };
627 testing.expect(len(&array) == 5);
628 array[2] = 0;
629 testing.expect(len(&array) == 5);
630 }
631}
632
633/// Takes a pointer to an array, an array, a sentinel-terminated pointer,
634/// or a slice, and returns the length.
635/// In the case of a sentinel-terminated array, it scans the array
636/// for a sentinel and uses that for the length, rather than using the array length.
637/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.
638pub fn lenZ(ptr: var) usize {
639 return switch (@typeInfo(@TypeOf(ptr))) {
640 .Array => |info| if (info.sentinel) |sentinel|
641 indexOfSentinel(info.child, sentinel, &ptr)
642 else
643 info.len,
644 .Pointer => |info| switch (info.size) {
645 .One => switch (@typeInfo(info.child)) {
646 .Array => |x| if (x.sentinel) |sentinel|
647 indexOfSentinel(x.child, sentinel, ptr)
648 else
649 ptr.len,
650 else => @compileError("invalid type given to std.mem.lenZ"),
651 },
652 .Many => if (info.sentinel) |sentinel|
653 indexOfSentinel(info.child, sentinel, ptr)
654 else
655 @compileError("length of pointer with no sentinel"),
656 .C => indexOfSentinel(info.child, 0, ptr),
657 .Slice => if (info.sentinel) |sentinel|
658 indexOfSentinel(info.child, sentinel, ptr.ptr)
659 else
660 ptr.len,
661 },
662 else => @compileError("invalid type given to std.mem.lenZ"),
663 };
664}
665
666test "lenZ" {
667 testing.expect(lenZ("aoeu") == 4);
668
669 {
670 var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
671 testing.expect(lenZ(&array) == 5);
672 testing.expect(lenZ(array[0..3]) == 3);
673 array[2] = 0;
674 const ptr = @as([*:0]u16, array[0..2 :0]);
675 testing.expect(lenZ(ptr) == 2);
676 }
677 {
678 var array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 };
679 testing.expect(lenZ(&array) == 5);
680 array[2] = 0;
681 testing.expect(lenZ(&array) == 2);
682 }
600683}
601684
602685pub fn indexOfSentinel(comptime Elem: type, comptime sentinel: Elem, ptr: [*:sentinel]const Elem) usize {
......@@ -810,8 +893,7 @@ pub const readIntBig = switch (builtin.endian) {
810893pub fn readIntSliceNative(comptime T: type, bytes: []const u8) T {
811894 const n = @divExact(T.bit_count, 8);
812895 assert(bytes.len >= n);
813 // TODO https://github.com/ziglang/zig/issues/863
814 return readIntNative(T, @ptrCast(*const [n]u8, bytes.ptr));
896 return readIntNative(T, bytes[0..n]);
815897}
816898
817899/// 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
849931pub fn readIntSlice(comptime T: type, bytes: []const u8, endian: builtin.Endian) T {
850932 const n = @divExact(T.bit_count, 8);
851933 assert(bytes.len >= n);
852 // TODO https://github.com/ziglang/zig/issues/863
853 return readInt(T, @ptrCast(*const [n]u8, bytes.ptr), endian);
934 return readInt(T, bytes[0..n], endian);
854935}
855936
856937test "comptime read/write int" {
......@@ -1572,24 +1653,24 @@ pub fn nativeToBig(comptime T: type, x: T) T {
15721653}
15731654
15741655fn AsBytesReturnType(comptime P: type) type {
1575 if (comptime !trait.isSingleItemPtr(P))
1656 if (!trait.isSingleItemPtr(P))
15761657 @compileError("expected single item pointer, passed " ++ @typeName(P));
15771658
1578 const size = @as(usize, @sizeOf(meta.Child(P)));
1579 const alignment = comptime meta.alignment(P);
1659 const size = @sizeOf(meta.Child(P));
1660 const alignment = meta.alignment(P);
15801661
15811662 if (alignment == 0) {
1582 if (comptime trait.isConstPtr(P))
1663 if (trait.isConstPtr(P))
15831664 return *const [size]u8;
15841665 return *[size]u8;
15851666 }
15861667
1587 if (comptime trait.isConstPtr(P))
1668 if (trait.isConstPtr(P))
15881669 return *align(alignment) const [size]u8;
15891670 return *align(alignment) [size]u8;
15901671}
15911672
1592///Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness.
1673/// Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness.
15931674pub fn asBytes(ptr: var) AsBytesReturnType(@TypeOf(ptr)) {
15941675 const P = @TypeOf(ptr);
15951676 return @ptrCast(AsBytesReturnType(P), ptr);
......@@ -1736,34 +1817,50 @@ fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {
17361817}
17371818
17381819pub fn bytesAsSlice(comptime T: type, bytes: var) BytesAsSliceReturnType(T, @TypeOf(bytes)) {
1739 const bytesSlice = if (comptime trait.isPtrTo(.Array)(@TypeOf(bytes))) bytes[0..] else bytes;
1740
17411820 // let's not give an undefined pointer to @ptrCast
17421821 // it may be equal to zero and fail a null check
1743 if (bytesSlice.len == 0) {
1822 if (bytes.len == 0) {
17441823 return &[0]T{};
17451824 }
17461825
1747 const bytesType = @TypeOf(bytesSlice);
1748 const alignment = comptime meta.alignment(bytesType);
1826 const Bytes = @TypeOf(bytes);
1827 const alignment = comptime meta.alignment(Bytes);
17491828
1750 const castTarget = if (comptime trait.isConstPtr(bytesType)) [*]align(alignment) const T else [*]align(alignment) T;
1829 const cast_target = if (comptime trait.isConstPtr(Bytes)) [*]align(alignment) const T else [*]align(alignment) T;
17511830
1752 return @ptrCast(castTarget, bytesSlice.ptr)[0..@divExact(bytes.len, @sizeOf(T))];
1831 return @ptrCast(cast_target, bytes)[0..@divExact(bytes.len, @sizeOf(T))];
17531832}
17541833
17551834test "bytesAsSlice" {
1756 const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
1757 const slice = bytesAsSlice(u16, bytes[0..]);
1758 testing.expect(slice.len == 2);
1759 testing.expect(bigToNative(u16, slice[0]) == 0xDEAD);
1760 testing.expect(bigToNative(u16, slice[1]) == 0xBEEF);
1835 {
1836 const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
1837 const slice = bytesAsSlice(u16, bytes[0..]);
1838 testing.expect(slice.len == 2);
1839 testing.expect(bigToNative(u16, slice[0]) == 0xDEAD);
1840 testing.expect(bigToNative(u16, slice[1]) == 0xBEEF);
1841 }
1842 {
1843 const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
1844 var runtime_zero: usize = 0;
1845 const slice = bytesAsSlice(u16, bytes[runtime_zero..]);
1846 testing.expect(slice.len == 2);
1847 testing.expect(bigToNative(u16, slice[0]) == 0xDEAD);
1848 testing.expect(bigToNative(u16, slice[1]) == 0xBEEF);
1849 }
17611850}
17621851
17631852test "bytesAsSlice keeps pointer alignment" {
1764 var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 };
1765 const numbers = bytesAsSlice(u32, bytes[0..]);
1766 comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32);
1853 {
1854 var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 };
1855 const numbers = bytesAsSlice(u32, bytes[0..]);
1856 comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32);
1857 }
1858 {
1859 var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 };
1860 var runtime_zero: usize = 0;
1861 const numbers = bytesAsSlice(u32, bytes[runtime_zero..]);
1862 comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32);
1863 }
17671864}
17681865
17691866test "bytesAsSlice on a packed struct" {
......@@ -1799,21 +1896,19 @@ fn SliceAsBytesReturnType(comptime sliceType: type) type {
17991896}
18001897
18011898pub fn sliceAsBytes(slice: var) SliceAsBytesReturnType(@TypeOf(slice)) {
1802 const actualSlice = if (comptime trait.isPtrTo(.Array)(@TypeOf(slice))) slice[0..] else slice;
1803 const actualSliceTypeInfo = @typeInfo(@TypeOf(actualSlice)).Pointer;
1899 const Slice = @TypeOf(slice);
18041900
18051901 // let's not give an undefined pointer to @ptrCast
18061902 // it may be equal to zero and fail a null check
1807 if (actualSlice.len == 0 and actualSliceTypeInfo.sentinel == null) {
1903 if (slice.len == 0 and comptime meta.sentinel(Slice) == null) {
18081904 return &[0]u8{};
18091905 }
18101906
1811 const sliceType = @TypeOf(actualSlice);
1812 const alignment = comptime meta.alignment(sliceType);
1907 const alignment = comptime meta.alignment(Slice);
18131908
1814 const castTarget = if (comptime trait.isConstPtr(sliceType)) [*]align(alignment) const u8 else [*]align(alignment) u8;
1909 const cast_target = if (comptime trait.isConstPtr(Slice)) [*]align(alignment) const u8 else [*]align(alignment) u8;
18151910
1816 return @ptrCast(castTarget, actualSlice.ptr)[0 .. actualSlice.len * @sizeOf(comptime meta.Child(sliceType))];
1911 return @ptrCast(cast_target, slice)[0 .. slice.len * @sizeOf(meta.Elem(Slice))];
18171912}
18181913
18191914test "sliceAsBytes" {
......@@ -1883,39 +1978,6 @@ test "sliceAsBytes and bytesAsSlice back" {
18831978 testing.expect(bytes[11] == math.maxInt(u8));
18841979}
18851980
1886fn SubArrayPtrReturnType(comptime T: type, comptime length: usize) type {
1887 if (trait.isConstPtr(T))
1888 return *const [length]meta.Child(meta.Child(T));
1889 return *[length]meta.Child(meta.Child(T));
1890}
1891
1892/// Given a pointer to an array, returns a pointer to a portion of that array, preserving constness.
1893/// TODO this will be obsoleted by https://github.com/ziglang/zig/issues/863
1894pub fn subArrayPtr(
1895 ptr: var,
1896 comptime start: usize,
1897 comptime length: usize,
1898) SubArrayPtrReturnType(@TypeOf(ptr), length) {
1899 assert(start + length <= ptr.*.len);
1900
1901 const ReturnType = SubArrayPtrReturnType(@TypeOf(ptr), length);
1902 const T = meta.Child(meta.Child(@TypeOf(ptr)));
1903 return @ptrCast(ReturnType, &ptr[start]);
1904}
1905
1906test "subArrayPtr" {
1907 const a1: [6]u8 = "abcdef".*;
1908 const sub1 = subArrayPtr(&a1, 2, 3);
1909 testing.expect(eql(u8, sub1, "cde"));
1910
1911 var a2: [6]u8 = "abcdef".*;
1912 var sub2 = subArrayPtr(&a2, 2, 3);
1913
1914 testing.expect(eql(u8, sub2, "cde"));
1915 sub2[1] = 'X';
1916 testing.expect(eql(u8, &a2, "abcXef"));
1917}
1918
19191981/// Round an address up to the nearest aligned address
19201982/// The alignment must be a power of 2 and greater than 0.
19211983pub fn alignForward(addr: usize, alignment: usize) usize {
lib/std/meta.zig+50-15
......@@ -104,7 +104,7 @@ pub fn Child(comptime T: type) type {
104104 .Array => |info| info.child,
105105 .Pointer => |info| info.child,
106106 .Optional => |info| info.child,
107 else => @compileError("Expected pointer, optional, or array type, " ++ "found '" ++ @typeName(T) ++ "'"),
107 else => @compileError("Expected pointer, optional, or array type, found '" ++ @typeName(T) ++ "'"),
108108 };
109109}
110110
......@@ -115,30 +115,65 @@ test "std.meta.Child" {
115115 testing.expect(Child(?u8) == u8);
116116}
117117
118/// Given a type with a sentinel e.g. `[:0]u8`, returns the sentinel
119pub fn Sentinel(comptime T: type) Child(T) {
120 // comptime asserts that ptr has a sentinel
118/// Given a "memory span" type, returns the "element type".
119pub fn Elem(comptime T: type) type {
121120 switch (@typeInfo(T)) {
122 .Array => |arrayInfo| {
123 return comptime arrayInfo.sentinel.?;
121 .Array => |info| return info.child,
122 .Pointer => |info| switch (info.size) {
123 .One => switch (@typeInfo(info.child)) {
124 .Array => |array_info| return array_info.child,
125 else => {},
126 },
127 .Many, .C, .Slice => return info.child,
124128 },
125 .Pointer => |ptrInfo| {
126 switch (ptrInfo.size) {
127 .Many, .Slice => {
128 return comptime ptrInfo.sentinel.?;
129 else => {},
130 }
131 @compileError("Expected pointer, slice, or array, found '" ++ @typeName(T) ++ "'");
132}
133
134test "std.meta.Elem" {
135 testing.expect(Elem([1]u8) == u8);
136 testing.expect(Elem([*]u8) == u8);
137 testing.expect(Elem([]u8) == u8);
138 testing.expect(Elem(*[10]u8) == u8);
139}
140
141/// Given a type which can have a sentinel e.g. `[:0]u8`, returns the sentinel value,
142/// or `null` if there is not one.
143/// Types which cannot possibly have a sentinel will be a compile error.
144pub fn sentinel(comptime T: type) ?Elem(T) {
145 switch (@typeInfo(T)) {
146 .Array => |info| return info.sentinel,
147 .Pointer => |info| {
148 switch (info.size) {
149 .Many, .Slice => return info.sentinel,
150 .One => switch (@typeInfo(info.child)) {
151 .Array => |array_info| return array_info.sentinel,
152 else => {},
129153 },
130154 else => {},
131155 }
132156 },
133157 else => {},
134158 }
135 @compileError("not a sentinel type, found '" ++ @typeName(T) ++ "'");
159 @compileError("type '" ++ @typeName(T) ++ "' cannot possibly have a sentinel");
136160}
137161
138test "std.meta.Sentinel" {
139 testing.expectEqual(@as(u8, 0), Sentinel([:0]u8));
140 testing.expectEqual(@as(u8, 0), Sentinel([*:0]u8));
141 testing.expectEqual(@as(u8, 0), Sentinel([5:0]u8));
162test "std.meta.sentinel" {
163 testSentinel();
164 comptime testSentinel();
165}
166
167fn testSentinel() void {
168 testing.expectEqual(@as(u8, 0), sentinel([:0]u8).?);
169 testing.expectEqual(@as(u8, 0), sentinel([*:0]u8).?);
170 testing.expectEqual(@as(u8, 0), sentinel([5:0]u8).?);
171 testing.expectEqual(@as(u8, 0), sentinel(*const [5:0]u8).?);
172
173 testing.expect(sentinel([]u8) == null);
174 testing.expect(sentinel([*]u8) == null);
175 testing.expect(sentinel([5]u8) == null);
176 testing.expect(sentinel(*const [5]u8) == null);
142177}
143178
144179pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout {
lib/std/meta/trait.zig+7-5
......@@ -230,9 +230,10 @@ pub fn isSingleItemPtr(comptime T: type) bool {
230230
231231test "std.meta.trait.isSingleItemPtr" {
232232 const array = [_]u8{0} ** 10;
233 testing.expect(isSingleItemPtr(@TypeOf(&array[0])));
234 testing.expect(!isSingleItemPtr(@TypeOf(array)));
235 testing.expect(!isSingleItemPtr(@TypeOf(array[0..1])));
233 comptime testing.expect(isSingleItemPtr(@TypeOf(&array[0])));
234 comptime testing.expect(!isSingleItemPtr(@TypeOf(array)));
235 var runtime_zero: usize = 0;
236 testing.expect(!isSingleItemPtr(@TypeOf(array[runtime_zero..1])));
236237}
237238
238239pub fn isManyItemPtr(comptime T: type) bool {
......@@ -259,7 +260,8 @@ pub fn isSlice(comptime T: type) bool {
259260
260261test "std.meta.trait.isSlice" {
261262 const array = [_]u8{0} ** 10;
262 testing.expect(isSlice(@TypeOf(array[0..])));
263 var runtime_zero: usize = 0;
264 testing.expect(isSlice(@TypeOf(array[runtime_zero..])));
263265 testing.expect(!isSlice(@TypeOf(array)));
264266 testing.expect(!isSlice(@TypeOf(&array[0])));
265267}
......@@ -276,7 +278,7 @@ pub fn isIndexable(comptime T: type) bool {
276278
277279test "std.meta.trait.isIndexable" {
278280 const array = [_]u8{0} ** 10;
279 const slice = array[0..];
281 const slice = @as([]const u8, &array);
280282
281283 testing.expect(isIndexable(@TypeOf(array)));
282284 testing.expect(isIndexable(@TypeOf(&array)));
lib/std/net.zig+7-5
......@@ -612,8 +612,7 @@ fn linuxLookupName(
612612 } else {
613613 mem.copy(u8, &sa6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff");
614614 mem.copy(u8, &da6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff");
615 // TODO https://github.com/ziglang/zig/issues/863
616 mem.writeIntNative(u32, @ptrCast(*[4]u8, da6.addr[12..].ptr), addr.addr.in.addr);
615 mem.writeIntNative(u32, da6.addr[12..], addr.addr.in.addr);
617616 da4.addr = addr.addr.in.addr;
618617 da = @ptrCast(*os.sockaddr, &da4);
619618 dalen = @sizeOf(os.sockaddr_in);
......@@ -821,7 +820,7 @@ fn linuxLookupNameFromHosts(
821820 // Skip to the delimiter in the stream, to fix parsing
822821 try stream.skipUntilDelimiterOrEof('\n');
823822 // Use the truncated line. A truncated comment or hostname will be handled correctly.
824 break :blk line_buf[0..];
823 break :blk @as([]u8, &line_buf); // TODO the cast should not be necessary
825824 },
826825 else => |e| return e,
827826 }) |line| {
......@@ -958,7 +957,10 @@ fn linuxLookupNameFromDns(
958957 }
959958 }
960959
961 var ap = [2][]u8{ apbuf[0][0..0], apbuf[1][0..0] };
960 var ap = [2][]u8{ apbuf[0], apbuf[1] };
961 ap[0].len = 0;
962 ap[1].len = 0;
963
962964 try resMSendRc(qp[0..nq], ap[0..nq], apbuf[0..nq], rc);
963965
964966 var i: usize = 0;
......@@ -1015,7 +1017,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {
10151017 // Skip to the delimiter in the stream, to fix parsing
10161018 try stream.skipUntilDelimiterOrEof('\n');
10171019 // Give an empty line to the while loop, which will be skipped.
1018 break :blk line_buf[0..0];
1020 break :blk @as([]u8, line_buf[0..0]); // TODO the cast should not be necessary
10191021 },
10201022 else => |e| return e,
10211023 }) |line| {
lib/std/os.zig+143-5
......@@ -461,13 +461,11 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
461461 );
462462
463463 switch (rc) {
464 .SUCCESS => {},
464 .SUCCESS => return,
465465 .INVALID_HANDLE => unreachable, // Handle not open for writing
466466 .ACCESS_DENIED => return error.CannotTruncate,
467467 else => return windows.unexpectedStatus(rc),
468468 }
469
470 return;
471469 }
472470
473471 while (true) {
......@@ -852,6 +850,7 @@ pub const OpenError = error{
852850
853851/// Open and possibly create a file. Keeps trying if it gets interrupted.
854852/// See also `openC`.
853/// TODO support windows
855854pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t {
856855 const file_path_c = try toPosixPath(file_path);
857856 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 {
859858
860859/// Open and possibly create a file. Keeps trying if it gets interrupted.
861860/// See also `open`.
861/// TODO support windows
862862pub fn openC(file_path: [*:0]const u8, flags: u32, perm: usize) OpenError!fd_t {
863863 while (true) {
864864 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 {
892892/// Open and possibly create a file. Keeps trying if it gets interrupted.
893893/// `file_path` is relative to the open directory handle `dir_fd`.
894894/// See also `openatC`.
895/// TODO support windows
895896pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t {
896897 const file_path_c = try toPosixPath(file_path);
897898 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
900901/// Open and possibly create a file. Keeps trying if it gets interrupted.
901902/// `file_path` is relative to the open directory handle `dir_fd`.
902903/// See also `openat`.
904/// TODO support windows
903905pub fn openatC(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t {
904906 while (true) {
905907 const rc = system.openat(dir_fd, file_path, flags, mode);
......@@ -1527,6 +1529,9 @@ const RenameError = error{
15271529 RenameAcrossMountPoints,
15281530 InvalidUtf8,
15291531 BadPathName,
1532 NoDevice,
1533 SharingViolation,
1534 PipeBusy,
15301535} || UnexpectedError;
15311536
15321537/// 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
15801585 return windows.MoveFileExW(old_path, new_path, flags);
15811586}
15821587
1588/// Change the name or location of a file based on an open directory handle.
1589pub fn renameat(
1590 old_dir_fd: fd_t,
1591 old_path: []const u8,
1592 new_dir_fd: fd_t,
1593 new_path: []const u8,
1594) RenameError!void {
1595 if (builtin.os.tag == .windows) {
1596 const old_path_w = try windows.sliceToPrefixedFileW(old_path);
1597 const new_path_w = try windows.sliceToPrefixedFileW(new_path);
1598 return renameatW(old_dir_fd, &old_path_w, new_dir_fd, &new_path_w, windows.TRUE);
1599 } else {
1600 const old_path_c = try toPosixPath(old_path);
1601 const new_path_c = try toPosixPath(new_path);
1602 return renameatZ(old_dir_fd, &old_path_c, new_dir_fd, &new_path_c);
1603 }
1604}
1605
1606/// Same as `renameat` except the parameters are null-terminated byte arrays.
1607pub fn renameatZ(
1608 old_dir_fd: fd_t,
1609 old_path: [*:0]const u8,
1610 new_dir_fd: fd_t,
1611 new_path: [*:0]const u8,
1612) RenameError!void {
1613 if (builtin.os.tag == .windows) {
1614 const old_path_w = try windows.cStrToPrefixedFileW(old_path);
1615 const new_path_w = try windows.cStrToPrefixedFileW(new_path);
1616 return renameatW(old_dir_fd, &old_path_w, new_dir_fd, &new_path_w, windows.TRUE);
1617 }
1618
1619 switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) {
1620 0 => return,
1621 EACCES => return error.AccessDenied,
1622 EPERM => return error.AccessDenied,
1623 EBUSY => return error.FileBusy,
1624 EDQUOT => return error.DiskQuota,
1625 EFAULT => unreachable,
1626 EINVAL => unreachable,
1627 EISDIR => return error.IsDir,
1628 ELOOP => return error.SymLinkLoop,
1629 EMLINK => return error.LinkQuotaExceeded,
1630 ENAMETOOLONG => return error.NameTooLong,
1631 ENOENT => return error.FileNotFound,
1632 ENOTDIR => return error.NotDir,
1633 ENOMEM => return error.SystemResources,
1634 ENOSPC => return error.NoSpaceLeft,
1635 EEXIST => return error.PathAlreadyExists,
1636 ENOTEMPTY => return error.PathAlreadyExists,
1637 EROFS => return error.ReadOnlyFileSystem,
1638 EXDEV => return error.RenameAcrossMountPoints,
1639 else => |err| return unexpectedErrno(err),
1640 }
1641}
1642
1643/// Same as `renameat` except the parameters are null-terminated UTF16LE encoded byte arrays.
1644/// Assumes target is Windows.
1645/// TODO these args can actually be slices when using ntdll. audit the rest of the W functions too.
1646pub fn renameatW(
1647 old_dir_fd: fd_t,
1648 old_path: [*:0]const u16,
1649 new_dir_fd: fd_t,
1650 new_path_w: [*:0]const u16,
1651 ReplaceIfExists: windows.BOOLEAN,
1652) RenameError!void {
1653 const access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE;
1654 const src_fd = try windows.OpenFileW(old_dir_fd, old_path, null, access_mask, windows.FILE_OPEN);
1655 defer windows.CloseHandle(src_fd);
1656
1657 const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1);
1658 var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION)) = undefined;
1659 const new_path = mem.span(new_path_w);
1660 const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path.len * 2;
1661 if (struct_len > struct_buf_len) return error.NameTooLong;
1662
1663 const rename_info = @ptrCast(*windows.FILE_RENAME_INFORMATION, &rename_info_buf);
1664
1665 rename_info.* = .{
1666 .ReplaceIfExists = ReplaceIfExists,
1667 .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(new_path_w)) null else new_dir_fd,
1668 .FileNameLength = @intCast(u32, new_path.len * 2), // already checked error.NameTooLong
1669 .FileName = undefined,
1670 };
1671 std.mem.copy(u16, @as([*]u16, &rename_info.FileName)[0..new_path.len], new_path);
1672
1673 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
1674
1675 const rc = windows.ntdll.NtSetInformationFile(
1676 src_fd,
1677 &io_status_block,
1678 rename_info,
1679 @intCast(u32, struct_len), // already checked for error.NameTooLong
1680 .FileRenameInformation,
1681 );
1682
1683 switch (rc) {
1684 .SUCCESS => return,
1685 .INVALID_HANDLE => unreachable,
1686 .INVALID_PARAMETER => unreachable,
1687 .OBJECT_PATH_SYNTAX_BAD => unreachable,
1688 .ACCESS_DENIED => return error.AccessDenied,
1689 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
1690 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
1691 else => return windows.unexpectedStatus(rc),
1692 }
1693}
1694
15831695pub const MakeDirError = error{
15841696 AccessDenied,
15851697 DiskQuota,
......@@ -2072,7 +2184,7 @@ const ListenError = error{
20722184 OperationNotSupported,
20732185} || UnexpectedError;
20742186
2075pub fn listen(sockfd: i32, backlog: u32) ListenError!void {
2187pub fn listen(sockfd: fd_t, backlog: u32) ListenError!void {
20762188 const rc = system.listen(sockfd, backlog);
20772189 switch (errno(rc)) {
20782190 0 => return,
......@@ -2363,7 +2475,7 @@ pub fn connect(sockfd: fd_t, sock_addr: *const sockaddr, len: socklen_t) Connect
23632475 }
23642476}
23652477
2366pub fn getsockoptError(sockfd: i32) ConnectError!void {
2478pub fn getsockoptError(sockfd: fd_t) ConnectError!void {
23672479 var err_code: u32 = undefined;
23682480 var size: u32 = @sizeOf(u32);
23692481 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 {
30513163 }
30523164}
30533165
3166pub const FcntlError = error{
3167 PermissionDenied,
3168 FileBusy,
3169 ProcessFdQuotaExceeded,
3170 Locked,
3171} || UnexpectedError;
3172
3173pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
3174 while (true) {
3175 const rc = system.fcntl(fd, cmd, arg);
3176 switch (errno(rc)) {
3177 0 => return @intCast(usize, rc),
3178 EINTR => continue,
3179 EACCES => return error.Locked,
3180 EBADF => unreachable,
3181 EBUSY => return error.FileBusy,
3182 EINVAL => unreachable, // invalid parameters
3183 EPERM => return error.PermissionDenied,
3184 EMFILE => return error.ProcessFdQuotaExceeded,
3185 ENOTDIR => unreachable, // invalid parameter
3186 else => |err| return unexpectedErrno(err),
3187 }
3188 }
3189}
3190
30543191pub const RealPathError = error{
30553192 FileNotFound,
30563193 AccessDenied,
......@@ -3125,6 +3262,7 @@ pub fn realpathC(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
31253262}
31263263
31273264/// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded.
3265/// TODO use ntdll for better semantics
31283266pub fn realpathW(pathname: [*:0]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
31293267 const h_file = try windows.CreateFileW(
31303268 pathname,
lib/std/os/bits/dragonfly.zig+2
......@@ -283,6 +283,8 @@ pub const F_LOCK = 1;
283283pub const F_TLOCK = 2;
284284pub const F_TEST = 3;
285285
286pub const FD_CLOEXEC = 1;
287
286288pub const AT_FDCWD = -328243;
287289pub const AT_SYMLINK_NOFOLLOW = 1;
288290pub const AT_REMOVEDIR = 2;
lib/std/os/bits/freebsd.zig+2
......@@ -355,6 +355,8 @@ pub const F_GETOWN_EX = 16;
355355
356356pub const F_GETOWNER_UIDS = 17;
357357
358pub const FD_CLOEXEC = 1;
359
358360pub const SEEK_SET = 0;
359361pub const SEEK_CUR = 1;
360362pub const SEEK_END = 2;
lib/std/os/bits/linux.zig+2
......@@ -136,6 +136,8 @@ pub const MAP_FIXED_NOREPLACE = 0x100000;
136136/// For anonymous mmap, memory could be uninitialized
137137pub const MAP_UNINITIALIZED = 0x4000000;
138138
139pub const FD_CLOEXEC = 1;
140
139141pub const F_OK = 0;
140142pub const X_OK = 1;
141143pub const W_OK = 2;
lib/std/os/bits/netbsd.zig+2
......@@ -312,6 +312,8 @@ pub const F_GETLK = 7;
312312pub const F_SETLK = 8;
313313pub const F_SETLKW = 9;
314314
315pub const FD_CLOEXEC = 1;
316
315317pub const SEEK_SET = 0;
316318pub const SEEK_CUR = 1;
317319pub const SEEK_END = 2;
lib/std/os/linux.zig+8-4
......@@ -465,17 +465,17 @@ pub fn renameat(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const
465465 return syscall4(
466466 SYS_renameat,
467467 @bitCast(usize, @as(isize, oldfd)),
468 @ptrToInt(old),
468 @ptrToInt(oldpath),
469469 @bitCast(usize, @as(isize, newfd)),
470 @ptrToInt(new),
470 @ptrToInt(newpath),
471471 );
472472 } else {
473473 return syscall5(
474474 SYS_renameat2,
475475 @bitCast(usize, @as(isize, oldfd)),
476 @ptrToInt(old),
476 @ptrToInt(oldpath),
477477 @bitCast(usize, @as(isize, newfd)),
478 @ptrToInt(new),
478 @ptrToInt(newpath),
479479 0,
480480 );
481481 }
......@@ -588,6 +588,10 @@ pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize {
588588 return syscall4(SYS_wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0);
589589}
590590
591pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) usize {
592 return syscall3(SYS_fcntl, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, cmd)), arg);
593}
594
591595var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime);
592596
593597// We must follow the C calling convention when we call into the VDSO
lib/std/os/test.zig+44-12
......@@ -1,7 +1,8 @@
11const std = @import("../std.zig");
22const os = std.os;
33const testing = std.testing;
4const expect = std.testing.expect;
4const expect = testing.expect;
5const expectEqual = testing.expectEqual;
56const io = std.io;
67const fs = std.fs;
78const mem = std.mem;
......@@ -19,8 +20,8 @@ test "makePath, put some files in it, deleteTree" {
1920 try fs.cwd().makePath("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");
2021 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");
2122 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");
22 try fs.deleteTree("os_test_tmp");
23 if (fs.cwd().openDirTraverse("os_test_tmp")) |dir| {
23 try fs.cwd().deleteTree("os_test_tmp");
24 if (fs.cwd().openDir("os_test_tmp", .{})) |dir| {
2425 @panic("expected error");
2526 } else |err| {
2627 expect(err == error.FileNotFound);
......@@ -37,7 +38,7 @@ test "access file" {
3738
3839 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", "");
3940 try os.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", os.F_OK);
40 try fs.deleteTree("os_test_tmp");
41 try fs.cwd().deleteTree("os_test_tmp");
4142}
4243
4344fn testThreadIdFn(thread_id: *Thread.Id) void {
......@@ -46,9 +47,9 @@ fn testThreadIdFn(thread_id: *Thread.Id) void {
4647
4748test "sendfile" {
4849 try fs.cwd().makePath("os_test_tmp");
49 defer fs.deleteTree("os_test_tmp") catch {};
50 defer fs.cwd().deleteTree("os_test_tmp") catch {};
5051
51 var dir = try fs.cwd().openDirList("os_test_tmp");
52 var dir = try fs.cwd().openDir("os_test_tmp", .{});
5253 defer dir.close();
5354
5455 const line1 = "line1\n";
......@@ -112,14 +113,16 @@ test "fs.copyFile" {
112113 const dest_file = "tmp_test_copy_file2.txt";
113114 const dest_file2 = "tmp_test_copy_file3.txt";
114115
115 try fs.cwd().writeFile(src_file, data);
116 defer fs.cwd().deleteFile(src_file) catch {};
116 const cwd = fs.cwd();
117117
118 try fs.copyFile(src_file, dest_file);
119 defer fs.cwd().deleteFile(dest_file) catch {};
118 try cwd.writeFile(src_file, data);
119 defer cwd.deleteFile(src_file) catch {};
120120
121 try fs.copyFileMode(src_file, dest_file2, File.default_mode);
122 defer fs.cwd().deleteFile(dest_file2) catch {};
121 try cwd.copyFile(src_file, cwd, dest_file, .{});
122 defer cwd.deleteFile(dest_file) catch {};
123
124 try cwd.copyFile(src_file, cwd, dest_file2, .{ .override_mode = File.default_mode });
125 defer cwd.deleteFile(dest_file2) catch {};
123126
124127 try expectFileContents(dest_file, data);
125128 try expectFileContents(dest_file2, data);
......@@ -446,3 +449,32 @@ test "getenv" {
446449 expect(os.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null);
447450 }
448451}
452
453test "fcntl" {
454 if (builtin.os.tag == .windows)
455 return error.SkipZigTest;
456
457 const test_out_file = "os_tmp_test";
458
459 const file = try fs.cwd().createFile(test_out_file, .{});
460 defer {
461 file.close();
462 fs.cwd().deleteFile(test_out_file) catch {};
463 }
464
465 // Note: The test assumes createFile opens the file with O_CLOEXEC
466 {
467 const flags = try os.fcntl(file.handle, os.F_GETFD, 0);
468 expect((flags & os.FD_CLOEXEC) != 0);
469 }
470 {
471 _ = try os.fcntl(file.handle, os.F_SETFD, 0);
472 const flags = try os.fcntl(file.handle, os.F_GETFD, 0);
473 expect((flags & os.FD_CLOEXEC) == 0);
474 }
475 {
476 _ = try os.fcntl(file.handle, os.F_SETFD, os.FD_CLOEXEC);
477 const flags = try os.fcntl(file.handle, os.F_GETFD, 0);
478 expect((flags & os.FD_CLOEXEC) != 0);
479 }
480}
lib/std/os/windows.zig+85-1
......@@ -88,6 +88,82 @@ pub fn CreateFileW(
8888 return result;
8989}
9090
91pub const OpenError = error{
92 IsDir,
93 FileNotFound,
94 NoDevice,
95 SharingViolation,
96 AccessDenied,
97 PipeBusy,
98 PathAlreadyExists,
99 Unexpected,
100 NameTooLong,
101};
102
103/// TODO rename to CreateFileW
104/// TODO actually we don't need the path parameter to be null terminated
105pub fn OpenFileW(
106 dir: ?HANDLE,
107 sub_path_w: [*:0]const u16,
108 sa: ?*SECURITY_ATTRIBUTES,
109 access_mask: ACCESS_MASK,
110 creation: ULONG,
111) OpenError!HANDLE {
112 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
113 return error.IsDir;
114 }
115 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
116 return error.IsDir;
117 }
118
119 var result: HANDLE = undefined;
120
121 const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) {
122 error.Overflow => return error.NameTooLong,
123 };
124 var nt_name = UNICODE_STRING{
125 .Length = path_len_bytes,
126 .MaximumLength = path_len_bytes,
127 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
128 };
129 var attr = OBJECT_ATTRIBUTES{
130 .Length = @sizeOf(OBJECT_ATTRIBUTES),
131 .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir,
132 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
133 .ObjectName = &nt_name,
134 .SecurityDescriptor = if (sa) |ptr| ptr.lpSecurityDescriptor else null,
135 .SecurityQualityOfService = null,
136 };
137 var io: IO_STATUS_BLOCK = undefined;
138 const rc = ntdll.NtCreateFile(
139 &result,
140 access_mask,
141 &attr,
142 &io,
143 null,
144 FILE_ATTRIBUTE_NORMAL,
145 FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
146 creation,
147 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
148 null,
149 0,
150 );
151 switch (rc) {
152 .SUCCESS => return result,
153 .OBJECT_NAME_INVALID => unreachable,
154 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
155 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
156 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
157 .INVALID_PARAMETER => unreachable,
158 .SHARING_VIOLATION => return error.SharingViolation,
159 .ACCESS_DENIED => return error.AccessDenied,
160 .PIPE_BUSY => return error.PipeBusy,
161 .OBJECT_PATH_SYNTAX_BAD => unreachable,
162 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
163 else => return unexpectedStatus(rc),
164 }
165}
166
91167pub const CreatePipeError = error{Unexpected};
92168
93169pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void {
......@@ -1200,7 +1276,15 @@ pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError {
12001276 // 614 is the length of the longest windows error desciption
12011277 var buf_u16: [614]u16 = undefined;
12021278 var buf_u8: [614]u8 = undefined;
1203 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);
1279 const len = kernel32.FormatMessageW(
1280 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
1281 null,
1282 err,
1283 MAKELANGID(LANG.NEUTRAL, SUBLANG.DEFAULT),
1284 &buf_u16,
1285 buf_u16.len / @sizeOf(TCHAR),
1286 null,
1287 );
12041288 _ = std.unicode.utf16leToUtf8(&buf_u8, buf_u16[0..len]) catch unreachable;
12051289 std.debug.warn("error.Unexpected: GetLastError({}): {}\n", .{ @enumToInt(err), buf_u8[0..len] });
12061290 std.debug.dumpCurrentStackTrace(null);
lib/std/os/windows/bits.zig+7
......@@ -242,6 +242,13 @@ pub const FILE_NAME_INFORMATION = extern struct {
242242 FileName: [1]WCHAR,
243243};
244244
245pub const FILE_RENAME_INFORMATION = extern struct {
246 ReplaceIfExists: BOOLEAN,
247 RootDirectory: ?HANDLE,
248 FileNameLength: ULONG,
249 FileName: [1]WCHAR,
250};
251
245252pub const IO_STATUS_BLOCK = extern struct {
246253 // "DUMMYUNIONNAME" expands to "u"
247254 u: extern union {
lib/std/rand.zig+1-1
......@@ -5,7 +5,7 @@
55// ```
66// var buf: [8]u8 = undefined;
77// try std.crypto.randomBytes(buf[0..]);
8// const seed = mem.readIntSliceLittle(u64, buf[0..8]);
8// const seed = mem.readIntLittle(u64, buf[0..8]);
99//
1010// var r = DefaultPrng.init(seed);
1111//
lib/std/thread.zig+45-6
......@@ -6,6 +6,8 @@ const windows = std.os.windows;
66const c = std.c;
77const assert = std.debug.assert;
88
9const bad_startfn_ret = "expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'";
10
911pub const Thread = struct {
1012 data: Data,
1113
......@@ -158,15 +160,34 @@ pub const Thread = struct {
158160 };
159161 fn threadMain(raw_arg: windows.LPVOID) callconv(.C) windows.DWORD {
160162 const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;
163
161164 switch (@typeInfo(@TypeOf(startFn).ReturnType)) {
162 .Int => {
163 return startFn(arg);
165 .NoReturn => {
166 startFn(arg);
164167 },
165168 .Void => {
166169 startFn(arg);
167170 return 0;
168171 },
169 else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"),
172 .Int => |info| {
173 if (info.bits != 8) {
174 @compileError(bad_startfn_ret);
175 }
176 return startFn(arg);
177 },
178 .ErrorUnion => |info| {
179 if (info.payload != void) {
180 @compileError(bad_startfn_ret);
181 }
182 startFn(arg) catch |err| {
183 std.debug.warn("error: {}\n", .{@errorName(err)});
184 if (@errorReturnTrace()) |trace| {
185 std.debug.dumpStackTrace(trace.*);
186 }
187 };
188 return 0;
189 },
190 else => @compileError(bad_startfn_ret),
170191 }
171192 }
172193 };
......@@ -202,14 +223,32 @@ pub const Thread = struct {
202223 const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*;
203224
204225 switch (@typeInfo(@TypeOf(startFn).ReturnType)) {
205 .Int => {
206 return startFn(arg);
226 .NoReturn => {
227 startFn(arg);
207228 },
208229 .Void => {
209230 startFn(arg);
210231 return 0;
211232 },
212 else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"),
233 .Int => |info| {
234 if (info.bits != 8) {
235 @compileError(bad_startfn_ret);
236 }
237 return startFn(arg);
238 },
239 .ErrorUnion => |info| {
240 if (info.payload != void) {
241 @compileError(bad_startfn_ret);
242 }
243 startFn(arg) catch |err| {
244 std.debug.warn("error: {}\n", .{@errorName(err)});
245 if (@errorReturnTrace()) |trace| {
246 std.debug.dumpStackTrace(trace.*);
247 }
248 };
249 return 0;
250 },
251 else => @compileError(bad_startfn_ret),
213252 }
214253 }
215254 fn posixThreadMain(ctx: ?*c_void) callconv(.C) ?*c_void {
lib/std/unicode.zig+10-10
......@@ -251,12 +251,12 @@ pub const Utf16LeIterator = struct {
251251 pub fn nextCodepoint(it: *Utf16LeIterator) !?u21 {
252252 assert(it.i <= it.bytes.len);
253253 if (it.i == it.bytes.len) return null;
254 const c0: u21 = mem.readIntSliceLittle(u16, it.bytes[it.i .. it.i + 2]);
254 const c0: u21 = mem.readIntLittle(u16, it.bytes[it.i..][0..2]);
255255 if (c0 & ~@as(u21, 0x03ff) == 0xd800) {
256256 // surrogate pair
257257 it.i += 2;
258258 if (it.i >= it.bytes.len) return error.DanglingSurrogateHalf;
259 const c1: u21 = mem.readIntSliceLittle(u16, it.bytes[it.i .. it.i + 2]);
259 const c1: u21 = mem.readIntLittle(u16, it.bytes[it.i..][0..2]);
260260 if (c1 & ~@as(u21, 0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;
261261 it.i += 2;
262262 return 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff));
......@@ -630,11 +630,11 @@ test "utf8ToUtf16LeWithNull" {
630630 }
631631}
632632
633/// Converts a UTF-8 string literal into a UTF-16LE string literal.
634pub fn utf8ToUtf16LeStringLiteral(comptime utf8: []const u8) *const [calcUtf16LeLen(utf8) :0] u16 {
633/// Converts a UTF-8 string literal into a UTF-16LE string literal.
634pub fn utf8ToUtf16LeStringLiteral(comptime utf8: []const u8) *const [calcUtf16LeLen(utf8):0]u16 {
635635 comptime {
636636 const len: usize = calcUtf16LeLen(utf8);
637 var utf16le: [len :0]u16 = [_ :0]u16{0} ** len;
637 var utf16le: [len:0]u16 = [_:0]u16{0} ** len;
638638 const utf16le_len = utf8ToUtf16Le(&utf16le, utf8[0..]) catch |err| @compileError(err);
639639 assert(len == utf16le_len);
640640 return &utf16le;
......@@ -660,8 +660,8 @@ fn calcUtf16LeLen(utf8: []const u8) usize {
660660}
661661
662662test "utf8ToUtf16LeStringLiteral" {
663{
664 const bytes = [_:0]u16{ 0x41 };
663 {
664 const bytes = [_:0]u16{0x41};
665665 const utf16 = utf8ToUtf16LeStringLiteral("A");
666666 testing.expectEqualSlices(u16, &bytes, utf16);
667667 testing.expect(utf16[1] == 0);
......@@ -673,19 +673,19 @@ test "utf8ToUtf16LeStringLiteral" {
673673 testing.expect(utf16[2] == 0);
674674 }
675675 {
676 const bytes = [_:0]u16{ 0x02FF };
676 const bytes = [_:0]u16{0x02FF};
677677 const utf16 = utf8ToUtf16LeStringLiteral("\u{02FF}");
678678 testing.expectEqualSlices(u16, &bytes, utf16);
679679 testing.expect(utf16[1] == 0);
680680 }
681681 {
682 const bytes = [_:0]u16{ 0x7FF };
682 const bytes = [_:0]u16{0x7FF};
683683 const utf16 = utf8ToUtf16LeStringLiteral("\u{7FF}");
684684 testing.expectEqualSlices(u16, &bytes, utf16);
685685 testing.expect(utf16[1] == 0);
686686 }
687687 {
688 const bytes = [_:0]u16{ 0x801 };
688 const bytes = [_:0]u16{0x801};
689689 const utf16 = utf8ToUtf16LeStringLiteral("\u{801}");
690690 testing.expectEqualSlices(u16, &bytes, utf16);
691691 testing.expect(utf16[1] == 0);
lib/std/zig/ast.zig+66-92
......@@ -743,11 +743,11 @@ pub const Node = struct {
743743 var i = index;
744744
745745 switch (self.init_arg_expr) {
746 InitArg.Type => |t| {
746 .Type => |t| {
747747 if (i < 1) return t;
748748 i -= 1;
749749 },
750 InitArg.None, InitArg.Enum => {},
750 .None, .Enum => {},
751751 }
752752
753753 if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i).*;
......@@ -907,12 +907,7 @@ pub const Node = struct {
907907 }
908908
909909 switch (self.return_type) {
910 // TODO allow this and next prong to share bodies since the types are the same
911 ReturnType.Explicit => |node| {
912 if (i < 1) return node;
913 i -= 1;
914 },
915 ReturnType.InferErrorSet => |node| {
910 .Explicit, .InferErrorSet => |node| {
916911 if (i < 1) return node;
917912 i -= 1;
918913 },
......@@ -937,9 +932,7 @@ pub const Node = struct {
937932 pub fn lastToken(self: *const FnProto) TokenIndex {
938933 if (self.body_node) |body_node| return body_node.lastToken();
939934 switch (self.return_type) {
940 // TODO allow this and next prong to share bodies since the types are the same
941 ReturnType.Explicit => |node| return node.lastToken(),
942 ReturnType.InferErrorSet => |node| return node.lastToken(),
935 .Explicit, .InferErrorSet => |node| return node.lastToken(),
943936 }
944937 }
945938 };
......@@ -1515,55 +1508,55 @@ pub const Node = struct {
15151508 i -= 1;
15161509
15171510 switch (self.op) {
1518 Op.Catch => |maybe_payload| {
1511 .Catch => |maybe_payload| {
15191512 if (maybe_payload) |payload| {
15201513 if (i < 1) return payload;
15211514 i -= 1;
15221515 }
15231516 },
15241517
1525 Op.Add,
1526 Op.AddWrap,
1527 Op.ArrayCat,
1528 Op.ArrayMult,
1529 Op.Assign,
1530 Op.AssignBitAnd,
1531 Op.AssignBitOr,
1532 Op.AssignBitShiftLeft,
1533 Op.AssignBitShiftRight,
1534 Op.AssignBitXor,
1535 Op.AssignDiv,
1536 Op.AssignSub,
1537 Op.AssignSubWrap,
1538 Op.AssignMod,
1539 Op.AssignAdd,
1540 Op.AssignAddWrap,
1541 Op.AssignMul,
1542 Op.AssignMulWrap,
1543 Op.BangEqual,
1544 Op.BitAnd,
1545 Op.BitOr,
1546 Op.BitShiftLeft,
1547 Op.BitShiftRight,
1548 Op.BitXor,
1549 Op.BoolAnd,
1550 Op.BoolOr,
1551 Op.Div,
1552 Op.EqualEqual,
1553 Op.ErrorUnion,
1554 Op.GreaterOrEqual,
1555 Op.GreaterThan,
1556 Op.LessOrEqual,
1557 Op.LessThan,
1558 Op.MergeErrorSets,
1559 Op.Mod,
1560 Op.Mul,
1561 Op.MulWrap,
1562 Op.Period,
1563 Op.Range,
1564 Op.Sub,
1565 Op.SubWrap,
1566 Op.UnwrapOptional,
1518 .Add,
1519 .AddWrap,
1520 .ArrayCat,
1521 .ArrayMult,
1522 .Assign,
1523 .AssignBitAnd,
1524 .AssignBitOr,
1525 .AssignBitShiftLeft,
1526 .AssignBitShiftRight,
1527 .AssignBitXor,
1528 .AssignDiv,
1529 .AssignSub,
1530 .AssignSubWrap,
1531 .AssignMod,
1532 .AssignAdd,
1533 .AssignAddWrap,
1534 .AssignMul,
1535 .AssignMulWrap,
1536 .BangEqual,
1537 .BitAnd,
1538 .BitOr,
1539 .BitShiftLeft,
1540 .BitShiftRight,
1541 .BitXor,
1542 .BoolAnd,
1543 .BoolOr,
1544 .Div,
1545 .EqualEqual,
1546 .ErrorUnion,
1547 .GreaterOrEqual,
1548 .GreaterThan,
1549 .LessOrEqual,
1550 .LessThan,
1551 .MergeErrorSets,
1552 .Mod,
1553 .Mul,
1554 .MulWrap,
1555 .Period,
1556 .Range,
1557 .Sub,
1558 .SubWrap,
1559 .UnwrapOptional,
15671560 => {},
15681561 }
15691562
......@@ -1594,7 +1587,6 @@ pub const Node = struct {
15941587 Await,
15951588 BitNot,
15961589 BoolNot,
1597 Cancel,
15981590 OptionalType,
15991591 Negation,
16001592 NegationWrap,
......@@ -1631,8 +1623,7 @@ pub const Node = struct {
16311623 var i = index;
16321624
16331625 switch (self.op) {
1634 // TODO https://github.com/ziglang/zig/issues/1107
1635 Op.SliceType => |addr_of_info| {
1626 .PtrType, .SliceType => |addr_of_info| {
16361627 if (addr_of_info.sentinel) |sentinel| {
16371628 if (i < 1) return sentinel;
16381629 i -= 1;
......@@ -1644,14 +1635,7 @@ pub const Node = struct {
16441635 }
16451636 },
16461637
1647 Op.PtrType => |addr_of_info| {
1648 if (addr_of_info.align_info) |align_info| {
1649 if (i < 1) return align_info.node;
1650 i -= 1;
1651 }
1652 },
1653
1654 Op.ArrayType => |array_info| {
1638 .ArrayType => |array_info| {
16551639 if (i < 1) return array_info.len_expr;
16561640 i -= 1;
16571641 if (array_info.sentinel) |sentinel| {
......@@ -1660,16 +1644,15 @@ pub const Node = struct {
16601644 }
16611645 },
16621646
1663 Op.AddressOf,
1664 Op.Await,
1665 Op.BitNot,
1666 Op.BoolNot,
1667 Op.Cancel,
1668 Op.OptionalType,
1669 Op.Negation,
1670 Op.NegationWrap,
1671 Op.Try,
1672 Op.Resume,
1647 .AddressOf,
1648 .Await,
1649 .BitNot,
1650 .BoolNot,
1651 .OptionalType,
1652 .Negation,
1653 .NegationWrap,
1654 .Try,
1655 .Resume,
16731656 => {},
16741657 }
16751658
......@@ -1853,19 +1836,14 @@ pub const Node = struct {
18531836 var i = index;
18541837
18551838 switch (self.kind) {
1856 Kind.Break => |maybe_label| {
1839 .Break,
1840 .Continue => |maybe_label| {
18571841 if (maybe_label) |label| {
18581842 if (i < 1) return label;
18591843 i -= 1;
18601844 }
18611845 },
1862 Kind.Continue => |maybe_label| {
1863 if (maybe_label) |label| {
1864 if (i < 1) return label;
1865 i -= 1;
1866 }
1867 },
1868 Kind.Return => {},
1846 .Return => {},
18691847 }
18701848
18711849 if (self.rhs) |rhs| {
......@@ -1886,17 +1864,13 @@ pub const Node = struct {
18861864 }
18871865
18881866 switch (self.kind) {
1889 Kind.Break => |maybe_label| {
1890 if (maybe_label) |label| {
1891 return label.lastToken();
1892 }
1893 },
1894 Kind.Continue => |maybe_label| {
1867 .Break,
1868 .Continue => |maybe_label| {
18951869 if (maybe_label) |label| {
18961870 return label.lastToken();
18971871 }
18981872 },
1899 Kind.Return => return self.ltoken,
1873 .Return => return self.ltoken,
19001874 }
19011875
19021876 return self.ltoken;
......@@ -2137,11 +2111,11 @@ pub const Node = struct {
21372111 i -= 1;
21382112
21392113 switch (self.kind) {
2140 Kind.Variable => |variable_name| {
2114 .Variable => |variable_name| {
21412115 if (i < 1) return &variable_name.base;
21422116 i -= 1;
21432117 },
2144 Kind.Return => |return_type| {
2118 .Return => |return_type| {
21452119 if (i < 1) return return_type;
21462120 i -= 1;
21472121 },
lib/std/zig/parse.zig+336-351
......@@ -23,7 +23,7 @@ pub fn parse(allocator: *Allocator, source: []const u8) Allocator.Error!*Tree {
2323 var arena = std.heap.ArenaAllocator.init(allocator);
2424 errdefer arena.deinit();
2525 const tree = try arena.allocator.create(ast.Tree);
26 tree.* = ast.Tree{
26 tree.* = .{
2727 .source = source,
2828 .root_node = undefined,
2929 .arena_allocator = arena,
......@@ -66,10 +66,10 @@ pub fn parse(allocator: *Allocator, source: []const u8) Allocator.Error!*Tree {
6666/// Root <- skip ContainerMembers eof
6767fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!*Node.Root {
6868 const node = try arena.create(Node.Root);
69 node.* = Node.Root{
69 node.* = .{
7070 .decls = try parseContainerMembers(arena, it, tree),
7171 .eof_token = eatToken(it, .Eof) orelse {
72 try tree.errors.push(AstError{
72 try tree.errors.push(.{
7373 .ExpectedContainerMembers = .{ .token = it.index },
7474 });
7575 return error.ParseError;
......@@ -139,8 +139,8 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No
139139 }
140140
141141 if (visib_token != null) {
142 try tree.errors.push(AstError{
143 .ExpectedPubItem = AstError.ExpectedPubItem{ .token = it.index },
142 try tree.errors.push(.{
143 .ExpectedPubItem = .{ .token = it.index },
144144 });
145145 return error.ParseError;
146146 }
......@@ -157,8 +157,8 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No
157157
158158 // Dangling doc comment
159159 if (doc_comments != null) {
160 try tree.errors.push(AstError{
161 .UnattachedDocComment = AstError.UnattachedDocComment{ .token = doc_comments.?.firstToken() },
160 try tree.errors.push(.{
161 .UnattachedDocComment = .{ .token = doc_comments.?.firstToken() },
162162 });
163163 }
164164 break;
......@@ -177,7 +177,7 @@ fn parseContainerDocComments(arena: *Allocator, it: *TokenIterator, tree: *Tree)
177177 if (lines.len == 0) return null;
178178
179179 const node = try arena.create(Node.DocComment);
180 node.* = Node.DocComment{
180 node.* = .{
181181 .lines = lines,
182182 };
183183 return &node.base;
......@@ -186,15 +186,15 @@ fn parseContainerDocComments(arena: *Allocator, it: *TokenIterator, tree: *Tree)
186186/// TestDecl <- KEYWORD_test STRINGLITERALSINGLE Block
187187fn parseTestDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
188188 const test_token = eatToken(it, .Keyword_test) orelse return null;
189 const name_node = try expectNode(arena, it, tree, parseStringLiteralSingle, AstError{
190 .ExpectedStringLiteral = AstError.ExpectedStringLiteral{ .token = it.index },
189 const name_node = try expectNode(arena, it, tree, parseStringLiteralSingle, .{
190 .ExpectedStringLiteral = .{ .token = it.index },
191191 });
192 const block_node = try expectNode(arena, it, tree, parseBlock, AstError{
193 .ExpectedLBrace = AstError.ExpectedLBrace{ .token = it.index },
192 const block_node = try expectNode(arena, it, tree, parseBlock, .{
193 .ExpectedLBrace = .{ .token = it.index },
194194 });
195195
196196 const test_node = try arena.create(Node.TestDecl);
197 test_node.* = Node.TestDecl{
197 test_node.* = .{
198198 .doc_comments = null,
199199 .test_token = test_token,
200200 .name = name_node,
......@@ -211,12 +211,12 @@ fn parseTopLevelComptime(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*
211211 return null;
212212 };
213213 putBackToken(it, lbrace);
214 const block_node = try expectNode(arena, it, tree, parseBlockExpr, AstError{
215 .ExpectedLabelOrLBrace = AstError.ExpectedLabelOrLBrace{ .token = it.index },
214 const block_node = try expectNode(arena, it, tree, parseBlockExpr, .{
215 .ExpectedLabelOrLBrace = .{ .token = it.index },
216216 });
217217
218218 const comptime_node = try arena.create(Node.Comptime);
219 comptime_node.* = Node.Comptime{
219 comptime_node.* = .{
220220 .doc_comments = null,
221221 .comptime_token = tok,
222222 .expr = block_node,
......@@ -250,8 +250,8 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
250250 fn_node.body_node = body_node;
251251 return node;
252252 }
253 try tree.errors.push(AstError{
254 .ExpectedSemiOrLBrace = AstError.ExpectedSemiOrLBrace{ .token = it.index },
253 try tree.errors.push(.{
254 .ExpectedSemiOrLBrace = .{ .token = it.index },
255255 });
256256 return null;
257257 }
......@@ -277,8 +277,8 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
277277 }
278278
279279 if (thread_local_token != null) {
280 try tree.errors.push(AstError{
281 .ExpectedVarDecl = AstError.ExpectedVarDecl{ .token = it.index },
280 try tree.errors.push(.{
281 .ExpectedVarDecl = .{ .token = it.index },
282282 });
283283 return error.ParseError;
284284 }
......@@ -291,8 +291,8 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
291291 }
292292
293293 const use_node = (try parseUse(arena, it, tree)) orelse return null;
294 const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{
295 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
294 const expr_node = try expectNode(arena, it, tree, parseExpr, .{
295 .ExpectedExpr = .{ .token = it.index },
296296 });
297297 const semicolon_token = try expectToken(it, tree, .Semicolon);
298298 const use_node_raw = use_node.cast(Node.Use).?;
......@@ -310,7 +310,7 @@ fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
310310 if (fnCC == .Extern) {
311311 putBackToken(it, fnCC.Extern); // 'extern' is also used in ContainerDecl
312312 } else {
313 try tree.errors.push(AstError{
313 try tree.errors.push(.{
314314 .ExpectedToken = .{ .token = it.index, .expected_id = .Keyword_fn },
315315 });
316316 return error.ParseError;
......@@ -328,16 +328,16 @@ fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
328328 const exclamation_token = eatToken(it, .Bang);
329329
330330 const return_type_expr = (try parseVarType(arena, it, tree)) orelse
331 try expectNode(arena, it, tree, parseTypeExpr, AstError{
332 .ExpectedReturnType = AstError.ExpectedReturnType{ .token = it.index },
331 try expectNode(arena, it, tree, parseTypeExpr, .{
332 .ExpectedReturnType = .{ .token = it.index },
333333 });
334334
335 const return_type = if (exclamation_token != null)
336 Node.FnProto.ReturnType{
335 const return_type: Node.FnProto.ReturnType = if (exclamation_token != null)
336 .{
337337 .InferErrorSet = return_type_expr,
338338 }
339339 else
340 Node.FnProto.ReturnType{
340 .{
341341 .Explicit = return_type_expr,
342342 };
343343
......@@ -347,7 +347,7 @@ fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
347347 null;
348348
349349 const fn_proto_node = try arena.create(Node.FnProto);
350 fn_proto_node.* = Node.FnProto{
350 fn_proto_node.* = .{
351351 .doc_comments = null,
352352 .visib_token = null,
353353 .fn_token = fn_token,
......@@ -382,8 +382,8 @@ fn parseVarDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
382382
383383 const name_token = try expectToken(it, tree, .Identifier);
384384 const type_node = if (eatToken(it, .Colon) != null)
385 try expectNode(arena, it, tree, parseTypeExpr, AstError{
386 .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index },
385 try expectNode(arena, it, tree, parseTypeExpr, .{
386 .ExpectedTypeExpr = .{ .token = it.index },
387387 })
388388 else
389389 null;
......@@ -391,14 +391,14 @@ fn parseVarDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
391391 const section_node = try parseLinkSection(arena, it, tree);
392392 const eq_token = eatToken(it, .Equal);
393393 const init_node = if (eq_token != null) blk: {
394 break :blk try expectNode(arena, it, tree, parseExpr, AstError{
395 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
394 break :blk try expectNode(arena, it, tree, parseExpr, .{
395 .ExpectedExpr = .{ .token = it.index },
396396 });
397397 } else null;
398398 const semicolon_token = try expectToken(it, tree, .Semicolon);
399399
400400 const node = try arena.create(Node.VarDecl);
401 node.* = Node.VarDecl{
401 node.* = .{
402402 .doc_comments = null,
403403 .visib_token = null,
404404 .thread_local_token = null,
......@@ -433,22 +433,22 @@ fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
433433 node.* = .{ .token = var_tok };
434434 type_expr = &node.base;
435435 } else {
436 type_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{
437 .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index },
436 type_expr = try expectNode(arena, it, tree, parseTypeExpr, .{
437 .ExpectedTypeExpr = .{ .token = it.index },
438438 });
439439 align_expr = try parseByteAlign(arena, it, tree);
440440 }
441441 }
442442
443443 const value_expr = if (eatToken(it, .Equal)) |_|
444 try expectNode(arena, it, tree, parseExpr, AstError{
445 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
444 try expectNode(arena, it, tree, parseExpr, .{
445 .ExpectedExpr = .{ .token = it.index },
446446 })
447447 else
448448 null;
449449
450450 const node = try arena.create(Node.ContainerField);
451 node.* = Node.ContainerField{
451 node.* = .{
452452 .doc_comments = null,
453453 .comptime_token = comptime_token,
454454 .name_token = name_token,
......@@ -481,12 +481,12 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No
481481 }
482482
483483 if (comptime_token) |token| {
484 const block_expr = try expectNode(arena, it, tree, parseBlockExprStatement, AstError{
485 .ExpectedBlockOrAssignment = AstError.ExpectedBlockOrAssignment{ .token = it.index },
484 const block_expr = try expectNode(arena, it, tree, parseBlockExprStatement, .{
485 .ExpectedBlockOrAssignment = .{ .token = it.index },
486486 });
487487
488488 const node = try arena.create(Node.Comptime);
489 node.* = Node.Comptime{
489 node.* = .{
490490 .doc_comments = null,
491491 .comptime_token = token,
492492 .expr = block_expr,
......@@ -511,13 +511,13 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No
511511 const semicolon = eatToken(it, .Semicolon);
512512
513513 const body_node = if (semicolon == null) blk: {
514 break :blk try expectNode(arena, it, tree, parseBlockExprStatement, AstError{
515 .ExpectedBlockOrExpression = AstError.ExpectedBlockOrExpression{ .token = it.index },
514 break :blk try expectNode(arena, it, tree, parseBlockExprStatement, .{
515 .ExpectedBlockOrExpression = .{ .token = it.index },
516516 });
517517 } else null;
518518
519519 const node = try arena.create(Node.Suspend);
520 node.* = Node.Suspend{
520 node.* = .{
521521 .suspend_token = suspend_token,
522522 .body = body_node,
523523 };
......@@ -526,11 +526,11 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No
526526
527527 const defer_token = eatToken(it, .Keyword_defer) orelse eatToken(it, .Keyword_errdefer);
528528 if (defer_token) |token| {
529 const expr_node = try expectNode(arena, it, tree, parseBlockExprStatement, AstError{
530 .ExpectedBlockOrExpression = AstError.ExpectedBlockOrExpression{ .token = it.index },
529 const expr_node = try expectNode(arena, it, tree, parseBlockExprStatement, .{
530 .ExpectedBlockOrExpression = .{ .token = it.index },
531531 });
532532 const node = try arena.create(Node.Defer);
533 node.* = Node.Defer{
533 node.* = .{
534534 .defer_token = token,
535535 .expr = expr_node,
536536 };
......@@ -561,8 +561,8 @@ fn parseIfStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
561561 } else null;
562562
563563 if (block_expr == null and assign_expr == null) {
564 try tree.errors.push(AstError{
565 .ExpectedBlockOrAssignment = AstError.ExpectedBlockOrAssignment{ .token = it.index },
564 try tree.errors.push(.{
565 .ExpectedBlockOrAssignment = .{ .token = it.index },
566566 });
567567 return error.ParseError;
568568 }
......@@ -572,12 +572,12 @@ fn parseIfStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
572572 const else_node = if (semicolon == null) blk: {
573573 const else_token = eatToken(it, .Keyword_else) orelse break :blk null;
574574 const payload = try parsePayload(arena, it, tree);
575 const else_body = try expectNode(arena, it, tree, parseStatement, AstError{
576 .InvalidToken = AstError.InvalidToken{ .token = it.index },
575 const else_body = try expectNode(arena, it, tree, parseStatement, .{
576 .InvalidToken = .{ .token = it.index },
577577 });
578578
579579 const node = try arena.create(Node.Else);
580 node.* = Node.Else{
580 node.* = .{
581581 .else_token = else_token,
582582 .payload = payload,
583583 .body = else_body,
......@@ -599,8 +599,8 @@ fn parseIfStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
599599 if_prefix.@"else" = else_node;
600600 return if_node;
601601 }
602 try tree.errors.push(AstError{
603 .ExpectedSemiOrElse = AstError.ExpectedSemiOrElse{ .token = it.index },
602 try tree.errors.push(.{
603 .ExpectedSemiOrElse = .{ .token = it.index },
604604 });
605605 return error.ParseError;
606606 }
......@@ -628,8 +628,8 @@ fn parseLabeledStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*
628628 }
629629
630630 if (label_token != null) {
631 try tree.errors.push(AstError{
632 .ExpectedLabelable = AstError.ExpectedLabelable{ .token = it.index },
631 try tree.errors.push(.{
632 .ExpectedLabelable = .{ .token = it.index },
633633 });
634634 return error.ParseError;
635635 }
......@@ -665,12 +665,12 @@ fn parseForStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
665665 for_prefix.body = block_expr_node;
666666
667667 if (eatToken(it, .Keyword_else)) |else_token| {
668 const statement_node = try expectNode(arena, it, tree, parseStatement, AstError{
669 .InvalidToken = AstError.InvalidToken{ .token = it.index },
668 const statement_node = try expectNode(arena, it, tree, parseStatement, .{
669 .InvalidToken = .{ .token = it.index },
670670 });
671671
672672 const else_node = try arena.create(Node.Else);
673 else_node.* = Node.Else{
673 else_node.* = .{
674674 .else_token = else_token,
675675 .payload = null,
676676 .body = statement_node,
......@@ -689,12 +689,12 @@ fn parseForStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
689689 if (eatToken(it, .Semicolon) != null) return node;
690690
691691 if (eatToken(it, .Keyword_else)) |else_token| {
692 const statement_node = try expectNode(arena, it, tree, parseStatement, AstError{
693 .ExpectedStatement = AstError.ExpectedStatement{ .token = it.index },
692 const statement_node = try expectNode(arena, it, tree, parseStatement, .{
693 .ExpectedStatement = .{ .token = it.index },
694694 });
695695
696696 const else_node = try arena.create(Node.Else);
697 else_node.* = Node.Else{
697 else_node.* = .{
698698 .else_token = else_token,
699699 .payload = null,
700700 .body = statement_node,
......@@ -703,8 +703,8 @@ fn parseForStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
703703 return node;
704704 }
705705
706 try tree.errors.push(AstError{
707 .ExpectedSemiOrElse = AstError.ExpectedSemiOrElse{ .token = it.index },
706 try tree.errors.push(.{
707 .ExpectedSemiOrElse = .{ .token = it.index },
708708 });
709709 return null;
710710 }
......@@ -725,12 +725,12 @@ fn parseWhileStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
725725 if (eatToken(it, .Keyword_else)) |else_token| {
726726 const payload = try parsePayload(arena, it, tree);
727727
728 const statement_node = try expectNode(arena, it, tree, parseStatement, AstError{
729 .InvalidToken = AstError.InvalidToken{ .token = it.index },
728 const statement_node = try expectNode(arena, it, tree, parseStatement, .{
729 .InvalidToken = .{ .token = it.index },
730730 });
731731
732732 const else_node = try arena.create(Node.Else);
733 else_node.* = Node.Else{
733 else_node.* = .{
734734 .else_token = else_token,
735735 .payload = payload,
736736 .body = statement_node,
......@@ -751,12 +751,12 @@ fn parseWhileStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
751751 if (eatToken(it, .Keyword_else)) |else_token| {
752752 const payload = try parsePayload(arena, it, tree);
753753
754 const statement_node = try expectNode(arena, it, tree, parseStatement, AstError{
755 .ExpectedStatement = AstError.ExpectedStatement{ .token = it.index },
754 const statement_node = try expectNode(arena, it, tree, parseStatement, .{
755 .ExpectedStatement = .{ .token = it.index },
756756 });
757757
758758 const else_node = try arena.create(Node.Else);
759 else_node.* = Node.Else{
759 else_node.* = .{
760760 .else_token = else_token,
761761 .payload = payload,
762762 .body = statement_node,
......@@ -765,8 +765,8 @@ fn parseWhileStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
765765 return node;
766766 }
767767
768 try tree.errors.push(AstError{
769 .ExpectedSemiOrElse = AstError.ExpectedSemiOrElse{ .token = it.index },
768 try tree.errors.push(.{
769 .ExpectedSemiOrElse = .{ .token = it.index },
770770 });
771771 return null;
772772 }
......@@ -894,8 +894,8 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
894894 }
895895
896896 if (eatToken(it, .Keyword_comptime)) |token| {
897 const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{
898 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
897 const expr_node = try expectNode(arena, it, tree, parseExpr, .{
898 .ExpectedExpr = .{ .token = it.index },
899899 });
900900 const node = try arena.create(Node.Comptime);
901901 node.* = .{
......@@ -907,8 +907,8 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
907907 }
908908
909909 if (eatToken(it, .Keyword_noasync)) |token| {
910 const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{
911 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
910 const expr_node = try expectNode(arena, it, tree, parseExpr, .{
911 .ExpectedExpr = .{ .token = it.index },
912912 });
913913 const node = try arena.create(Node.Noasync);
914914 node.* = .{
......@@ -930,13 +930,13 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
930930 }
931931
932932 if (eatToken(it, .Keyword_resume)) |token| {
933 const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{
934 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
933 const expr_node = try expectNode(arena, it, tree, parseExpr, .{
934 .ExpectedExpr = .{ .token = it.index },
935935 });
936936 const node = try arena.create(Node.PrefixOp);
937937 node.* = .{
938938 .op_token = token,
939 .op = Node.PrefixOp.Op.Resume,
939 .op = .Resume,
940940 .rhs = expr_node,
941941 };
942942 return &node.base;
......@@ -992,7 +992,7 @@ fn parseBlock(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
992992 const rbrace = try expectToken(it, tree, .RBrace);
993993
994994 const block_node = try arena.create(Node.Block);
995 block_node.* = Node.Block{
995 block_node.* = .{
996996 .label = null,
997997 .lbrace = lbrace,
998998 .statements = statements,
......@@ -1019,8 +1019,8 @@ fn parseLoopExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
10191019 if (inline_token == null) return null;
10201020
10211021 // If we've seen "inline", there should have been a "for" or "while"
1022 try tree.errors.push(AstError{
1023 .ExpectedInlinable = AstError.ExpectedInlinable{ .token = it.index },
1022 try tree.errors.push(.{
1023 .ExpectedInlinable = .{ .token = it.index },
10241024 });
10251025 return error.ParseError;
10261026}
......@@ -1030,18 +1030,18 @@ fn parseForExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
10301030 const node = (try parseForPrefix(arena, it, tree)) orelse return null;
10311031 const for_prefix = node.cast(Node.For).?;
10321032
1033 const body_node = try expectNode(arena, it, tree, parseExpr, AstError{
1034 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
1033 const body_node = try expectNode(arena, it, tree, parseExpr, .{
1034 .ExpectedExpr = .{ .token = it.index },
10351035 });
10361036 for_prefix.body = body_node;
10371037
10381038 if (eatToken(it, .Keyword_else)) |else_token| {
1039 const body = try expectNode(arena, it, tree, parseExpr, AstError{
1040 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
1039 const body = try expectNode(arena, it, tree, parseExpr, .{
1040 .ExpectedExpr = .{ .token = it.index },
10411041 });
10421042
10431043 const else_node = try arena.create(Node.Else);
1044 else_node.* = Node.Else{
1044 else_node.* = .{
10451045 .else_token = else_token,
10461046 .payload = null,
10471047 .body = body,
......@@ -1058,19 +1058,19 @@ fn parseWhileExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
10581058 const node = (try parseWhilePrefix(arena, it, tree)) orelse return null;
10591059 const while_prefix = node.cast(Node.While).?;
10601060
1061 const body_node = try expectNode(arena, it, tree, parseExpr, AstError{
1062 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
1061 const body_node = try expectNode(arena, it, tree, parseExpr, .{
1062 .ExpectedExpr = .{ .token = it.index },
10631063 });
10641064 while_prefix.body = body_node;
10651065
10661066 if (eatToken(it, .Keyword_else)) |else_token| {
10671067 const payload = try parsePayload(arena, it, tree);
1068 const body = try expectNode(arena, it, tree, parseExpr, AstError{
1069 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
1068 const body = try expectNode(arena, it, tree, parseExpr, .{
1069 .ExpectedExpr = .{ .token = it.index },
10701070 });
10711071
10721072 const else_node = try arena.create(Node.Else);
1073 else_node.* = Node.Else{
1073 else_node.* = .{
10741074 .else_token = else_token,
10751075 .payload = payload,
10761076 .body = body,
......@@ -1098,14 +1098,14 @@ fn parseInitList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.Suf
10981098 const lbrace = eatToken(it, .LBrace) orelse return null;
10991099 var init_list = Node.SuffixOp.Op.InitList.init(arena);
11001100
1101 const op = blk: {
1101 const op: Node.SuffixOp.Op = blk: {
11021102 if (try parseFieldInit(arena, it, tree)) |field_init| {
11031103 try init_list.push(field_init);
11041104 while (eatToken(it, .Comma)) |_| {
11051105 const next = (try parseFieldInit(arena, it, tree)) orelse break;
11061106 try init_list.push(next);
11071107 }
1108 break :blk Node.SuffixOp.Op{ .StructInitializer = init_list };
1108 break :blk .{ .StructInitializer = init_list };
11091109 }
11101110
11111111 if (try parseExpr(arena, it, tree)) |expr| {
......@@ -1114,14 +1114,14 @@ fn parseInitList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.Suf
11141114 const next = (try parseExpr(arena, it, tree)) orelse break;
11151115 try init_list.push(next);
11161116 }
1117 break :blk Node.SuffixOp.Op{ .ArrayInitializer = init_list };
1117 break :blk .{ .ArrayInitializer = init_list };
11181118 }
11191119
1120 break :blk Node.SuffixOp.Op{ .StructInitializer = init_list };
1120 break :blk .{ .StructInitializer = init_list };
11211121 };
11221122
11231123 const node = try arena.create(Node.SuffixOp);
1124 node.* = Node.SuffixOp{
1124 node.* = .{
11251125 .lhs = .{ .node = undefined }, // set by caller
11261126 .op = op,
11271127 .rtoken = try expectToken(it, tree, .RBrace),
......@@ -1140,8 +1140,8 @@ fn parseErrorUnionExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
11401140
11411141 if (try SimpleBinOpParseFn(.Bang, Node.InfixOp.Op.ErrorUnion)(arena, it, tree)) |node| {
11421142 const error_union = node.cast(Node.InfixOp).?;
1143 const type_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{
1144 .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index },
1143 const type_expr = try expectNode(arena, it, tree, parseTypeExpr, .{
1144 .ExpectedTypeExpr = .{ .token = it.index },
11451145 });
11461146 error_union.lhs = suffix_expr;
11471147 error_union.rhs = type_expr;
......@@ -1168,8 +1168,8 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
11681168 return parsePrimaryTypeExpr(arena, it, tree);
11691169 }
11701170 // TODO: Implement hack for parsing `async fn ...` in ast_parse_suffix_expr
1171 var res = try expectNode(arena, it, tree, parsePrimaryTypeExpr, AstError{
1172 .ExpectedPrimaryTypeExpr = AstError.ExpectedPrimaryTypeExpr{ .token = it.index },
1171 var res = try expectNode(arena, it, tree, parsePrimaryTypeExpr, .{
1172 .ExpectedPrimaryTypeExpr = .{ .token = it.index },
11731173 });
11741174
11751175 while (try parseSuffixOp(arena, it, tree)) |node| {
......@@ -1182,16 +1182,16 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
11821182 }
11831183
11841184 const params = (try parseFnCallArguments(arena, it, tree)) orelse {
1185 try tree.errors.push(AstError{
1186 .ExpectedParamList = AstError.ExpectedParamList{ .token = it.index },
1185 try tree.errors.push(.{
1186 .ExpectedParamList = .{ .token = it.index },
11871187 });
11881188 return null;
11891189 };
11901190 const node = try arena.create(Node.SuffixOp);
1191 node.* = Node.SuffixOp{
1191 node.* = .{
11921192 .lhs = .{ .node = res },
1193 .op = Node.SuffixOp.Op{
1194 .Call = Node.SuffixOp.Op.Call{
1193 .op = .{
1194 .Call = .{
11951195 .params = params.list,
11961196 .async_token = async_token,
11971197 },
......@@ -1215,10 +1215,10 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
12151215 }
12161216 if (try parseFnCallArguments(arena, it, tree)) |params| {
12171217 const call = try arena.create(Node.SuffixOp);
1218 call.* = Node.SuffixOp{
1218 call.* = .{
12191219 .lhs = .{ .node = res },
1220 .op = Node.SuffixOp.Op{
1221 .Call = Node.SuffixOp.Op.Call{
1220 .op = .{
1221 .Call = .{
12221222 .params = params.list,
12231223 .async_token = null,
12241224 },
......@@ -1264,7 +1264,7 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N
12641264 if (try parseBuiltinCall(arena, it, tree)) |node| return node;
12651265 if (eatToken(it, .CharLiteral)) |token| {
12661266 const node = try arena.create(Node.CharLiteral);
1267 node.* = Node.CharLiteral{
1267 node.* = .{
12681268 .token = token,
12691269 };
12701270 return &node.base;
......@@ -1300,15 +1300,15 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N
13001300 }
13011301 if (eatToken(it, .Keyword_error)) |token| {
13021302 const period = try expectToken(it, tree, .Period);
1303 const identifier = try expectNode(arena, it, tree, parseIdentifier, AstError{
1304 .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index },
1303 const identifier = try expectNode(arena, it, tree, parseIdentifier, .{
1304 .ExpectedIdentifier = .{ .token = it.index },
13051305 });
13061306 const global_error_set = try createLiteral(arena, Node.ErrorType, token);
13071307 const node = try arena.create(Node.InfixOp);
13081308 node.* = .{
13091309 .op_token = period,
13101310 .lhs = global_error_set,
1311 .op = Node.InfixOp.Op.Period,
1311 .op = .Period,
13121312 .rhs = identifier,
13131313 };
13141314 return &node.base;
......@@ -1358,7 +1358,7 @@ fn parseErrorSetDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
13581358 const rbrace = try expectToken(it, tree, .RBrace);
13591359
13601360 const node = try arena.create(Node.ErrorSetDecl);
1361 node.* = Node.ErrorSetDecl{
1361 node.* = .{
13621362 .error_token = error_token,
13631363 .decls = decls,
13641364 .rbrace_token = rbrace,
......@@ -1369,13 +1369,13 @@ fn parseErrorSetDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
13691369/// GroupedExpr <- LPAREN Expr RPAREN
13701370fn parseGroupedExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
13711371 const lparen = eatToken(it, .LParen) orelse return null;
1372 const expr = try expectNode(arena, it, tree, parseExpr, AstError{
1373 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
1372 const expr = try expectNode(arena, it, tree, parseExpr, .{
1373 .ExpectedExpr = .{ .token = it.index },
13741374 });
13751375 const rparen = try expectToken(it, tree, .RParen);
13761376
13771377 const node = try arena.create(Node.GroupedExpression);
1378 node.* = Node.GroupedExpression{
1378 node.* = .{
13791379 .lparen = lparen,
13801380 .expr = expr,
13811381 .rparen = rparen,
......@@ -1435,8 +1435,8 @@ fn parseLoopTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
14351435 if (inline_token == null) return null;
14361436
14371437 // If we've seen "inline", there should have been a "for" or "while"
1438 try tree.errors.push(AstError{
1439 .ExpectedInlinable = AstError.ExpectedInlinable{ .token = it.index },
1438 try tree.errors.push(.{
1439 .ExpectedInlinable = .{ .token = it.index },
14401440 });
14411441 return error.ParseError;
14421442}
......@@ -1446,18 +1446,18 @@ fn parseForTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
14461446 const node = (try parseForPrefix(arena, it, tree)) orelse return null;
14471447 const for_prefix = node.cast(Node.For).?;
14481448
1449 const type_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{
1450 .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index },
1449 const type_expr = try expectNode(arena, it, tree, parseTypeExpr, .{
1450 .ExpectedTypeExpr = .{ .token = it.index },
14511451 });
14521452 for_prefix.body = type_expr;
14531453
14541454 if (eatToken(it, .Keyword_else)) |else_token| {
1455 const else_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{
1456 .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index },
1455 const else_expr = try expectNode(arena, it, tree, parseTypeExpr, .{
1456 .ExpectedTypeExpr = .{ .token = it.index },
14571457 });
14581458
14591459 const else_node = try arena.create(Node.Else);
1460 else_node.* = Node.Else{
1460 else_node.* = .{
14611461 .else_token = else_token,
14621462 .payload = null,
14631463 .body = else_expr,
......@@ -1474,20 +1474,20 @@ fn parseWhileTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Nod
14741474 const node = (try parseWhilePrefix(arena, it, tree)) orelse return null;
14751475 const while_prefix = node.cast(Node.While).?;
14761476
1477 const type_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{
1478 .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index },
1477 const type_expr = try expectNode(arena, it, tree, parseTypeExpr, .{
1478 .ExpectedTypeExpr = .{ .token = it.index },
14791479 });
14801480 while_prefix.body = type_expr;
14811481
14821482 if (eatToken(it, .Keyword_else)) |else_token| {
14831483 const payload = try parsePayload(arena, it, tree);
14841484
1485 const else_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{
1486 .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index },
1485 const else_expr = try expectNode(arena, it, tree, parseTypeExpr, .{
1486 .ExpectedTypeExpr = .{ .token = it.index },
14871487 });
14881488
14891489 const else_node = try arena.create(Node.Else);
1490 else_node.* = Node.Else{
1490 else_node.* = .{
14911491 .else_token = else_token,
14921492 .payload = null,
14931493 .body = else_expr,
......@@ -1503,8 +1503,8 @@ fn parseWhileTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Nod
15031503fn parseSwitchExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
15041504 const switch_token = eatToken(it, .Keyword_switch) orelse return null;
15051505 _ = try expectToken(it, tree, .LParen);
1506 const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{
1507 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
1506 const expr_node = try expectNode(arena, it, tree, parseExpr, .{
1507 .ExpectedExpr = .{ .token = it.index },
15081508 });
15091509 _ = try expectToken(it, tree, .RParen);
15101510 _ = try expectToken(it, tree, .LBrace);
......@@ -1512,7 +1512,7 @@ fn parseSwitchExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
15121512 const rbrace = try expectToken(it, tree, .RBrace);
15131513
15141514 const node = try arena.create(Node.Switch);
1515 node.* = Node.Switch{
1515 node.* = .{
15161516 .switch_token = switch_token,
15171517 .expr = expr_node,
15181518 .cases = cases,
......@@ -1526,12 +1526,12 @@ fn parseAsmExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
15261526 const asm_token = eatToken(it, .Keyword_asm) orelse return null;
15271527 const volatile_token = eatToken(it, .Keyword_volatile);
15281528 _ = try expectToken(it, tree, .LParen);
1529 const template = try expectNode(arena, it, tree, parseExpr, AstError{
1530 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
1529 const template = try expectNode(arena, it, tree, parseExpr, .{
1530 .ExpectedExpr = .{ .token = it.index },
15311531 });
15321532
15331533 const node = try arena.create(Node.Asm);
1534 node.* = Node.Asm{
1534 node.* = .{
15351535 .asm_token = asm_token,
15361536 .volatile_token = volatile_token,
15371537 .template = template,
......@@ -1553,7 +1553,7 @@ fn parseAnonLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
15531553 // anon enum literal
15541554 if (eatToken(it, .Identifier)) |name| {
15551555 const node = try arena.create(Node.EnumLiteral);
1556 node.* = Node.EnumLiteral{
1556 node.* = .{
15571557 .dot = dot,
15581558 .name = name,
15591559 };
......@@ -1580,32 +1580,32 @@ fn parseAsmOutput(arena: *Allocator, it: *TokenIterator, tree: *Tree, asm_node:
15801580/// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
15811581fn parseAsmOutputItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.AsmOutput {
15821582 const lbracket = eatToken(it, .LBracket) orelse return null;
1583 const name = try expectNode(arena, it, tree, parseIdentifier, AstError{
1584 .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index },
1583 const name = try expectNode(arena, it, tree, parseIdentifier, .{
1584 .ExpectedIdentifier = .{ .token = it.index },
15851585 });
15861586 _ = try expectToken(it, tree, .RBracket);
15871587
1588 const constraint = try expectNode(arena, it, tree, parseStringLiteral, AstError{
1589 .ExpectedStringLiteral = AstError.ExpectedStringLiteral{ .token = it.index },
1588 const constraint = try expectNode(arena, it, tree, parseStringLiteral, .{
1589 .ExpectedStringLiteral = .{ .token = it.index },
15901590 });
15911591
15921592 _ = try expectToken(it, tree, .LParen);
1593 const kind = blk: {
1593 const kind: Node.AsmOutput.Kind = blk: {
15941594 if (eatToken(it, .Arrow) != null) {
1595 const return_ident = try expectNode(arena, it, tree, parseTypeExpr, AstError{
1596 .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index },
1595 const return_ident = try expectNode(arena, it, tree, parseTypeExpr, .{
1596 .ExpectedTypeExpr = .{ .token = it.index },
15971597 });
1598 break :blk Node.AsmOutput.Kind{ .Return = return_ident };
1598 break :blk .{ .Return = return_ident };
15991599 }
1600 const variable = try expectNode(arena, it, tree, parseIdentifier, AstError{
1601 .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index },
1600 const variable = try expectNode(arena, it, tree, parseIdentifier, .{
1601 .ExpectedIdentifier = .{ .token = it.index },
16021602 });
1603 break :blk Node.AsmOutput.Kind{ .Variable = variable.cast(Node.Identifier).? };
1603 break :blk .{ .Variable = variable.cast(Node.Identifier).? };
16041604 };
16051605 const rparen = try expectToken(it, tree, .RParen);
16061606
16071607 const node = try arena.create(Node.AsmOutput);
1608 node.* = Node.AsmOutput{
1608 node.* = .{
16091609 .lbracket = lbracket,
16101610 .symbolic_name = name,
16111611 .constraint = constraint,
......@@ -1625,23 +1625,23 @@ fn parseAsmInput(arena: *Allocator, it: *TokenIterator, tree: *Tree, asm_node: *
16251625/// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN
16261626fn parseAsmInputItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.AsmInput {
16271627 const lbracket = eatToken(it, .LBracket) orelse return null;
1628 const name = try expectNode(arena, it, tree, parseIdentifier, AstError{
1629 .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index },
1628 const name = try expectNode(arena, it, tree, parseIdentifier, .{
1629 .ExpectedIdentifier = .{ .token = it.index },
16301630 });
16311631 _ = try expectToken(it, tree, .RBracket);
16321632
1633 const constraint = try expectNode(arena, it, tree, parseStringLiteral, AstError{
1634 .ExpectedStringLiteral = AstError.ExpectedStringLiteral{ .token = it.index },
1633 const constraint = try expectNode(arena, it, tree, parseStringLiteral, .{
1634 .ExpectedStringLiteral = .{ .token = it.index },
16351635 });
16361636
16371637 _ = try expectToken(it, tree, .LParen);
1638 const expr = try expectNode(arena, it, tree, parseExpr, AstError{
1639 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
1638 const expr = try expectNode(arena, it, tree, parseExpr, .{
1639 .ExpectedExpr = .{ .token = it.index },
16401640 });
16411641 const rparen = try expectToken(it, tree, .RParen);
16421642
16431643 const node = try arena.create(Node.AsmInput);
1644 node.* = Node.AsmInput{
1644 node.* = .{
16451645 .lbracket = lbracket,
16461646 .symbolic_name = name,
16471647 .constraint = constraint,
......@@ -1664,8 +1664,8 @@ fn parseAsmClobbers(arena: *Allocator, it: *TokenIterator, tree: *Tree, asm_node
16641664/// BreakLabel <- COLON IDENTIFIER
16651665fn parseBreakLabel(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
16661666 _ = eatToken(it, .Colon) orelse return null;
1667 return try expectNode(arena, it, tree, parseIdentifier, AstError{
1668 .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index },
1667 return try expectNode(arena, it, tree, parseIdentifier, .{
1668 .ExpectedIdentifier = .{ .token = it.index },
16691669 });
16701670}
16711671
......@@ -1694,12 +1694,12 @@ fn parseFieldInit(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
16941694 putBackToken(it, period_token);
16951695 return null;
16961696 };
1697 const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{
1698 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
1697 const expr_node = try expectNode(arena, it, tree, parseExpr, .{
1698 .ExpectedExpr = .{ .token = it.index },
16991699 });
17001700
17011701 const node = try arena.create(Node.FieldInitializer);
1702 node.* = Node.FieldInitializer{
1702 node.* = .{
17031703 .period_token = period_token,
17041704 .name_token = name_token,
17051705 .expr = expr_node,
......@@ -1711,8 +1711,8 @@ fn parseFieldInit(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
17111711fn parseWhileContinueExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
17121712 _ = eatToken(it, .Colon) orelse return null;
17131713 _ = try expectToken(it, tree, .LParen);
1714 const node = try expectNode(arena, it, tree, parseAssignExpr, AstError{
1715 .ExpectedExprOrAssignment = AstError.ExpectedExprOrAssignment{ .token = it.index },
1714 const node = try expectNode(arena, it, tree, parseAssignExpr, .{
1715 .ExpectedExprOrAssignment = .{ .token = it.index },
17161716 });
17171717 _ = try expectToken(it, tree, .RParen);
17181718 return node;
......@@ -1722,8 +1722,8 @@ fn parseWhileContinueExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?
17221722fn parseLinkSection(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
17231723 _ = eatToken(it, .Keyword_linksection) orelse return null;
17241724 _ = try expectToken(it, tree, .LParen);
1725 const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{
1726 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
1725 const expr_node = try expectNode(arena, it, tree, parseExpr, .{
1726 .ExpectedExpr = .{ .token = it.index },
17271727 });
17281728 _ = try expectToken(it, tree, .RParen);
17291729 return expr_node;
......@@ -1733,8 +1733,8 @@ fn parseLinkSection(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
17331733fn parseCallconv(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
17341734 _ = eatToken(it, .Keyword_callconv) orelse return null;
17351735 _ = try expectToken(it, tree, .LParen);
1736 const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{
1737 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
1736 const expr_node = try expectNode(arena, it, tree, parseExpr, .{
1737 .ExpectedExpr = .{ .token = it.index },
17381738 });
17391739 _ = try expectToken(it, tree, .RParen);
17401740 return expr_node;
......@@ -1775,14 +1775,14 @@ fn parseParamDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
17751775 comptime_token == null and
17761776 name_token == null and
17771777 doc_comments == null) return null;
1778 try tree.errors.push(AstError{
1779 .ExpectedParamType = AstError.ExpectedParamType{ .token = it.index },
1778 try tree.errors.push(.{
1779 .ExpectedParamType = .{ .token = it.index },
17801780 });
17811781 return error.ParseError;
17821782 };
17831783
17841784 const param_decl = try arena.create(Node.ParamDecl);
1785 param_decl.* = Node.ParamDecl{
1785 param_decl.* = .{
17861786 .doc_comments = doc_comments,
17871787 .comptime_token = comptime_token,
17881788 .noalias_token = noalias_token,
......@@ -1821,14 +1821,14 @@ const ParamType = union(enum) {
18211821fn parseIfPrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
18221822 const if_token = eatToken(it, .Keyword_if) orelse return null;
18231823 _ = try expectToken(it, tree, .LParen);
1824 const condition = try expectNode(arena, it, tree, parseExpr, AstError{
1825 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
1824 const condition = try expectNode(arena, it, tree, parseExpr, .{
1825 .ExpectedExpr = .{ .token = it.index },
18261826 });
18271827 _ = try expectToken(it, tree, .RParen);
18281828 const payload = try parsePtrPayload(arena, it, tree);
18291829
18301830 const node = try arena.create(Node.If);
1831 node.* = Node.If{
1831 node.* = .{
18321832 .if_token = if_token,
18331833 .condition = condition,
18341834 .payload = payload,
......@@ -1843,8 +1843,8 @@ fn parseWhilePrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
18431843 const while_token = eatToken(it, .Keyword_while) orelse return null;
18441844
18451845 _ = try expectToken(it, tree, .LParen);
1846 const condition = try expectNode(arena, it, tree, parseExpr, AstError{
1847 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
1846 const condition = try expectNode(arena, it, tree, parseExpr, .{
1847 .ExpectedExpr = .{ .token = it.index },
18481848 });
18491849 _ = try expectToken(it, tree, .RParen);
18501850
......@@ -1852,7 +1852,7 @@ fn parseWhilePrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
18521852 const continue_expr = try parseWhileContinueExpr(arena, it, tree);
18531853
18541854 const node = try arena.create(Node.While);
1855 node.* = Node.While{
1855 node.* = .{
18561856 .label = null,
18571857 .inline_token = null,
18581858 .while_token = while_token,
......@@ -1870,17 +1870,17 @@ fn parseForPrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
18701870 const for_token = eatToken(it, .Keyword_for) orelse return null;
18711871
18721872 _ = try expectToken(it, tree, .LParen);
1873 const array_expr = try expectNode(arena, it, tree, parseExpr, AstError{
1874 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
1873 const array_expr = try expectNode(arena, it, tree, parseExpr, .{
1874 .ExpectedExpr = .{ .token = it.index },
18751875 });
18761876 _ = try expectToken(it, tree, .RParen);
18771877
1878 const payload = try expectNode(arena, it, tree, parsePtrIndexPayload, AstError{
1879 .ExpectedPayload = AstError.ExpectedPayload{ .token = it.index },
1878 const payload = try expectNode(arena, it, tree, parsePtrIndexPayload, .{
1879 .ExpectedPayload = .{ .token = it.index },
18801880 });
18811881
18821882 const node = try arena.create(Node.For);
1883 node.* = Node.For{
1883 node.* = .{
18841884 .label = null,
18851885 .inline_token = null,
18861886 .for_token = for_token,
......@@ -1895,13 +1895,13 @@ fn parseForPrefix(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
18951895/// Payload <- PIPE IDENTIFIER PIPE
18961896fn parsePayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
18971897 const lpipe = eatToken(it, .Pipe) orelse return null;
1898 const identifier = try expectNode(arena, it, tree, parseIdentifier, AstError{
1899 .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index },
1898 const identifier = try expectNode(arena, it, tree, parseIdentifier, .{
1899 .ExpectedIdentifier = .{ .token = it.index },
19001900 });
19011901 const rpipe = try expectToken(it, tree, .Pipe);
19021902
19031903 const node = try arena.create(Node.Payload);
1904 node.* = Node.Payload{
1904 node.* = .{
19051905 .lpipe = lpipe,
19061906 .error_symbol = identifier,
19071907 .rpipe = rpipe,
......@@ -1913,13 +1913,13 @@ fn parsePayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
19131913fn parsePtrPayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
19141914 const lpipe = eatToken(it, .Pipe) orelse return null;
19151915 const asterisk = eatToken(it, .Asterisk);
1916 const identifier = try expectNode(arena, it, tree, parseIdentifier, AstError{
1917 .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index },
1916 const identifier = try expectNode(arena, it, tree, parseIdentifier, .{
1917 .ExpectedIdentifier = .{ .token = it.index },
19181918 });
19191919 const rpipe = try expectToken(it, tree, .Pipe);
19201920
19211921 const node = try arena.create(Node.PointerPayload);
1922 node.* = Node.PointerPayload{
1922 node.* = .{
19231923 .lpipe = lpipe,
19241924 .ptr_token = asterisk,
19251925 .value_symbol = identifier,
......@@ -1932,21 +1932,21 @@ fn parsePtrPayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
19321932fn parsePtrIndexPayload(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
19331933 const lpipe = eatToken(it, .Pipe) orelse return null;
19341934 const asterisk = eatToken(it, .Asterisk);
1935 const identifier = try expectNode(arena, it, tree, parseIdentifier, AstError{
1936 .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index },
1935 const identifier = try expectNode(arena, it, tree, parseIdentifier, .{
1936 .ExpectedIdentifier = .{ .token = it.index },
19371937 });
19381938
19391939 const index = if (eatToken(it, .Comma) == null)
19401940 null
19411941 else
1942 try expectNode(arena, it, tree, parseIdentifier, AstError{
1943 .ExpectedIdentifier = AstError.ExpectedIdentifier{ .token = it.index },
1942 try expectNode(arena, it, tree, parseIdentifier, .{
1943 .ExpectedIdentifier = .{ .token = it.index },
19441944 });
19451945
19461946 const rpipe = try expectToken(it, tree, .Pipe);
19471947
19481948 const node = try arena.create(Node.PointerIndexPayload);
1949 node.* = Node.PointerIndexPayload{
1949 node.* = .{
19501950 .lpipe = lpipe,
19511951 .ptr_token = asterisk,
19521952 .value_symbol = identifier,
......@@ -1961,8 +1961,8 @@ fn parseSwitchProng(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
19611961 const node = (try parseSwitchCase(arena, it, tree)) orelse return null;
19621962 const arrow = try expectToken(it, tree, .EqualAngleBracketRight);
19631963 const payload = try parsePtrPayload(arena, it, tree);
1964 const expr = try expectNode(arena, it, tree, parseAssignExpr, AstError{
1965 .ExpectedExprOrAssignment = AstError.ExpectedExprOrAssignment{ .token = it.index },
1964 const expr = try expectNode(arena, it, tree, parseAssignExpr, .{
1965 .ExpectedExprOrAssignment = .{ .token = it.index },
19661966 });
19671967
19681968 const switch_case = node.cast(Node.SwitchCase).?;
......@@ -1987,14 +1987,14 @@ fn parseSwitchCase(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
19871987 }
19881988 } else if (eatToken(it, .Keyword_else)) |else_token| {
19891989 const else_node = try arena.create(Node.SwitchElse);
1990 else_node.* = Node.SwitchElse{
1990 else_node.* = .{
19911991 .token = else_token,
19921992 };
19931993 try list.push(&else_node.base);
19941994 } else return null;
19951995
19961996 const node = try arena.create(Node.SwitchCase);
1997 node.* = Node.SwitchCase{
1997 node.* = .{
19981998 .items = list,
19991999 .arrow_token = undefined, // set by caller
20002000 .payload = null,
......@@ -2007,15 +2007,15 @@ fn parseSwitchCase(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
20072007fn parseSwitchItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
20082008 const expr = (try parseExpr(arena, it, tree)) orelse return null;
20092009 if (eatToken(it, .Ellipsis3)) |token| {
2010 const range_end = try expectNode(arena, it, tree, parseExpr, AstError{
2011 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
2010 const range_end = try expectNode(arena, it, tree, parseExpr, .{
2011 .ExpectedExpr = .{ .token = it.index },
20122012 });
20132013
20142014 const node = try arena.create(Node.InfixOp);
2015 node.* = Node.InfixOp{
2015 node.* = .{
20162016 .op_token = token,
20172017 .lhs = expr,
2018 .op = Node.InfixOp.Op{ .Range = {} },
2018 .op = .Range,
20192019 .rhs = range_end,
20202020 };
20212021 return &node.base;
......@@ -2039,24 +2039,22 @@ fn parseSwitchItem(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
20392039/// / MINUSPERCENTEQUAL
20402040/// / EQUAL
20412041fn parseAssignOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
2042 const Op = Node.InfixOp.Op;
2043
20442042 const token = nextToken(it);
2045 const op = switch (token.ptr.id) {
2046 .AsteriskEqual => Op{ .AssignMul = {} },
2047 .SlashEqual => Op{ .AssignDiv = {} },
2048 .PercentEqual => Op{ .AssignMod = {} },
2049 .PlusEqual => Op{ .AssignAdd = {} },
2050 .MinusEqual => Op{ .AssignSub = {} },
2051 .AngleBracketAngleBracketLeftEqual => Op{ .AssignBitShiftLeft = {} },
2052 .AngleBracketAngleBracketRightEqual => Op{ .AssignBitShiftRight = {} },
2053 .AmpersandEqual => Op{ .AssignBitAnd = {} },
2054 .CaretEqual => Op{ .AssignBitXor = {} },
2055 .PipeEqual => Op{ .AssignBitOr = {} },
2056 .AsteriskPercentEqual => Op{ .AssignMulWrap = {} },
2057 .PlusPercentEqual => Op{ .AssignAddWrap = {} },
2058 .MinusPercentEqual => Op{ .AssignSubWrap = {} },
2059 .Equal => Op{ .Assign = {} },
2043 const op: Node.InfixOp.Op = switch (token.ptr.id) {
2044 .AsteriskEqual => .AssignMul,
2045 .SlashEqual => .AssignDiv,
2046 .PercentEqual => .AssignMod,
2047 .PlusEqual => .AssignAdd,
2048 .MinusEqual => .AssignSub,
2049 .AngleBracketAngleBracketLeftEqual => .AssignBitShiftLeft,
2050 .AngleBracketAngleBracketRightEqual => .AssignBitShiftRight,
2051 .AmpersandEqual => .AssignBitAnd,
2052 .CaretEqual => .AssignBitXor,
2053 .PipeEqual => .AssignBitOr,
2054 .AsteriskPercentEqual => .AssignMulWrap,
2055 .PlusPercentEqual => .AssignAddWrap,
2056 .MinusPercentEqual => .AssignSubWrap,
2057 .Equal => .Assign,
20602058 else => {
20612059 putBackToken(it, token.index);
20622060 return null;
......@@ -2064,7 +2062,7 @@ fn parseAssignOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
20642062 };
20652063
20662064 const node = try arena.create(Node.InfixOp);
2067 node.* = Node.InfixOp{
2065 node.* = .{
20682066 .op_token = token.index,
20692067 .lhs = undefined, // set by caller
20702068 .op = op,
......@@ -2081,16 +2079,14 @@ fn parseAssignOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
20812079/// / LARROWEQUAL
20822080/// / RARROWEQUAL
20832081fn parseCompareOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
2084 const ops = Node.InfixOp.Op;
2085
20862082 const token = nextToken(it);
2087 const op = switch (token.ptr.id) {
2088 .EqualEqual => ops{ .EqualEqual = {} },
2089 .BangEqual => ops{ .BangEqual = {} },
2090 .AngleBracketLeft => ops{ .LessThan = {} },
2091 .AngleBracketRight => ops{ .GreaterThan = {} },
2092 .AngleBracketLeftEqual => ops{ .LessOrEqual = {} },
2093 .AngleBracketRightEqual => ops{ .GreaterOrEqual = {} },
2083 const op: Node.InfixOp.Op = switch (token.ptr.id) {
2084 .EqualEqual => .EqualEqual,
2085 .BangEqual => .BangEqual,
2086 .AngleBracketLeft => .LessThan,
2087 .AngleBracketRight => .GreaterThan,
2088 .AngleBracketLeftEqual => .LessOrEqual,
2089 .AngleBracketRightEqual => .GreaterOrEqual,
20942090 else => {
20952091 putBackToken(it, token.index);
20962092 return null;
......@@ -2107,15 +2103,13 @@ fn parseCompareOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
21072103/// / KEYWORD_orelse
21082104/// / KEYWORD_catch Payload?
21092105fn parseBitwiseOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
2110 const ops = Node.InfixOp.Op;
2111
21122106 const token = nextToken(it);
2113 const op = switch (token.ptr.id) {
2114 .Ampersand => ops{ .BitAnd = {} },
2115 .Caret => ops{ .BitXor = {} },
2116 .Pipe => ops{ .BitOr = {} },
2117 .Keyword_orelse => ops{ .UnwrapOptional = {} },
2118 .Keyword_catch => ops{ .Catch = try parsePayload(arena, it, tree) },
2107 const op: Node.InfixOp.Op = switch (token.ptr.id) {
2108 .Ampersand => .BitAnd,
2109 .Caret => .BitXor,
2110 .Pipe => .BitOr,
2111 .Keyword_orelse => .UnwrapOptional,
2112 .Keyword_catch => .{ .Catch = try parsePayload(arena, it, tree) },
21192113 else => {
21202114 putBackToken(it, token.index);
21212115 return null;
......@@ -2129,12 +2123,10 @@ fn parseBitwiseOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
21292123/// <- LARROW2
21302124/// / RARROW2
21312125fn parseBitShiftOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
2132 const ops = Node.InfixOp.Op;
2133
21342126 const token = nextToken(it);
2135 const op = switch (token.ptr.id) {
2136 .AngleBracketAngleBracketLeft => ops{ .BitShiftLeft = {} },
2137 .AngleBracketAngleBracketRight => ops{ .BitShiftRight = {} },
2127 const op: Node.InfixOp.Op = switch (token.ptr.id) {
2128 .AngleBracketAngleBracketLeft => .BitShiftLeft,
2129 .AngleBracketAngleBracketRight => .BitShiftRight,
21382130 else => {
21392131 putBackToken(it, token.index);
21402132 return null;
......@@ -2151,15 +2143,13 @@ fn parseBitShiftOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
21512143/// / PLUSPERCENT
21522144/// / MINUSPERCENT
21532145fn parseAdditionOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
2154 const ops = Node.InfixOp.Op;
2155
21562146 const token = nextToken(it);
2157 const op = switch (token.ptr.id) {
2158 .Plus => ops{ .Add = {} },
2159 .Minus => ops{ .Sub = {} },
2160 .PlusPlus => ops{ .ArrayCat = {} },
2161 .PlusPercent => ops{ .AddWrap = {} },
2162 .MinusPercent => ops{ .SubWrap = {} },
2147 const op: Node.InfixOp.Op = switch (token.ptr.id) {
2148 .Plus => .Add,
2149 .Minus => .Sub,
2150 .PlusPlus => .ArrayCat,
2151 .PlusPercent => .AddWrap,
2152 .MinusPercent => .SubWrap,
21632153 else => {
21642154 putBackToken(it, token.index);
21652155 return null;
......@@ -2177,16 +2167,14 @@ fn parseAdditionOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
21772167/// / ASTERISK2
21782168/// / ASTERISKPERCENT
21792169fn parseMultiplyOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
2180 const ops = Node.InfixOp.Op;
2181
21822170 const token = nextToken(it);
2183 const op = switch (token.ptr.id) {
2184 .PipePipe => ops{ .BoolOr = {} },
2185 .Asterisk => ops{ .Mul = {} },
2186 .Slash => ops{ .Div = {} },
2187 .Percent => ops{ .Mod = {} },
2188 .AsteriskAsterisk => ops{ .ArrayMult = {} },
2189 .AsteriskPercent => ops{ .MulWrap = {} },
2171 const op: Node.InfixOp.Op = switch (token.ptr.id) {
2172 .PipePipe => .MergeErrorSets,
2173 .Asterisk => .Mul,
2174 .Slash => .Div,
2175 .Percent => .Mod,
2176 .AsteriskAsterisk => .ArrayMult,
2177 .AsteriskPercent => .MulWrap,
21902178 else => {
21912179 putBackToken(it, token.index);
21922180 return null;
......@@ -2205,17 +2193,15 @@ fn parseMultiplyOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
22052193/// / KEYWORD_try
22062194/// / KEYWORD_await
22072195fn parsePrefixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
2208 const ops = Node.PrefixOp.Op;
2209
22102196 const token = nextToken(it);
2211 const op = switch (token.ptr.id) {
2212 .Bang => ops{ .BoolNot = {} },
2213 .Minus => ops{ .Negation = {} },
2214 .Tilde => ops{ .BitNot = {} },
2215 .MinusPercent => ops{ .NegationWrap = {} },
2216 .Ampersand => ops{ .AddressOf = {} },
2217 .Keyword_try => ops{ .Try = {} },
2218 .Keyword_await => ops{ .Await = .{} },
2197 const op: Node.PrefixOp.Op = switch (token.ptr.id) {
2198 .Bang => .BoolNot,
2199 .Minus => .Negation,
2200 .Tilde => .BitNot,
2201 .MinusPercent => .NegationWrap,
2202 .Ampersand => .AddressOf,
2203 .Keyword_try => .Try,
2204 .Keyword_await => .Await,
22192205 else => {
22202206 putBackToken(it, token.index);
22212207 return null;
......@@ -2223,7 +2209,7 @@ fn parsePrefixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
22232209 };
22242210
22252211 const node = try arena.create(Node.PrefixOp);
2226 node.* = Node.PrefixOp{
2212 node.* = .{
22272213 .op_token = token.index,
22282214 .op = op,
22292215 .rhs = undefined, // set by caller
......@@ -2246,9 +2232,9 @@ fn parsePrefixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
22462232fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
22472233 if (eatToken(it, .QuestionMark)) |token| {
22482234 const node = try arena.create(Node.PrefixOp);
2249 node.* = Node.PrefixOp{
2235 node.* = .{
22502236 .op_token = token,
2251 .op = Node.PrefixOp.Op.OptionalType,
2237 .op = .OptionalType,
22522238 .rhs = undefined, // set by caller
22532239 };
22542240 return &node.base;
......@@ -2264,7 +2250,7 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
22642250 return null;
22652251 };
22662252 const node = try arena.create(Node.AnyFrameType);
2267 node.* = Node.AnyFrameType{
2253 node.* = .{
22682254 .anyframe_token = token,
22692255 .result = Node.AnyFrameType.Result{
22702256 .arrow_token = arrow,
......@@ -2286,18 +2272,18 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
22862272 while (true) {
22872273 if (eatToken(it, .Keyword_align)) |align_token| {
22882274 const lparen = try expectToken(it, tree, .LParen);
2289 const expr_node = try expectNode(arena, it, tree, parseExpr, AstError{
2290 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
2275 const expr_node = try expectNode(arena, it, tree, parseExpr, .{
2276 .ExpectedExpr = .{ .token = it.index },
22912277 });
22922278
22932279 // Optional bit range
22942280 const bit_range = if (eatToken(it, .Colon)) |_| bit_range_value: {
2295 const range_start = try expectNode(arena, it, tree, parseIntegerLiteral, AstError{
2296 .ExpectedIntegerLiteral = AstError.ExpectedIntegerLiteral{ .token = it.index },
2281 const range_start = try expectNode(arena, it, tree, parseIntegerLiteral, .{
2282 .ExpectedIntegerLiteral = .{ .token = it.index },
22972283 });
22982284 _ = try expectToken(it, tree, .Colon);
2299 const range_end = try expectNode(arena, it, tree, parseIntegerLiteral, AstError{
2300 .ExpectedIntegerLiteral = AstError.ExpectedIntegerLiteral{ .token = it.index },
2285 const range_end = try expectNode(arena, it, tree, parseIntegerLiteral, .{
2286 .ExpectedIntegerLiteral = .{ .token = it.index },
23012287 });
23022288
23032289 break :bit_range_value Node.PrefixOp.PtrInfo.Align.BitRange{
......@@ -2340,8 +2326,8 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
23402326 while (true) {
23412327 if (try parseByteAlign(arena, it, tree)) |align_expr| {
23422328 if (slice_type.align_info != null) {
2343 try tree.errors.push(AstError{
2344 .ExtraAlignQualifier = AstError.ExtraAlignQualifier{ .token = it.index },
2329 try tree.errors.push(.{
2330 .ExtraAlignQualifier = .{ .token = it.index },
23452331 });
23462332 return error.ParseError;
23472333 }
......@@ -2353,8 +2339,8 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
23532339 }
23542340 if (eatToken(it, .Keyword_const)) |const_token| {
23552341 if (slice_type.const_token != null) {
2356 try tree.errors.push(AstError{
2357 .ExtraConstQualifier = AstError.ExtraConstQualifier{ .token = it.index },
2342 try tree.errors.push(.{
2343 .ExtraConstQualifier = .{ .token = it.index },
23582344 });
23592345 return error.ParseError;
23602346 }
......@@ -2363,8 +2349,8 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
23632349 }
23642350 if (eatToken(it, .Keyword_volatile)) |volatile_token| {
23652351 if (slice_type.volatile_token != null) {
2366 try tree.errors.push(AstError{
2367 .ExtraVolatileQualifier = AstError.ExtraVolatileQualifier{ .token = it.index },
2352 try tree.errors.push(.{
2353 .ExtraVolatileQualifier = .{ .token = it.index },
23682354 });
23692355 return error.ParseError;
23702356 }
......@@ -2373,8 +2359,8 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
23732359 }
23742360 if (eatToken(it, .Keyword_allowzero)) |allowzero_token| {
23752361 if (slice_type.allowzero_token != null) {
2376 try tree.errors.push(AstError{
2377 .ExtraAllowZeroQualifier = AstError.ExtraAllowZeroQualifier{ .token = it.index },
2362 try tree.errors.push(.{
2363 .ExtraAllowZeroQualifier = .{ .token = it.index },
23782364 });
23792365 return error.ParseError;
23802366 }
......@@ -2398,15 +2384,14 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
23982384/// / DOTASTERISK
23992385/// / DOTQUESTIONMARK
24002386fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
2401 const Op = Node.SuffixOp.Op;
24022387 const OpAndToken = struct {
24032388 op: Node.SuffixOp.Op,
24042389 token: TokenIndex,
24052390 };
2406 const op_and_token = blk: {
2391 const op_and_token: OpAndToken = blk: {
24072392 if (eatToken(it, .LBracket)) |_| {
2408 const index_expr = try expectNode(arena, it, tree, parseExpr, AstError{
2409 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
2393 const index_expr = try expectNode(arena, it, tree, parseExpr, .{
2394 .ExpectedExpr = .{ .token = it.index },
24102395 });
24112396
24122397 if (eatToken(it, .Ellipsis2) != null) {
......@@ -2415,9 +2400,9 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
24152400 try parseExpr(arena, it, tree)
24162401 else
24172402 null;
2418 break :blk OpAndToken{
2419 .op = Op{
2420 .Slice = Op.Slice{
2403 break :blk .{
2404 .op = .{
2405 .Slice = .{
24212406 .start = index_expr,
24222407 .end = end_expr,
24232408 .sentinel = sentinel,
......@@ -2427,14 +2412,14 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
24272412 };
24282413 }
24292414
2430 break :blk OpAndToken{
2431 .op = Op{ .ArrayAccess = index_expr },
2415 break :blk .{
2416 .op = .{ .ArrayAccess = index_expr },
24322417 .token = try expectToken(it, tree, .RBracket),
24332418 };
24342419 }
24352420
24362421 if (eatToken(it, .PeriodAsterisk)) |period_asterisk| {
2437 break :blk OpAndToken{ .op = Op{ .Deref = {} }, .token = period_asterisk };
2422 break :blk .{ .op = .Deref, .token = period_asterisk };
24382423 }
24392424
24402425 if (eatToken(it, .Period)) |period| {
......@@ -2443,19 +2428,19 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
24432428 // Should there be an ast.Node.SuffixOp.FieldAccess variant? Or should
24442429 // this grammar rule be altered?
24452430 const node = try arena.create(Node.InfixOp);
2446 node.* = Node.InfixOp{
2431 node.* = .{
24472432 .op_token = period,
24482433 .lhs = undefined, // set by caller
2449 .op = Node.InfixOp.Op.Period,
2434 .op = .Period,
24502435 .rhs = identifier,
24512436 };
24522437 return &node.base;
24532438 }
24542439 if (eatToken(it, .QuestionMark)) |question_mark| {
2455 break :blk OpAndToken{ .op = Op{ .UnwrapOptional = {} }, .token = question_mark };
2440 break :blk .{ .op = .UnwrapOptional, .token = question_mark };
24562441 }
2457 try tree.errors.push(AstError{
2458 .ExpectedSuffixOp = AstError.ExpectedSuffixOp{ .token = it.index },
2442 try tree.errors.push(.{
2443 .ExpectedSuffixOp = .{ .token = it.index },
24592444 });
24602445 return null;
24612446 }
......@@ -2464,7 +2449,7 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
24642449 };
24652450
24662451 const node = try arena.create(Node.SuffixOp);
2467 node.* = Node.SuffixOp{
2452 node.* = .{
24682453 .lhs = undefined, // set by caller
24692454 .op = op_and_token.op,
24702455 .rtoken = op_and_token.token,
......@@ -2491,22 +2476,22 @@ fn parseArrayTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
24912476 const lbracket = eatToken(it, .LBracket) orelse return null;
24922477 const expr = try parseExpr(arena, it, tree);
24932478 const sentinel = if (eatToken(it, .Colon)) |_|
2494 try expectNode(arena, it, tree, parseExpr, AstError{
2479 try expectNode(arena, it, tree, parseExpr, .{
24952480 .ExpectedExpr = .{ .token = it.index },
24962481 })
24972482 else
24982483 null;
24992484 const rbracket = try expectToken(it, tree, .RBracket);
25002485
2501 const op = if (expr) |len_expr|
2502 Node.PrefixOp.Op{
2486 const op: Node.PrefixOp.Op = if (expr) |len_expr|
2487 .{
25032488 .ArrayType = .{
25042489 .len_expr = len_expr,
25052490 .sentinel = sentinel,
25062491 },
25072492 }
25082493 else
2509 Node.PrefixOp.Op{
2494 .{
25102495 .SliceType = Node.PrefixOp.PtrInfo{
25112496 .allowzero_token = null,
25122497 .align_info = null,
......@@ -2517,7 +2502,7 @@ fn parseArrayTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
25172502 };
25182503
25192504 const node = try arena.create(Node.PrefixOp);
2520 node.* = Node.PrefixOp{
2505 node.* = .{
25212506 .op_token = lbracket,
25222507 .op = op,
25232508 .rhs = undefined, // set by caller
......@@ -2533,7 +2518,7 @@ fn parseArrayTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
25332518fn parsePtrTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
25342519 if (eatToken(it, .Asterisk)) |asterisk| {
25352520 const sentinel = if (eatToken(it, .Colon)) |_|
2536 try expectNode(arena, it, tree, parseExpr, AstError{
2521 try expectNode(arena, it, tree, parseExpr, .{
25372522 .ExpectedExpr = .{ .token = it.index },
25382523 })
25392524 else
......@@ -2549,17 +2534,17 @@ fn parsePtrTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
25492534
25502535 if (eatToken(it, .AsteriskAsterisk)) |double_asterisk| {
25512536 const node = try arena.create(Node.PrefixOp);
2552 node.* = Node.PrefixOp{
2537 node.* = .{
25532538 .op_token = double_asterisk,
2554 .op = Node.PrefixOp.Op{ .PtrType = .{} },
2539 .op = .{ .PtrType = .{} },
25552540 .rhs = undefined, // set by caller
25562541 };
25572542
25582543 // Special case for **, which is its own token
25592544 const child = try arena.create(Node.PrefixOp);
2560 child.* = Node.PrefixOp{
2545 child.* = .{
25612546 .op_token = double_asterisk,
2562 .op = Node.PrefixOp.Op{ .PtrType = .{} },
2547 .op = .{ .PtrType = .{} },
25632548 .rhs = undefined, // set by caller
25642549 };
25652550 node.rhs = &child.base;
......@@ -2586,7 +2571,7 @@ fn parsePtrTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
25862571 }
25872572 }
25882573 const sentinel = if (eatToken(it, .Colon)) |_|
2589 try expectNode(arena, it, tree, parseExpr, AstError{
2574 try expectNode(arena, it, tree, parseExpr, .{
25902575 .ExpectedExpr = .{ .token = it.index },
25912576 })
25922577 else
......@@ -2629,8 +2614,8 @@ fn parseContainerDeclType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?
26292614 .Keyword_struct => Node.ContainerDecl.InitArg{ .None = {} },
26302615 .Keyword_enum => blk: {
26312616 if (eatToken(it, .LParen) != null) {
2632 const expr = try expectNode(arena, it, tree, parseExpr, AstError{
2633 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
2617 const expr = try expectNode(arena, it, tree, parseExpr, .{
2618 .ExpectedExpr = .{ .token = it.index },
26342619 });
26352620 _ = try expectToken(it, tree, .RParen);
26362621 break :blk Node.ContainerDecl.InitArg{ .Type = expr };
......@@ -2641,8 +2626,8 @@ fn parseContainerDeclType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?
26412626 if (eatToken(it, .LParen) != null) {
26422627 if (eatToken(it, .Keyword_enum) != null) {
26432628 if (eatToken(it, .LParen) != null) {
2644 const expr = try expectNode(arena, it, tree, parseExpr, AstError{
2645 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
2629 const expr = try expectNode(arena, it, tree, parseExpr, .{
2630 .ExpectedExpr = .{ .token = it.index },
26462631 });
26472632 _ = try expectToken(it, tree, .RParen);
26482633 _ = try expectToken(it, tree, .RParen);
......@@ -2651,8 +2636,8 @@ fn parseContainerDeclType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?
26512636 _ = try expectToken(it, tree, .RParen);
26522637 break :blk Node.ContainerDecl.InitArg{ .Enum = null };
26532638 }
2654 const expr = try expectNode(arena, it, tree, parseExpr, AstError{
2655 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
2639 const expr = try expectNode(arena, it, tree, parseExpr, .{
2640 .ExpectedExpr = .{ .token = it.index },
26562641 });
26572642 _ = try expectToken(it, tree, .RParen);
26582643 break :blk Node.ContainerDecl.InitArg{ .Type = expr };
......@@ -2666,7 +2651,7 @@ fn parseContainerDeclType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?
26662651 };
26672652
26682653 const node = try arena.create(Node.ContainerDecl);
2669 node.* = Node.ContainerDecl{
2654 node.* = .{
26702655 .layout_token = null,
26712656 .kind_token = kind_token.index,
26722657 .init_arg_expr = init_arg_expr,
......@@ -2681,8 +2666,8 @@ fn parseContainerDeclType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?
26812666fn parseByteAlign(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
26822667 _ = eatToken(it, .Keyword_align) orelse return null;
26832668 _ = try expectToken(it, tree, .LParen);
2684 const expr = try expectNode(arena, it, tree, parseExpr, AstError{
2685 .ExpectedExpr = AstError.ExpectedExpr{ .token = it.index },
2669 const expr = try expectNode(arena, it, tree, parseExpr, .{
2670 .ExpectedExpr = .{ .token = it.index },
26862671 });
26872672 _ = try expectToken(it, tree, .RParen);
26882673 return expr;
......@@ -2738,7 +2723,7 @@ fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.InfixOp.Op) No
27382723 pub fn parse(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*Node {
27392724 const op_token = eatToken(it, token) orelse return null;
27402725 const node = try arena.create(Node.InfixOp);
2741 node.* = Node.InfixOp{
2726 node.* = .{
27422727 .op_token = op_token,
27432728 .lhs = undefined, // set by caller
27442729 .op = op,
......@@ -2754,13 +2739,13 @@ fn SimpleBinOpParseFn(comptime token: Token.Id, comptime op: Node.InfixOp.Op) No
27542739fn parseBuiltinCall(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
27552740 const token = eatToken(it, .Builtin) orelse return null;
27562741 const params = (try parseFnCallArguments(arena, it, tree)) orelse {
2757 try tree.errors.push(AstError{
2758 .ExpectedParamList = AstError.ExpectedParamList{ .token = it.index },
2742 try tree.errors.push(.{
2743 .ExpectedParamList = .{ .token = it.index },
27592744 });
27602745 return error.ParseError;
27612746 };
27622747 const node = try arena.create(Node.BuiltinCall);
2763 node.* = Node.BuiltinCall{
2748 node.* = .{
27642749 .builtin_token = token,
27652750 .params = params.list,
27662751 .rparen_token = params.rparen,
......@@ -2773,7 +2758,7 @@ fn parseErrorTag(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
27732758 const token = eatToken(it, .Identifier) orelse return null;
27742759
27752760 const node = try arena.create(Node.ErrorTag);
2776 node.* = Node.ErrorTag{
2761 node.* = .{
27772762 .doc_comments = doc_comments,
27782763 .name_token = token,
27792764 };
......@@ -2783,7 +2768,7 @@ fn parseErrorTag(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
27832768fn parseIdentifier(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
27842769 const token = eatToken(it, .Identifier) orelse return null;
27852770 const node = try arena.create(Node.Identifier);
2786 node.* = Node.Identifier{
2771 node.* = .{
27872772 .token = token,
27882773 };
27892774 return &node.base;
......@@ -2792,7 +2777,7 @@ fn parseIdentifier(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
27922777fn parseVarType(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
27932778 const token = eatToken(it, .Keyword_var) orelse return null;
27942779 const node = try arena.create(Node.VarType);
2795 node.* = Node.VarType{
2780 node.* = .{
27962781 .token = token,
27972782 };
27982783 return &node.base;
......@@ -2810,7 +2795,7 @@ fn createLiteral(arena: *Allocator, comptime T: type, token: TokenIndex) !*Node
28102795fn parseStringLiteralSingle(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
28112796 if (eatToken(it, .StringLiteral)) |token| {
28122797 const node = try arena.create(Node.StringLiteral);
2813 node.* = Node.StringLiteral{
2798 node.* = .{
28142799 .token = token,
28152800 };
28162801 return &node.base;
......@@ -2824,7 +2809,7 @@ fn parseStringLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Nod
28242809
28252810 if (eatToken(it, .MultilineStringLiteralLine)) |first_line| {
28262811 const node = try arena.create(Node.MultilineStringLiteral);
2827 node.* = Node.MultilineStringLiteral{
2812 node.* = .{
28282813 .lines = Node.MultilineStringLiteral.LineList.init(arena),
28292814 };
28302815 try node.lines.push(first_line);
......@@ -2840,7 +2825,7 @@ fn parseStringLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Nod
28402825fn parseIntegerLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
28412826 const token = eatToken(it, .IntegerLiteral) orelse return null;
28422827 const node = try arena.create(Node.IntegerLiteral);
2843 node.* = Node.IntegerLiteral{
2828 node.* = .{
28442829 .token = token,
28452830 };
28462831 return &node.base;
......@@ -2849,7 +2834,7 @@ fn parseIntegerLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
28492834fn parseFloatLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
28502835 const token = eatToken(it, .FloatLiteral) orelse return null;
28512836 const node = try arena.create(Node.FloatLiteral);
2852 node.* = Node.FloatLiteral{
2837 node.* = .{
28532838 .token = token,
28542839 };
28552840 return &node.base;
......@@ -2858,9 +2843,9 @@ fn parseFloatLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
28582843fn parseTry(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
28592844 const token = eatToken(it, .Keyword_try) orelse return null;
28602845 const node = try arena.create(Node.PrefixOp);
2861 node.* = Node.PrefixOp{
2846 node.* = .{
28622847 .op_token = token,
2863 .op = Node.PrefixOp.Op.Try,
2848 .op = .Try,
28642849 .rhs = undefined, // set by caller
28652850 };
28662851 return &node.base;
......@@ -2869,7 +2854,7 @@ fn parseTry(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
28692854fn parseUse(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
28702855 const token = eatToken(it, .Keyword_usingnamespace) orelse return null;
28712856 const node = try arena.create(Node.Use);
2872 node.* = Node.Use{
2857 node.* = .{
28732858 .doc_comments = null,
28742859 .visib_token = null,
28752860 .use_token = token,
......@@ -2884,17 +2869,17 @@ fn parseIf(arena: *Allocator, it: *TokenIterator, tree: *Tree, bodyParseFn: Node
28842869 const node = (try parseIfPrefix(arena, it, tree)) orelse return null;
28852870 const if_prefix = node.cast(Node.If).?;
28862871
2887 if_prefix.body = try expectNode(arena, it, tree, bodyParseFn, AstError{
2888 .InvalidToken = AstError.InvalidToken{ .token = it.index },
2872 if_prefix.body = try expectNode(arena, it, tree, bodyParseFn, .{
2873 .InvalidToken = .{ .token = it.index },
28892874 });
28902875
28912876 const else_token = eatToken(it, .Keyword_else) orelse return node;
28922877 const payload = try parsePayload(arena, it, tree);
2893 const else_expr = try expectNode(arena, it, tree, bodyParseFn, AstError{
2894 .InvalidToken = AstError.InvalidToken{ .token = it.index },
2878 const else_expr = try expectNode(arena, it, tree, bodyParseFn, .{
2879 .InvalidToken = .{ .token = it.index },
28952880 });
28962881 const else_node = try arena.create(Node.Else);
2897 else_node.* = Node.Else{
2882 else_node.* = .{
28982883 .else_token = else_token,
28992884 .payload = payload,
29002885 .body = else_expr,
......@@ -2914,7 +2899,7 @@ fn parseDocComment(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.D
29142899 if (lines.len == 0) return null;
29152900
29162901 const node = try arena.create(Node.DocComment);
2917 node.* = Node.DocComment{
2902 node.* = .{
29182903 .lines = lines,
29192904 };
29202905 return node;
......@@ -2925,7 +2910,7 @@ fn parseAppendedDocComment(arena: *Allocator, it: *TokenIterator, tree: *Tree, a
29252910 const comment_token = eatToken(it, .DocComment) orelse return null;
29262911 if (tree.tokensOnSameLine(after_token, comment_token)) {
29272912 const node = try arena.create(Node.DocComment);
2928 node.* = Node.DocComment{
2913 node.* = .{
29292914 .lines = Node.DocComment.LineList.init(arena),
29302915 };
29312916 try node.lines.push(comment_token);
......@@ -2974,14 +2959,14 @@ fn parsePrefixOpExpr(
29742959 switch (rightmost_op.id) {
29752960 .PrefixOp => {
29762961 const prefix_op = rightmost_op.cast(Node.PrefixOp).?;
2977 prefix_op.rhs = try expectNode(arena, it, tree, childParseFn, AstError{
2978 .InvalidToken = AstError.InvalidToken{ .token = it.index },
2962 prefix_op.rhs = try expectNode(arena, it, tree, childParseFn, .{
2963 .InvalidToken = .{ .token = it.index },
29792964 });
29802965 },
29812966 .AnyFrameType => {
29822967 const prom = rightmost_op.cast(Node.AnyFrameType).?;
2983 prom.result.?.return_type = try expectNode(arena, it, tree, childParseFn, AstError{
2984 .InvalidToken = AstError.InvalidToken{ .token = it.index },
2968 prom.result.?.return_type = try expectNode(arena, it, tree, childParseFn, .{
2969 .InvalidToken = .{ .token = it.index },
29852970 });
29862971 },
29872972 else => unreachable,
......@@ -3010,8 +2995,8 @@ fn parseBinOpExpr(
30102995 var res = (try childParseFn(arena, it, tree)) orelse return null;
30112996
30122997 while (try opParseFn(arena, it, tree)) |node| {
3013 const right = try expectNode(arena, it, tree, childParseFn, AstError{
3014 .InvalidToken = AstError.InvalidToken{ .token = it.index },
2998 const right = try expectNode(arena, it, tree, childParseFn, .{
2999 .InvalidToken = .{ .token = it.index },
30153000 });
30163001 const left = res;
30173002 res = node;
......@@ -3031,7 +3016,7 @@ fn parseBinOpExpr(
30313016
30323017fn createInfixOp(arena: *Allocator, index: TokenIndex, op: Node.InfixOp.Op) !*Node {
30333018 const node = try arena.create(Node.InfixOp);
3034 node.* = Node.InfixOp{
3019 node.* = .{
30353020 .op_token = index,
30363021 .lhs = undefined, // set by caller
30373022 .op = op,
......@@ -3051,8 +3036,8 @@ fn eatAnnotatedToken(it: *TokenIterator, id: Token.Id) ?AnnotatedToken {
30513036fn expectToken(it: *TokenIterator, tree: *Tree, id: Token.Id) Error!TokenIndex {
30523037 const token = nextToken(it);
30533038 if (token.ptr.id != id) {
3054 try tree.errors.push(AstError{
3055 .ExpectedToken = AstError.ExpectedToken{ .token = token.index, .expected_id = id },
3039 try tree.errors.push(.{
3040 .ExpectedToken = .{ .token = token.index, .expected_id = id },
30563041 });
30573042 return error.ParseError;
30583043 }
lib/std/zig/parser_test.zig+2
......@@ -1509,6 +1509,8 @@ test "zig fmt: error set declaration" {
15091509 \\const Error = error{OutOfMemory};
15101510 \\const Error = error{};
15111511 \\
1512 \\const Error = error{ OutOfMemory, OutOfTime };
1513 \\
15121514 );
15131515}
15141516
lib/std/zig/render.zig+41-18
......@@ -583,7 +583,6 @@ fn renderExpression(
583583 },
584584
585585 .Try,
586 .Cancel,
587586 .Resume,
588587 => {
589588 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space);
......@@ -1269,25 +1268,51 @@ fn renderExpression(
12691268 }
12701269
12711270 try renderToken(tree, stream, err_set_decl.error_token, indent, start_col, Space.None); // error
1272 try renderToken(tree, stream, lbrace, indent, start_col, Space.Newline); // {
1273 const new_indent = indent + indent_delta;
12741271
1275 var it = err_set_decl.decls.iterator(0);
1276 while (it.next()) |node| {
1277 try stream.writeByteNTimes(' ', new_indent);
1272 const src_has_trailing_comma = blk: {
1273 const maybe_comma = tree.prevToken(err_set_decl.rbrace_token);
1274 break :blk tree.tokens.at(maybe_comma).id == .Comma;
1275 };
12781276
1279 if (it.peek()) |next_node| {
1280 try renderExpression(allocator, stream, tree, new_indent, start_col, node.*, Space.None);
1281 try renderToken(tree, stream, tree.nextToken(node.*.lastToken()), new_indent, start_col, Space.Newline); // ,
1277 if (src_has_trailing_comma) {
1278 try renderToken(tree, stream, lbrace, indent, start_col, Space.Newline); // {
1279 const new_indent = indent + indent_delta;
12821280
1283 try renderExtraNewline(tree, stream, start_col, next_node.*);
1284 } else {
1285 try renderExpression(allocator, stream, tree, new_indent, start_col, node.*, Space.Comma);
1281 var it = err_set_decl.decls.iterator(0);
1282 while (it.next()) |node| {
1283 try stream.writeByteNTimes(' ', new_indent);
1284
1285 if (it.peek()) |next_node| {
1286 try renderExpression(allocator, stream, tree, new_indent, start_col, node.*, Space.None);
1287 try renderToken(tree, stream, tree.nextToken(node.*.lastToken()), new_indent, start_col, Space.Newline); // ,
1288
1289 try renderExtraNewline(tree, stream, start_col, next_node.*);
1290 } else {
1291 try renderExpression(allocator, stream, tree, new_indent, start_col, node.*, Space.Comma);
1292 }
12861293 }
1287 }
12881294
1289 try stream.writeByteNTimes(' ', indent);
1290 return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // }
1295 try stream.writeByteNTimes(' ', indent);
1296 return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // }
1297 } else {
1298 try renderToken(tree, stream, lbrace, indent, start_col, Space.Space); // {
1299
1300 var it = err_set_decl.decls.iterator(0);
1301 while (it.next()) |node| {
1302 if (it.peek()) |next_node| {
1303 try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.None);
1304
1305 const comma_token = tree.nextToken(node.*.lastToken());
1306 assert(tree.tokens.at(comma_token).id == .Comma);
1307 try renderToken(tree, stream, comma_token, indent, start_col, Space.Space); // ,
1308 try renderExtraNewline(tree, stream, start_col, next_node.*);
1309 } else {
1310 try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.Space);
1311 }
1312 }
1313
1314 return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // }
1315 }
12911316 },
12921317
12931318 .ErrorTag => {
......@@ -1590,8 +1615,7 @@ fn renderExpression(
15901615 }
15911616 } else {
15921617 var it = switch_case.items.iterator(0);
1593 while (true) {
1594 const node = it.next().?;
1618 while (it.next()) |node| {
15951619 if (it.peek()) |next_node| {
15961620 try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.None);
15971621
......@@ -1602,7 +1626,6 @@ fn renderExpression(
16021626 } else {
16031627 try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.Comma);
16041628 try stream.writeByteNTimes(' ', indent);
1605 break;
16061629 }
16071630 }
16081631 }
lib/std/zig/system.zig+1-1
......@@ -754,7 +754,7 @@ pub const NativeTargetInfo = struct {
754754 const rpath_list = mem.toSliceConst(u8, @ptrCast([*:0]u8, strtab[rpoff..].ptr));
755755 var it = mem.tokenize(rpath_list, ":");
756756 while (it.next()) |rpath| {
757 var dir = fs.cwd().openDirList(rpath) catch |err| switch (err) {
757 var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {
758758 error.NameTooLong => unreachable,
759759 error.InvalidUtf8 => unreachable,
760760 error.BadPathName => unreachable,
src-self-hosted/c_int.zig+4-4
......@@ -69,9 +69,9 @@ pub const CInt = struct {
6969 };
7070
7171 pub fn sizeInBits(cint: CInt, self: Target) u32 {
72 const arch = self.getArch();
72 const arch = self.cpu.arch;
7373 switch (self.os.tag) {
74 .freestanding, .other => switch (self.getArch()) {
74 .freestanding, .other => switch (self.cpu.arch) {
7575 .msp430 => switch (cint.id) {
7676 .Short,
7777 .UShort,
......@@ -94,7 +94,7 @@ pub const CInt = struct {
9494 => return 32,
9595 .Long,
9696 .ULong,
97 => return self.getArchPtrBitWidth(),
97 => return self.cpu.arch.ptrBitWidth(),
9898 .LongLong,
9999 .ULongLong,
100100 => return 64,
......@@ -114,7 +114,7 @@ pub const CInt = struct {
114114 => return 32,
115115 .Long,
116116 .ULong,
117 => return self.getArchPtrBitWidth(),
117 => return self.cpu.arch.ptrBitWidth(),
118118 .LongLong,
119119 .ULongLong,
120120 => return 64,
src-self-hosted/compilation.zig+17-13
......@@ -95,7 +95,7 @@ pub const ZigCompiler = struct {
9595
9696 pub fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
9797 if (self.native_libc.start()) |ptr| return ptr;
98 try self.native_libc.data.findNative(self.allocator);
98 self.native_libc.data = try LibCInstallation.findNative(.{ .allocator = self.allocator });
9999 self.native_libc.resolve();
100100 return &self.native_libc.data;
101101 }
......@@ -126,7 +126,7 @@ pub const Compilation = struct {
126126 name: Buffer,
127127 llvm_triple: Buffer,
128128 root_src_path: ?[]const u8,
129 target: Target,
129 target: std.Target,
130130 llvm_target: *llvm.Target,
131131 build_mode: builtin.Mode,
132132 zig_lib_dir: []const u8,
......@@ -338,7 +338,7 @@ pub const Compilation = struct {
338338 zig_compiler: *ZigCompiler,
339339 name: []const u8,
340340 root_src_path: ?[]const u8,
341 target: Target,
341 target: std.zig.CrossTarget,
342342 kind: Kind,
343343 build_mode: builtin.Mode,
344344 is_static: bool,
......@@ -370,13 +370,18 @@ pub const Compilation = struct {
370370 zig_compiler: *ZigCompiler,
371371 name: []const u8,
372372 root_src_path: ?[]const u8,
373 target: Target,
373 cross_target: std.zig.CrossTarget,
374374 kind: Kind,
375375 build_mode: builtin.Mode,
376376 is_static: bool,
377377 zig_lib_dir: []const u8,
378378 ) !void {
379379 const allocator = zig_compiler.allocator;
380
381 // TODO merge this line with stage2.zig crossTargetToTarget
382 const target_info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target);
383 const target = target_info.target;
384
380385 var comp = Compilation{
381386 .arena_allocator = std.heap.ArenaAllocator.init(allocator),
382387 .zig_compiler = zig_compiler,
......@@ -419,7 +424,7 @@ pub const Compilation = struct {
419424 .target_machine = undefined,
420425 .target_data_ref = undefined,
421426 .target_layout_str = undefined,
422 .target_ptr_bits = target.getArchPtrBitWidth(),
427 .target_ptr_bits = target.cpu.arch.ptrBitWidth(),
423428
424429 .root_package = undefined,
425430 .std_package = undefined,
......@@ -440,7 +445,7 @@ pub const Compilation = struct {
440445 }
441446
442447 comp.name = try Buffer.init(comp.arena(), name);
443 comp.llvm_triple = try util.getTriple(comp.arena(), target);
448 comp.llvm_triple = try util.getLLVMTriple(comp.arena(), target);
444449 comp.llvm_target = try util.llvmTargetFromTriple(comp.llvm_triple);
445450 comp.zig_std_dir = try fs.path.join(comp.arena(), &[_][]const u8{ zig_lib_dir, "std" });
446451
......@@ -455,10 +460,8 @@ pub const Compilation = struct {
455460 var target_specific_cpu_features: ?[*:0]u8 = null;
456461 defer llvm.DisposeMessage(target_specific_cpu_args);
457462 defer llvm.DisposeMessage(target_specific_cpu_features);
458 if (target == Target.Native) {
459 target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory;
460 target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory;
461 }
463
464 // TODO detect native CPU & features here
462465
463466 comp.target_machine = llvm.CreateTargetMachine(
464467 comp.llvm_target,
......@@ -517,8 +520,7 @@ pub const Compilation = struct {
517520
518521 if (comp.tmp_dir.getOrNull()) |tmp_dir_result|
519522 if (tmp_dir_result.*) |tmp_dir| {
520 // TODO evented I/O?
521 fs.deleteTree(tmp_dir) catch {};
523 fs.cwd().deleteTree(tmp_dir) catch {};
522524 } else |_| {};
523525 }
524526
......@@ -1122,7 +1124,9 @@ pub const Compilation = struct {
11221124 self.libc_link_lib = link_lib;
11231125
11241126 // get a head start on looking for the native libc
1125 if (self.target == Target.Native and self.override_libc == null) {
1127 // TODO this is missing a bunch of logic related to whether the target is native
1128 // and whether we can build libc
1129 if (self.override_libc == null) {
11261130 try self.deinit_group.call(startFindingNativeLibC, .{self});
11271131 }
11281132 }
src-self-hosted/errmsg.zig+4-7
......@@ -164,8 +164,7 @@ pub const Msg = struct {
164164 const realpath_copy = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
165165 errdefer comp.gpa().free(realpath_copy);
166166
167 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
168 try parse_error.render(&tree_scope.tree.tokens, out_stream);
167 try parse_error.render(&tree_scope.tree.tokens, text_buf.outStream());
169168
170169 const msg = try comp.gpa().create(Msg);
171170 msg.* = Msg{
......@@ -204,8 +203,7 @@ pub const Msg = struct {
204203 const realpath_copy = try mem.dupe(allocator, u8, realpath);
205204 errdefer allocator.free(realpath_copy);
206205
207 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
208 try parse_error.render(&tree.tokens, out_stream);
206 try parse_error.render(&tree.tokens, text_buf.outStream());
209207
210208 const msg = try allocator.create(Msg);
211209 msg.* = Msg{
......@@ -272,7 +270,7 @@ pub const Msg = struct {
272270 });
273271 try stream.writeByteNTimes(' ', start_loc.column);
274272 try stream.writeByteNTimes('~', last_token.end - first_token.start);
275 try stream.write("\n");
273 try stream.writeAll("\n");
276274 }
277275
278276 pub fn printToFile(msg: *const Msg, file: fs.File, color: Color) !void {
......@@ -281,7 +279,6 @@ pub const Msg = struct {
281279 .On => true,
282280 .Off => false,
283281 };
284 var stream = &file.outStream().stream;
285 return msg.printToStream(stream, color_on);
282 return msg.printToStream(file.outStream(), color_on);
286283 }
287284};
src-self-hosted/ir.zig+1-1
......@@ -1099,7 +1099,6 @@ pub const Builder = struct {
10991099 .Await => return error.Unimplemented,
11001100 .BitNot => return error.Unimplemented,
11011101 .BoolNot => return error.Unimplemented,
1102 .Cancel => return error.Unimplemented,
11031102 .OptionalType => return error.Unimplemented,
11041103 .Negation => return error.Unimplemented,
11051104 .NegationWrap => return error.Unimplemented,
......@@ -1188,6 +1187,7 @@ pub const Builder = struct {
11881187 .ParamDecl => return error.Unimplemented,
11891188 .FieldInitializer => return error.Unimplemented,
11901189 .EnumLiteral => return error.Unimplemented,
1190 .Noasync => return error.Unimplemented,
11911191 }
11921192 }
11931193
src-self-hosted/libc_installation.zig+5-5
......@@ -280,7 +280,7 @@ pub const LibCInstallation = struct {
280280 // search in reverse order
281281 const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);
282282 const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
283 var search_dir = fs.cwd().openDirList(search_path) catch |err| switch (err) {
283 var search_dir = fs.cwd().openDir(search_path, .{}) catch |err| switch (err) {
284284 error.FileNotFound,
285285 error.NotDir,
286286 error.NoDevice,
......@@ -335,7 +335,7 @@ pub const LibCInstallation = struct {
335335 const stream = result_buf.outStream();
336336 try stream.print("{}\\Include\\{}\\ucrt", .{ search.path, search.version });
337337
338 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
338 var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) {
339339 error.FileNotFound,
340340 error.NotDir,
341341 error.NoDevice,
......@@ -382,7 +382,7 @@ pub const LibCInstallation = struct {
382382 const stream = result_buf.outStream();
383383 try stream.print("{}\\Lib\\{}\\ucrt\\{}", .{ search.path, search.version, arch_sub_dir });
384384
385 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
385 var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) {
386386 error.FileNotFound,
387387 error.NotDir,
388388 error.NoDevice,
......@@ -437,7 +437,7 @@ pub const LibCInstallation = struct {
437437 const stream = result_buf.outStream();
438438 try stream.print("{}\\Lib\\{}\\um\\{}", .{ search.path, search.version, arch_sub_dir });
439439
440 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
440 var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) {
441441 error.FileNotFound,
442442 error.NotDir,
443443 error.NoDevice,
......@@ -475,7 +475,7 @@ pub const LibCInstallation = struct {
475475
476476 try result_buf.append("\\include");
477477
478 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
478 var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) {
479479 error.FileNotFound,
480480 error.NotDir,
481481 error.NoDevice,
src-self-hosted/link.zig+54-58
......@@ -56,12 +56,13 @@ pub fn link(comp: *Compilation) !void {
5656 if (comp.haveLibC()) {
5757 // TODO https://github.com/ziglang/zig/issues/3190
5858 var libc = ctx.comp.override_libc orelse blk: {
59 switch (comp.target) {
60 Target.Native => {
61 break :blk comp.zig_compiler.getNativeLibC() catch return error.LibCRequiredButNotProvidedOrFound;
62 },
63 else => return error.LibCRequiredButNotProvidedOrFound,
64 }
59 @panic("this code has bitrotted");
60 //switch (comp.target) {
61 // Target.Native => {
62 // break :blk comp.zig_compiler.getNativeLibC() catch return error.LibCRequiredButNotProvidedOrFound;
63 // },
64 // else => return error.LibCRequiredButNotProvidedOrFound,
65 //}
6566 };
6667 ctx.libc = libc;
6768 }
......@@ -155,11 +156,11 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
155156 //bool shared = !g->is_static && is_lib;
156157 //Buf *soname = nullptr;
157158 if (ctx.comp.is_static) {
158 if (util.isArmOrThumb(ctx.comp.target)) {
159 try ctx.args.append("-Bstatic");
160 } else {
161 try ctx.args.append("-static");
162 }
159 //if (util.isArmOrThumb(ctx.comp.target)) {
160 // try ctx.args.append("-Bstatic");
161 //} else {
162 // try ctx.args.append("-static");
163 //}
163164 }
164165 //} else if (shared) {
165166 // lj->args.append("-shared");
......@@ -176,29 +177,24 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
176177
177178 if (ctx.link_in_crt) {
178179 const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o";
179 const crtbegino = if (ctx.comp.is_static) "crtbeginT.o" else "crtbegin.o";
180 try addPathJoin(ctx, ctx.libc.lib_dir.?, crt1o);
181 try addPathJoin(ctx, ctx.libc.lib_dir.?, "crti.o");
182 try addPathJoin(ctx, ctx.libc.static_lib_dir.?, crtbegino);
180 try addPathJoin(ctx, ctx.libc.crt_dir.?, crt1o);
181 try addPathJoin(ctx, ctx.libc.crt_dir.?, "crti.o");
183182 }
184183
185184 if (ctx.comp.haveLibC()) {
186185 try ctx.args.append("-L");
187186 // TODO addNullByte should probably return [:0]u8
188 try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr));
187 try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.crt_dir.?)).ptr));
189188
190 try ctx.args.append("-L");
191 try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr));
192
193 if (!ctx.comp.is_static) {
194 const dl = blk: {
195 if (ctx.libc.dynamic_linker_path) |dl| break :blk dl;
196 if (util.getDynamicLinkerPath(ctx.comp.target)) |dl| break :blk dl;
197 return error.LibCMissingDynamicLinker;
198 };
199 try ctx.args.append("-dynamic-linker");
200 try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr));
201 }
189 //if (!ctx.comp.is_static) {
190 // const dl = blk: {
191 // //if (ctx.libc.dynamic_linker_path) |dl| break :blk dl;
192 // //if (util.getDynamicLinkerPath(ctx.comp.target)) |dl| break :blk dl;
193 // return error.LibCMissingDynamicLinker;
194 // };
195 // try ctx.args.append("-dynamic-linker");
196 // try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr));
197 //}
202198 }
203199
204200 //if (shared) {
......@@ -265,13 +261,12 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
265261
266262 // crt end
267263 if (ctx.link_in_crt) {
268 try addPathJoin(ctx, ctx.libc.static_lib_dir.?, "crtend.o");
269 try addPathJoin(ctx, ctx.libc.lib_dir.?, "crtn.o");
264 try addPathJoin(ctx, ctx.libc.crt_dir.?, "crtn.o");
270265 }
271266
272 if (ctx.comp.target != Target.Native) {
273 try ctx.args.append("--allow-shlib-undefined");
274 }
267 //if (ctx.comp.target != Target.Native) {
268 // try ctx.args.append("--allow-shlib-undefined");
269 //}
275270}
276271
277272fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
......@@ -287,7 +282,7 @@ fn constructLinkerArgsCoff(ctx: *Context) !void {
287282 try ctx.args.append("-DEBUG");
288283 }
289284
290 switch (ctx.comp.target.getArch()) {
285 switch (ctx.comp.target.cpu.arch) {
291286 .i386 => try ctx.args.append("-MACHINE:X86"),
292287 .x86_64 => try ctx.args.append("-MACHINE:X64"),
293288 .aarch64 => try ctx.args.append("-MACHINE:ARM"),
......@@ -302,7 +297,7 @@ fn constructLinkerArgsCoff(ctx: *Context) !void {
302297 if (ctx.comp.haveLibC()) {
303298 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.msvc_lib_dir.?})).ptr));
304299 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.kernel32_lib_dir.?})).ptr));
305 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.lib_dir.?})).ptr));
300 try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.crt_dir.?})).ptr));
306301 }
307302
308303 if (ctx.link_in_crt) {
......@@ -417,7 +412,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
417412 }
418413 },
419414 .IPhoneOS => {
420 if (ctx.comp.target.getArch() == .aarch64) {
415 if (ctx.comp.target.cpu.arch == .aarch64) {
421416 // iOS does not need any crt1 files for arm64
422417 } else if (platform.versionLessThan(3, 1)) {
423418 try ctx.args.append("-lcrt1.o");
......@@ -435,28 +430,29 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
435430 }
436431 try addFnObjects(ctx);
437432
438 if (ctx.comp.target == Target.Native) {
439 for (ctx.comp.link_libs_list.toSliceConst()) |lib| {
440 if (mem.eql(u8, lib.name, "c")) {
441 // on Darwin, libSystem has libc in it, but also you have to use it
442 // to make syscalls because the syscall numbers are not documented
443 // and change between versions.
444 // so we always link against libSystem
445 try ctx.args.append("-lSystem");
446 } else {
447 if (mem.indexOfScalar(u8, lib.name, '/') == null) {
448 const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", .{lib.name});
449 try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
450 } else {
451 const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name);
452 try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
453 }
454 }
455 }
456 } else {
457 try ctx.args.append("-undefined");
458 try ctx.args.append("dynamic_lookup");
459 }
433 // TODO
434 //if (ctx.comp.target == Target.Native) {
435 // for (ctx.comp.link_libs_list.toSliceConst()) |lib| {
436 // if (mem.eql(u8, lib.name, "c")) {
437 // // on Darwin, libSystem has libc in it, but also you have to use it
438 // // to make syscalls because the syscall numbers are not documented
439 // // and change between versions.
440 // // so we always link against libSystem
441 // try ctx.args.append("-lSystem");
442 // } else {
443 // if (mem.indexOfScalar(u8, lib.name, '/') == null) {
444 // const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", .{lib.name});
445 // try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
446 // } else {
447 // const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name);
448 // try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
449 // }
450 // }
451 // }
452 //} else {
453 // try ctx.args.append("-undefined");
454 // try ctx.args.append("dynamic_lookup");
455 //}
460456
461457 if (platform.kind == .MacOS) {
462458 if (platform.versionLessThan(10, 5)) {
src-self-hosted/main.zig+56-47
......@@ -18,10 +18,6 @@ const Target = std.Target;
1818const errmsg = @import("errmsg.zig");
1919const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
2020
21var stderr_file: fs.File = undefined;
22var stderr: *io.OutStream(fs.File.WriteError) = undefined;
23var stdout: *io.OutStream(fs.File.WriteError) = undefined;
24
2521pub const io_mode = .evented;
2622
2723pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
......@@ -51,17 +47,14 @@ const Command = struct {
5147pub fn main() !void {
5248 const allocator = std.heap.c_allocator;
5349
54 stdout = &std.io.getStdOut().outStream().stream;
55
56 stderr_file = std.io.getStdErr();
57 stderr = &stderr_file.outStream().stream;
50 const stderr = io.getStdErr().outStream();
5851
5952 const args = try process.argsAlloc(allocator);
6053 defer process.argsFree(allocator, args);
6154
6255 if (args.len <= 1) {
63 try stderr.write("expected command argument\n\n");
64 try stderr.write(usage);
56 try stderr.writeAll("expected command argument\n\n");
57 try stderr.writeAll(usage);
6558 process.exit(1);
6659 }
6760
......@@ -78,8 +71,8 @@ pub fn main() !void {
7871 } else if (mem.eql(u8, cmd, "libc")) {
7972 return cmdLibC(allocator, cmd_args);
8073 } else if (mem.eql(u8, cmd, "targets")) {
81 const info = try std.zig.system.NativeTargetInfo.detect(allocator);
82 defer info.deinit(allocator);
74 const info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
75 const stdout = io.getStdOut().outStream();
8376 return @import("print_targets.zig").cmdTargets(allocator, cmd_args, stdout, info.target);
8477 } else if (mem.eql(u8, cmd, "version")) {
8578 return cmdVersion(allocator, cmd_args);
......@@ -91,7 +84,7 @@ pub fn main() !void {
9184 return cmdInternal(allocator, cmd_args);
9285 } else {
9386 try stderr.print("unknown command: {}\n\n", .{args[1]});
94 try stderr.write(usage);
87 try stderr.writeAll(usage);
9588 process.exit(1);
9689 }
9790}
......@@ -156,6 +149,8 @@ const usage_build_generic =
156149;
157150
158151fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Compilation.Kind) !void {
152 const stderr = io.getStdErr().outStream();
153
159154 var color: errmsg.Color = .Auto;
160155 var build_mode: std.builtin.Mode = .Debug;
161156 var emit_bin = true;
......@@ -208,11 +203,11 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
208203 const arg = args[i];
209204 if (mem.startsWith(u8, arg, "-")) {
210205 if (mem.eql(u8, arg, "--help")) {
211 try stdout.write(usage_build_generic);
206 try io.getStdOut().writeAll(usage_build_generic);
212207 process.exit(0);
213208 } else if (mem.eql(u8, arg, "--color")) {
214209 if (i + 1 >= args.len) {
215 try stderr.write("expected [auto|on|off] after --color\n");
210 try stderr.writeAll("expected [auto|on|off] after --color\n");
216211 process.exit(1);
217212 }
218213 i += 1;
......@@ -229,7 +224,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
229224 }
230225 } else if (mem.eql(u8, arg, "--mode")) {
231226 if (i + 1 >= args.len) {
232 try stderr.write("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n");
227 try stderr.writeAll("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n");
233228 process.exit(1);
234229 }
235230 i += 1;
......@@ -248,49 +243,49 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
248243 }
249244 } else if (mem.eql(u8, arg, "--name")) {
250245 if (i + 1 >= args.len) {
251 try stderr.write("expected parameter after --name\n");
246 try stderr.writeAll("expected parameter after --name\n");
252247 process.exit(1);
253248 }
254249 i += 1;
255250 provided_name = args[i];
256251 } else if (mem.eql(u8, arg, "--ver-major")) {
257252 if (i + 1 >= args.len) {
258 try stderr.write("expected parameter after --ver-major\n");
253 try stderr.writeAll("expected parameter after --ver-major\n");
259254 process.exit(1);
260255 }
261256 i += 1;
262257 version.major = try std.fmt.parseInt(u32, args[i], 10);
263258 } else if (mem.eql(u8, arg, "--ver-minor")) {
264259 if (i + 1 >= args.len) {
265 try stderr.write("expected parameter after --ver-minor\n");
260 try stderr.writeAll("expected parameter after --ver-minor\n");
266261 process.exit(1);
267262 }
268263 i += 1;
269264 version.minor = try std.fmt.parseInt(u32, args[i], 10);
270265 } else if (mem.eql(u8, arg, "--ver-patch")) {
271266 if (i + 1 >= args.len) {
272 try stderr.write("expected parameter after --ver-patch\n");
267 try stderr.writeAll("expected parameter after --ver-patch\n");
273268 process.exit(1);
274269 }
275270 i += 1;
276271 version.patch = try std.fmt.parseInt(u32, args[i], 10);
277272 } else if (mem.eql(u8, arg, "--linker-script")) {
278273 if (i + 1 >= args.len) {
279 try stderr.write("expected parameter after --linker-script\n");
274 try stderr.writeAll("expected parameter after --linker-script\n");
280275 process.exit(1);
281276 }
282277 i += 1;
283278 linker_script = args[i];
284279 } else if (mem.eql(u8, arg, "--libc")) {
285280 if (i + 1 >= args.len) {
286 try stderr.write("expected parameter after --libc\n");
281 try stderr.writeAll("expected parameter after --libc\n");
287282 process.exit(1);
288283 }
289284 i += 1;
290285 libc_arg = args[i];
291286 } else if (mem.eql(u8, arg, "-mllvm")) {
292287 if (i + 1 >= args.len) {
293 try stderr.write("expected parameter after -mllvm\n");
288 try stderr.writeAll("expected parameter after -mllvm\n");
294289 process.exit(1);
295290 }
296291 i += 1;
......@@ -300,14 +295,14 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
300295 try mllvm_flags.append(args[i]);
301296 } else if (mem.eql(u8, arg, "-mmacosx-version-min")) {
302297 if (i + 1 >= args.len) {
303 try stderr.write("expected parameter after -mmacosx-version-min\n");
298 try stderr.writeAll("expected parameter after -mmacosx-version-min\n");
304299 process.exit(1);
305300 }
306301 i += 1;
307302 macosx_version_min = args[i];
308303 } else if (mem.eql(u8, arg, "-mios-version-min")) {
309304 if (i + 1 >= args.len) {
310 try stderr.write("expected parameter after -mios-version-min\n");
305 try stderr.writeAll("expected parameter after -mios-version-min\n");
311306 process.exit(1);
312307 }
313308 i += 1;
......@@ -348,7 +343,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
348343 linker_rdynamic = true;
349344 } else if (mem.eql(u8, arg, "--pkg-begin")) {
350345 if (i + 2 >= args.len) {
351 try stderr.write("expected [name] [path] after --pkg-begin\n");
346 try stderr.writeAll("expected [name] [path] after --pkg-begin\n");
352347 process.exit(1);
353348 }
354349 i += 1;
......@@ -363,7 +358,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
363358 if (cur_pkg.parent) |parent| {
364359 cur_pkg = parent;
365360 } else {
366 try stderr.write("encountered --pkg-end with no matching --pkg-begin\n");
361 try stderr.writeAll("encountered --pkg-end with no matching --pkg-begin\n");
367362 process.exit(1);
368363 }
369364 } else if (mem.startsWith(u8, arg, "-l")) {
......@@ -411,18 +406,18 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
411406 var it = mem.separate(basename, ".");
412407 break :blk it.next() orelse basename;
413408 } else {
414 try stderr.write("--name [name] not provided and unable to infer\n");
409 try stderr.writeAll("--name [name] not provided and unable to infer\n");
415410 process.exit(1);
416411 }
417412 };
418413
419414 if (root_src_file == null and link_objects.len == 0 and assembly_files.len == 0) {
420 try stderr.write("Expected source file argument or at least one --object or --assembly argument\n");
415 try stderr.writeAll("Expected source file argument or at least one --object or --assembly argument\n");
421416 process.exit(1);
422417 }
423418
424419 if (out_type == Compilation.Kind.Obj and link_objects.len != 0) {
425 try stderr.write("When building an object file, --object arguments are invalid\n");
420 try stderr.writeAll("When building an object file, --object arguments are invalid\n");
426421 process.exit(1);
427422 }
428423
......@@ -440,7 +435,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
440435 &zig_compiler,
441436 root_name,
442437 root_src_file,
443 Target.Native,
438 .{},
444439 out_type,
445440 build_mode,
446441 !is_dynamic,
......@@ -478,7 +473,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
478473 comp.linker_rdynamic = linker_rdynamic;
479474
480475 if (macosx_version_min != null and ios_version_min != null) {
481 try stderr.write("-mmacosx-version-min and -mios-version-min options not allowed together\n");
476 try stderr.writeAll("-mmacosx-version-min and -mios-version-min options not allowed together\n");
482477 process.exit(1);
483478 }
484479
......@@ -501,6 +496,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
501496}
502497
503498fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
499 const stderr_file = io.getStdErr();
500 const stderr = stderr_file.outStream();
504501 var count: usize = 0;
505502 while (!comp.cancelled) {
506503 const build_event = comp.events.get();
......@@ -551,7 +548,8 @@ const Fmt = struct {
551548};
552549
553550fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
554 libc.parse(allocator, libc_paths_file, stderr) catch |err| {
551 const stderr = io.getStdErr().outStream();
552 libc.* = LibCInstallation.parse(allocator, libc_paths_file, stderr) catch |err| {
555553 stderr.print("Unable to parse libc path file '{}': {}.\n" ++
556554 "Try running `zig libc` to see an example for the native target.\n", .{
557555 libc_paths_file,
......@@ -562,6 +560,7 @@ fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_fil
562560}
563561
564562fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
563 const stderr = io.getStdErr().outStream();
565564 switch (args.len) {
566565 0 => {},
567566 1 => {
......@@ -582,10 +581,12 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
582581 stderr.print("unable to find libc: {}\n", .{@errorName(err)}) catch {};
583582 process.exit(1);
584583 };
585 libc.render(stdout) catch process.exit(1);
584 libc.render(io.getStdOut().outStream()) catch process.exit(1);
586585}
587586
588587fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
588 const stderr_file = io.getStdErr();
589 const stderr = stderr_file.outStream();
589590 var color: errmsg.Color = .Auto;
590591 var stdin_flag: bool = false;
591592 var check_flag: bool = false;
......@@ -597,11 +598,12 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
597598 const arg = args[i];
598599 if (mem.startsWith(u8, arg, "-")) {
599600 if (mem.eql(u8, arg, "--help")) {
600 try stdout.write(usage_fmt);
601 const stdout = io.getStdOut().outStream();
602 try stdout.writeAll(usage_fmt);
601603 process.exit(0);
602604 } else if (mem.eql(u8, arg, "--color")) {
603605 if (i + 1 >= args.len) {
604 try stderr.write("expected [auto|on|off] after --color\n");
606 try stderr.writeAll("expected [auto|on|off] after --color\n");
605607 process.exit(1);
606608 }
607609 i += 1;
......@@ -632,14 +634,13 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
632634
633635 if (stdin_flag) {
634636 if (input_files.len != 0) {
635 try stderr.write("cannot use --stdin with positional arguments\n");
637 try stderr.writeAll("cannot use --stdin with positional arguments\n");
636638 process.exit(1);
637639 }
638640
639 var stdin_file = io.getStdIn();
640 var stdin = stdin_file.inStream();
641 const stdin = io.getStdIn().inStream();
641642
642 const source_code = try stdin.stream.readAllAlloc(allocator, max_src_size);
643 const source_code = try stdin.readAllAlloc(allocator, max_src_size);
643644 defer allocator.free(source_code);
644645
645646 const tree = std.zig.parse(allocator, source_code) catch |err| {
......@@ -653,7 +654,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
653654 const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, tree, "<stdin>");
654655 defer msg.destroy();
655656
656 try msg.printToFile(stderr_file, color);
657 try msg.printToFile(io.getStdErr(), color);
657658 }
658659 if (tree.errors.len != 0) {
659660 process.exit(1);
......@@ -664,12 +665,13 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
664665 process.exit(code);
665666 }
666667
668 const stdout = io.getStdOut().outStream();
667669 _ = try std.zig.render(allocator, stdout, tree);
668670 return;
669671 }
670672
671673 if (input_files.len == 0) {
672 try stderr.write("expected at least one source file argument\n");
674 try stderr.writeAll("expected at least one source file argument\n");
673675 process.exit(1);
674676 }
675677
......@@ -713,6 +715,9 @@ const FmtError = error{
713715} || fs.File.OpenError;
714716
715717async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void {
718 const stderr_file = io.getStdErr();
719 const stderr = stderr_file.outStream();
720
716721 const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref);
717722 defer fmt.allocator.free(file_path);
718723
......@@ -729,7 +734,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
729734 max_src_size,
730735 ) catch |err| switch (err) {
731736 error.IsDir, error.AccessDenied => {
732 var dir = try fs.cwd().openDirList(file_path);
737 var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
733738 defer dir.close();
734739
735740 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
791796}
792797
793798fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void {
799 const stdout = io.getStdOut().outStream();
794800 try stdout.print("{}\n", .{c.ZIG_VERSION_STRING});
795801}
796802
797803fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void {
798 try stdout.write(usage);
804 const stdout = io.getStdOut();
805 try stdout.writeAll(usage);
799806}
800807
801808pub const info_zen =
......@@ -816,7 +823,7 @@ pub const info_zen =
816823;
817824
818825fn cmdZen(allocator: *Allocator, args: []const []const u8) !void {
819 try stdout.write(info_zen);
826 try io.getStdOut().writeAll(info_zen);
820827}
821828
822829const usage_internal =
......@@ -829,8 +836,9 @@ const usage_internal =
829836;
830837
831838fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void {
839 const stderr = io.getStdErr().outStream();
832840 if (args.len == 0) {
833 try stderr.write(usage_internal);
841 try stderr.writeAll(usage_internal);
834842 process.exit(1);
835843 }
836844
......@@ -849,10 +857,11 @@ fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void {
849857 }
850858
851859 try stderr.print("unknown sub command: {}\n\n", .{args[0]});
852 try stderr.write(usage_internal);
860 try stderr.writeAll(usage_internal);
853861}
854862
855863fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void {
864 const stdout = io.getStdOut().outStream();
856865 try stdout.print(
857866 \\ZIG_CMAKE_BINARY_DIR {}
858867 \\ZIG_CXX_COMPILER {}
src-self-hosted/print_targets.zig+1-1
......@@ -72,7 +72,7 @@ pub fn cmdTargets(
7272 };
7373 defer allocator.free(zig_lib_dir);
7474
75 var dir = try std.fs.cwd().openDirList(zig_lib_dir);
75 var dir = try std.fs.cwd().openDir(zig_lib_dir, .{});
7676 defer dir.close();
7777
7878 const vers_txt = try dir.readFileAlloc(allocator, "libc/glibc/vers.txt", 10 * 1024);
src-self-hosted/stage2.zig+2-2
......@@ -128,7 +128,7 @@ export fn stage2_translate_c(
128128 args_end: [*]?[*]const u8,
129129 resources_path: [*:0]const u8,
130130) Error {
131 var errors = @as([*]translate_c.ClangErrMsg, undefined)[0..0];
131 var errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
132132 out_ast.* = translate_c.translate(std.heap.c_allocator, args_begin, args_end, &errors, resources_path) catch |err| switch (err) {
133133 error.SemanticAnalyzeFail => {
134134 out_errors_ptr.* = errors.ptr;
......@@ -319,7 +319,7 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
319319 const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) {
320320 error.IsDir, error.AccessDenied => {
321321 // TODO make event based (and dir.next())
322 var dir = try fs.cwd().openDirList(file_path);
322 var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
323323 defer dir.close();
324324
325325 var dir_it = dir.iterate();
src-self-hosted/test.zig+2-2
......@@ -57,11 +57,11 @@ pub const TestContext = struct {
5757 errdefer allocator.free(self.zig_lib_dir);
5858
5959 try std.fs.cwd().makePath(tmp_dir_name);
60 errdefer std.fs.deleteTree(tmp_dir_name) catch {};
60 errdefer std.fs.cwd().deleteTree(tmp_dir_name) catch {};
6161 }
6262
6363 fn deinit(self: *TestContext) void {
64 std.fs.deleteTree(tmp_dir_name) catch {};
64 std.fs.cwd().deleteTree(tmp_dir_name) catch {};
6565 allocator.free(self.zig_lib_dir);
6666 self.zig_compiler.deinit();
6767 }
src-self-hosted/translate_c.zig+12-14
......@@ -1744,20 +1744,18 @@ fn writeEscapedString(buf: []u8, s: []const u8) void {
17441744// Returns either a string literal or a slice of `buf`.
17451745fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 {
17461746 return switch (c) {
1747 '\"' => "\\\""[0..],
1748 '\'' => "\\'"[0..],
1749 '\\' => "\\\\"[0..],
1750 '\n' => "\\n"[0..],
1751 '\r' => "\\r"[0..],
1752 '\t' => "\\t"[0..],
1753 else => {
1754 // Handle the remaining escapes Zig doesn't support by turning them
1755 // into their respective hex representation
1756 if (std.ascii.isCntrl(c))
1757 return std.fmt.bufPrint(char_buf[0..], "\\x{x:0<2}", .{c}) catch unreachable
1758 else
1759 return std.fmt.bufPrint(char_buf[0..], "{c}", .{c}) catch unreachable;
1760 },
1747 '\"' => "\\\"",
1748 '\'' => "\\'",
1749 '\\' => "\\\\",
1750 '\n' => "\\n",
1751 '\r' => "\\r",
1752 '\t' => "\\t",
1753 // Handle the remaining escapes Zig doesn't support by turning them
1754 // into their respective hex representation
1755 else => if (std.ascii.isCntrl(c))
1756 std.fmt.bufPrint(char_buf, "\\x{x:0<2}", .{c}) catch unreachable
1757 else
1758 std.fmt.bufPrint(char_buf, "{c}", .{c}) catch unreachable,
17611759 };
17621760}
17631761
src-self-hosted/util.zig+13-2
......@@ -3,8 +3,7 @@ const Target = std.Target;
33const llvm = @import("llvm.zig");
44
55pub fn getDarwinArchString(self: Target) [:0]const u8 {
6 const arch = self.getArch();
7 switch (arch) {
6 switch (self.cpu.arch) {
87 .aarch64 => return "arm64",
98 .thumb,
109 .arm,
......@@ -34,3 +33,15 @@ pub fn initializeAllTargets() void {
3433 llvm.InitializeAllAsmPrinters();
3534 llvm.InitializeAllAsmParsers();
3635}
36
37pub fn getLLVMTriple(allocator: *std.mem.Allocator, target: std.Target) !std.Buffer {
38 var result = try std.Buffer.initSize(allocator, 0);
39 errdefer result.deinit();
40
41 try result.outStream().print(
42 "{}-unknown-{}-{}",
43 .{ @tagName(target.cpu.arch), @tagName(target.os.tag), @tagName(target.abi) },
44 );
45
46 return result;
47}
src/all_types.hpp+6
......@@ -231,6 +231,7 @@ enum ConstPtrSpecial {
231231 // The pointer is a reference to a single object.
232232 ConstPtrSpecialRef,
233233 // The pointer points to an element in an underlying array.
234 // Not to be confused with ConstPtrSpecialSubArray.
234235 ConstPtrSpecialBaseArray,
235236 // The pointer points to a field in an underlying struct.
236237 ConstPtrSpecialBaseStruct,
......@@ -257,6 +258,10 @@ enum ConstPtrSpecial {
257258 // types to be the same, so all optionals of pointer types use x_ptr
258259 // instead of x_optional.
259260 ConstPtrSpecialNull,
261 // The pointer points to a sub-array (not an individual element).
262 // Not to be confused with ConstPtrSpecialBaseArray. However, it uses the same
263 // union payload struct (base_array).
264 ConstPtrSpecialSubArray,
260265};
261266
262267enum ConstPtrMut {
......@@ -3705,6 +3710,7 @@ struct IrInstGenSlice {
37053710 IrInstGen *start;
37063711 IrInstGen *end;
37073712 IrInstGen *result_loc;
3713 ZigValue *sentinel;
37083714 bool safety_check_on;
37093715};
37103716
src/analyze.cpp+36-20
......@@ -780,6 +780,8 @@ ZigType *get_error_union_type(CodeGen *g, ZigType *err_set_type, ZigType *payloa
780780}
781781
782782ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, ZigValue *sentinel) {
783 Error err;
784
783785 TypeId type_id = {};
784786 type_id.id = ZigTypeIdArray;
785787 type_id.data.array.codegen = g;
......@@ -791,7 +793,11 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, Zi
791793 return existing_entry->value;
792794 }
793795
794 assert(type_is_resolved(child_type, ResolveStatusSizeKnown));
796 size_t full_array_size = array_size + ((sentinel != nullptr) ? 1 : 0);
797
798 if (full_array_size != 0 && (err = type_resolve(g, child_type, ResolveStatusSizeKnown))) {
799 codegen_report_errors_and_exit(g);
800 }
795801
796802 ZigType *entry = new_type_table_entry(ZigTypeIdArray);
797803
......@@ -803,15 +809,8 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, Zi
803809 }
804810 buf_appendf(&entry->name, "]%s", buf_ptr(&child_type->name));
805811
806 size_t full_array_size;
807 if (array_size == 0) {
808 full_array_size = 0;
809 } else {
810 full_array_size = array_size + ((sentinel != nullptr) ? 1 : 0);
811 }
812
813812 entry->size_in_bits = child_type->size_in_bits * full_array_size;
814 entry->abi_align = child_type->abi_align;
813 entry->abi_align = (full_array_size == 0) ? 0 : child_type->abi_align;
815814 entry->abi_size = child_type->abi_size * full_array_size;
816815
817816 entry->data.array.child_type = child_type;
......@@ -1197,7 +1196,8 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent
11971196 LazyValueArrayType *lazy_array_type =
11981197 reinterpret_cast<LazyValueArrayType *>(type_val->data.x_lazy);
11991198
1200 if (lazy_array_type->length < 1) {
1199 // The sentinel counts as an extra element
1200 if (lazy_array_type->length == 0 && lazy_array_type->sentinel == nullptr) {
12011201 *is_zero_bits = true;
12021202 return ErrorNone;
12031203 }
......@@ -1452,7 +1452,7 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV
14521452 case LazyValueIdArrayType: {
14531453 LazyValueArrayType *lazy_array_type =
14541454 reinterpret_cast<LazyValueArrayType *>(type_val->data.x_lazy);
1455 if (lazy_array_type->length < 1)
1455 if (lazy_array_type->length == 0)
14561456 return OnePossibleValueYes;
14571457 return type_val_resolve_has_one_possible_value(g, lazy_array_type->elem_type->value);
14581458 }
......@@ -4488,7 +4488,14 @@ static uint32_t get_async_frame_align_bytes(CodeGen *g) {
44884488}
44894489
44904490uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
4491 ZigType *ptr_type = get_src_ptr_type(type);
4491 ZigType *ptr_type;
4492 if (type->id == ZigTypeIdStruct) {
4493 assert(type->data.structure.special == StructSpecialSlice);
4494 TypeStructField *ptr_field = type->data.structure.fields[slice_ptr_index];
4495 ptr_type = resolve_struct_field_type(g, ptr_field);
4496 } else {
4497 ptr_type = get_src_ptr_type(type);
4498 }
44924499 if (ptr_type->id == ZigTypeIdPointer) {
44934500 return (ptr_type->data.pointer.explicit_alignment == 0) ?
44944501 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) {
45054512 }
45064513}
45074514
4508bool get_ptr_const(ZigType *type) {
4509 ZigType *ptr_type = get_src_ptr_type(type);
4515bool get_ptr_const(CodeGen *g, ZigType *type) {
4516 ZigType *ptr_type;
4517 if (type->id == ZigTypeIdStruct) {
4518 assert(type->data.structure.special == StructSpecialSlice);
4519 TypeStructField *ptr_field = type->data.structure.fields[slice_ptr_index];
4520 ptr_type = resolve_struct_field_type(g, ptr_field);
4521 } else {
4522 ptr_type = get_src_ptr_type(type);
4523 }
45104524 if (ptr_type->id == ZigTypeIdPointer) {
45114525 return ptr_type->data.pointer.is_const;
45124526 } else if (ptr_type->id == ZigTypeIdFn) {
......@@ -5282,6 +5296,11 @@ static uint32_t hash_const_val_ptr(ZigValue *const_val) {
52825296 hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
52835297 hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
52845298 return hash_val;
5299 case ConstPtrSpecialSubArray:
5300 hash_val += (uint32_t)2643358777;
5301 hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
5302 hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
5303 return hash_val;
52855304 case ConstPtrSpecialBaseStruct:
52865305 hash_val += (uint32_t)3518317043;
52875306 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) {
58115830 // The elements array cannot be left unpopulated
58125831 ZigType *array_type = result->type;
58135832 ZigType *elem_type = array_type->data.array.child_type;
5814 ZigValue *sentinel_value = array_type->data.array.sentinel;
5815 const size_t elem_count = array_type->data.array.len + (sentinel_value != nullptr);
5833 const size_t elem_count = array_type->data.array.len;
58165834
58175835 result->data.x_array.data.s_none.elements = g->pass1_arena->allocate<ZigValue>(elem_count);
58185836 for (size_t i = 0; i < elem_count; i += 1) {
58195837 ZigValue *elem_val = &result->data.x_array.data.s_none.elements[i];
58205838 copy_const_val(g, elem_val, get_the_one_possible_value(g, elem_type));
58215839 }
5822 if (sentinel_value != nullptr) {
5823 ZigValue *last_elem_val = &result->data.x_array.data.s_none.elements[elem_count - 1];
5824 copy_const_val(g, last_elem_val, sentinel_value);
5825 }
58265840 } else if (result->type->id == ZigTypeIdPointer) {
58275841 result->data.x_ptr.special = ConstPtrSpecialRef;
58285842 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) {
67536767 return false;
67546768 return true;
67556769 case ConstPtrSpecialBaseArray:
6770 case ConstPtrSpecialSubArray:
67566771 if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val) {
67576772 return false;
67586773 }
......@@ -7010,6 +7025,7 @@ static void render_const_val_ptr(CodeGen *g, Buf *buf, ZigValue *const_val, ZigT
70107025 render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr));
70117026 return;
70127027 case ConstPtrSpecialBaseArray:
7028 case ConstPtrSpecialSubArray:
70137029 buf_appendf(buf, "*");
70147030 // TODO we need a source node for const_ptr_pointee because it can generate compile errors
70157031 render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr));
src/analyze.hpp+1-1
......@@ -76,7 +76,7 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node, bool all
7676
7777ZigType *get_src_ptr_type(ZigType *type);
7878uint32_t get_ptr_align(CodeGen *g, ZigType *type);
79bool get_ptr_const(ZigType *type);
79bool get_ptr_const(CodeGen *g, ZigType *type);
8080ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry);
8181ZigType *container_ref_type(ZigType *type_entry);
8282bool type_is_complete(ZigType *type_entry);
src/codegen.cpp+113-52
......@@ -5413,12 +5413,16 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI
54135413 ZigType *array_type = array_ptr_type->data.pointer.child_type;
54145414 LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type);
54155415
5416 LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
5417
54185416 bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base);
54195417
5420 ZigType *res_slice_ptr_type = instruction->base.value->type->data.structure.fields[slice_ptr_index]->type_entry;
5421 ZigValue *sentinel = res_slice_ptr_type->data.pointer.sentinel;
5418 ZigType *result_type = instruction->base.value->type;
5419 if (!type_has_bits(g, result_type)) {
5420 return nullptr;
5421 }
5422
5423 // This is not whether the result type has a sentinel, but whether there should be a sentinel check,
5424 // e.g. if they used [a..b :s] syntax.
5425 ZigValue *sentinel = instruction->sentinel;
54225426
54235427 if (array_type->id == ZigTypeIdArray ||
54245428 (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
54535457 }
54545458 }
54555459 if (!type_has_bits(g, array_type)) {
5460 LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
5461
54565462 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, "");
54575463
54585464 // 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
54615467 return tmp_struct_ptr;
54625468 }
54635469
5464
5465 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, "");
54665470 LLVMValueRef indices[] = {
54675471 LLVMConstNull(g->builtin_types.entry_usize->llvm_type),
54685472 start_val,
54695473 };
54705474 LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
5471 gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
5475 if (result_type->id == ZigTypeIdPointer) {
5476 ir_assert(instruction->result_loc == nullptr, &instruction->base);
5477 LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type);
5478 return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, "");
5479 } else {
5480 LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
5481 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, "");
5482 gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
54725483
5473 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, "");
5474 LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
5475 gen_store_untyped(g, len_value, len_field_ptr, 0, false);
5484 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, "");
5485 LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
5486 gen_store_untyped(g, len_value, len_field_ptr, 0, false);
54765487
5477 return tmp_struct_ptr;
5488 return tmp_struct_ptr;
5489 }
54785490 } else if (array_type->id == ZigTypeIdPointer) {
54795491 assert(array_type->data.pointer.ptr_len != PtrLenSingle);
54805492 LLVMValueRef start_val = ir_llvm_value(g, instruction->start);
......@@ -5488,24 +5500,39 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI
54885500 }
54895501 }
54905502
5491 if (type_has_bits(g, array_type)) {
5492 size_t gen_ptr_index = instruction->base.value->type->data.structure.fields[slice_ptr_index]->gen_index;
5493 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, "");
5494 LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");
5495 gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
5503 if (!type_has_bits(g, array_type)) {
5504 LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
5505 size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index;
5506 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, "");
5507 LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
5508 gen_store_untyped(g, len_value, len_field_ptr, 0, false);
5509 return tmp_struct_ptr;
54965510 }
54975511
5498 size_t gen_len_index = instruction->base.value->type->data.structure.fields[slice_len_index]->gen_index;
5512 LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");
5513 if (result_type->id == ZigTypeIdPointer) {
5514 ir_assert(instruction->result_loc == nullptr, &instruction->base);
5515 LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type);
5516 return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, "");
5517 }
5518
5519 LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
5520
5521 size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index;
5522 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, "");
5523 gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
5524
5525 size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index;
54995526 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, "");
55005527 LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
55015528 gen_store_untyped(g, len_value, len_field_ptr, 0, false);
55025529
55035530 return tmp_struct_ptr;
5531
55045532 } else if (array_type->id == ZigTypeIdStruct) {
55055533 assert(array_type->data.structure.special == StructSpecialSlice);
55065534 assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
55075535 assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
5508 assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(tmp_struct_ptr))) == LLVMStructTypeKind);
55095536
55105537 size_t ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index;
55115538 assert(ptr_index != SIZE_MAX);
......@@ -5542,15 +5569,22 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI
55425569 }
55435570 }
55445571
5545 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, "");
55465572 LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, "");
5547 gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
5573 if (result_type->id == ZigTypeIdPointer) {
5574 ir_assert(instruction->result_loc == nullptr, &instruction->base);
5575 LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type);
5576 return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, "");
5577 } else {
5578 LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
5579 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, "");
5580 gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
55485581
5549 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, "");
5550 LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
5551 gen_store_untyped(g, len_value, len_field_ptr, 0, false);
5582 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, "");
5583 LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
5584 gen_store_untyped(g, len_value, len_field_ptr, 0, false);
55525585
5553 return tmp_struct_ptr;
5586 return tmp_struct_ptr;
5587 }
55545588 } else {
55555589 zig_unreachable();
55565590 }
......@@ -6635,7 +6669,6 @@ static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ZigValue *array_co
66356669 };
66366670 return LLVMConstInBoundsGEP(base_ptr, indices, 2);
66376671 } else {
6638 assert(parent->id == ConstParentIdScalar);
66396672 return base_ptr;
66406673 }
66416674}
......@@ -6785,6 +6818,22 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Zig
67856818 used_bits += packed_bits_size;
67866819 }
67876820 }
6821
6822 if (type_entry->data.array.sentinel != nullptr) {
6823 ZigValue *elem_val = type_entry->data.array.sentinel;
6824 LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, elem_val);
6825
6826 if (is_big_endian) {
6827 LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, packed_bits_size, false);
6828 val = LLVMConstShl(val, shift_amt);
6829 val = LLVMConstOr(val, child_val);
6830 } else {
6831 LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false);
6832 LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt);
6833 val = LLVMConstOr(val, child_val_shifted);
6834 used_bits += packed_bits_size;
6835 }
6836 }
67886837 return val;
67896838 }
67906839 case ZigTypeIdVector:
......@@ -6847,24 +6896,16 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ZigValue *const_val, const cha
68476896 return const_val->llvm_value;
68486897 }
68496898 case ConstPtrSpecialBaseArray:
6899 case ConstPtrSpecialSubArray:
68506900 {
68516901 ZigValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val;
68526902 assert(array_const_val->type->id == ZigTypeIdArray);
68536903 if (!type_has_bits(g, array_const_val->type)) {
6854 if (array_const_val->type->data.array.sentinel != nullptr) {
6855 ZigValue *pointee = array_const_val->type->data.array.sentinel;
6856 render_const_val(g, pointee, "");
6857 render_const_val_global(g, pointee, "");
6858 const_val->llvm_value = LLVMConstBitCast(pointee->llvm_global,
6859 get_llvm_type(g, const_val->type));
6860 return const_val->llvm_value;
6861 } else {
6862 // make this a null pointer
6863 ZigType *usize = g->builtin_types.entry_usize;
6864 const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type),
6865 get_llvm_type(g, const_val->type));
6866 return const_val->llvm_value;
6867 }
6904 // make this a null pointer
6905 ZigType *usize = g->builtin_types.entry_usize;
6906 const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type),
6907 get_llvm_type(g, const_val->type));
6908 return const_val->llvm_value;
68686909 }
68696910 size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
68706911 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
96449685 return ErrorNone;
96459686}
96469687
9688static bool need_llvm_module(CodeGen *g) {
9689 return buf_len(&g->main_pkg->root_src_path) != 0;
9690}
9691
9692// before gen_c_objects
9693static bool main_output_dir_is_just_one_c_object_pre(CodeGen *g) {
9694 return g->enable_cache && g->c_source_files.length == 1 && !need_llvm_module(g) &&
9695 g->out_type == OutTypeObj && g->link_objects.length == 0;
9696}
9697
9698// after gen_c_objects
9699static bool main_output_dir_is_just_one_c_object_post(CodeGen *g) {
9700 return g->enable_cache && g->link_objects.length == 1 && !need_llvm_module(g) && g->out_type == OutTypeObj;
9701}
9702
96479703// returns true if it was a cache miss
96489704static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
96499705 Error err;
......@@ -9661,7 +9717,12 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
96619717 buf_len(c_source_basename), 0);
96629718
96639719 Buf *final_o_basename = buf_alloc();
9664 os_path_extname(c_source_basename, final_o_basename, nullptr);
9720 // We special case when doing build-obj for just one C file
9721 if (main_output_dir_is_just_one_c_object_pre(g)) {
9722 buf_init_from_buf(final_o_basename, g->root_out_name);
9723 } else {
9724 os_path_extname(c_source_basename, final_o_basename, nullptr);
9725 }
96659726 buf_append_str(final_o_basename, target_o_file_ext(g->zig_target));
96669727
96679728 CacheHash *cache_hash;
......@@ -10461,10 +10522,6 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
1046110522 return ErrorNone;
1046210523}
1046310524
10464static bool need_llvm_module(CodeGen *g) {
10465 return buf_len(&g->main_pkg->root_src_path) != 0;
10466}
10467
1046810525static void resolve_out_paths(CodeGen *g) {
1046910526 assert(g->output_dir != nullptr);
1047010527 assert(g->root_out_name != nullptr);
......@@ -10476,10 +10533,6 @@ static void resolve_out_paths(CodeGen *g) {
1047610533 case OutTypeUnknown:
1047710534 zig_unreachable();
1047810535 case OutTypeObj:
10479 if (g->enable_cache && g->link_objects.length == 1 && !need_llvm_module(g)) {
10480 buf_init_from_buf(&g->bin_file_output_path, g->link_objects.at(0));
10481 return;
10482 }
1048310536 if (need_llvm_module(g) && g->link_objects.length != 0 && !g->enable_cache &&
1048410537 buf_eql_buf(o_basename, out_basename))
1048510538 {
......@@ -10574,6 +10627,16 @@ static void output_type_information(CodeGen *g) {
1057410627 }
1057510628}
1057610629
10630static void init_output_dir(CodeGen *g, Buf *digest) {
10631 if (main_output_dir_is_just_one_c_object_post(g)) {
10632 g->output_dir = buf_alloc();
10633 os_path_dirname(g->link_objects.at(0), g->output_dir);
10634 } else {
10635 g->output_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s",
10636 buf_ptr(g->cache_dir), buf_ptr(digest));
10637 }
10638}
10639
1057710640void codegen_build_and_link(CodeGen *g) {
1057810641 Error err;
1057910642 assert(g->out_type != OutTypeUnknown);
......@@ -10616,8 +10679,7 @@ void codegen_build_and_link(CodeGen *g) {
1061610679 }
1061710680
1061810681 if (g->enable_cache && buf_len(&digest) != 0) {
10619 g->output_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s",
10620 buf_ptr(g->cache_dir), buf_ptr(&digest));
10682 init_output_dir(g, &digest);
1062110683 resolve_out_paths(g);
1062210684 } else {
1062310685 if (need_llvm_module(g)) {
......@@ -10638,8 +10700,7 @@ void codegen_build_and_link(CodeGen *g) {
1063810700 exit(1);
1063910701 }
1064010702 }
10641 g->output_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s",
10642 buf_ptr(g->cache_dir), buf_ptr(&digest));
10703 init_output_dir(g, &digest);
1064310704
1064410705 if ((err = os_make_path(g->output_dir))) {
1064510706 fprintf(stderr, "Unable to create output directory: %s\n", err_str(err));
src/ir.cpp+327-96
......@@ -784,14 +784,32 @@ static ZigValue *const_ptr_pointee_unchecked_no_isf(CodeGen *g, ZigValue *const_
784784 break;
785785 case ConstPtrSpecialBaseArray: {
786786 ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val;
787 if (const_val->data.x_ptr.data.base_array.elem_index == array_val->type->data.array.len) {
787 size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
788 if (elem_index == array_val->type->data.array.len) {
788789 result = array_val->type->data.array.sentinel;
789790 } else {
790791 expand_undef_array(g, array_val);
791 result = &array_val->data.x_array.data.s_none.elements[const_val->data.x_ptr.data.base_array.elem_index];
792 result = &array_val->data.x_array.data.s_none.elements[elem_index];
792793 }
793794 break;
794795 }
796 case ConstPtrSpecialSubArray: {
797 ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val;
798 size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
799
800 // TODO handle sentinel terminated arrays
801 expand_undef_array(g, array_val);
802 result = g->pass1_arena->create<ZigValue>();
803 result->special = array_val->special;
804 result->type = get_array_type(g, array_val->type->data.array.child_type,
805 array_val->type->data.array.len - elem_index, nullptr);
806 result->data.x_array.special = ConstArraySpecialNone;
807 result->data.x_array.data.s_none.elements = &array_val->data.x_array.data.s_none.elements[elem_index];
808 result->parent.id = ConstParentIdArray;
809 result->parent.data.p_array.array_val = array_val;
810 result->parent.data.p_array.elem_index = elem_index;
811 break;
812 }
795813 case ConstPtrSpecialBaseStruct: {
796814 ZigValue *struct_val = const_val->data.x_ptr.data.base_struct.struct_val;
797815 expand_undef_struct(g, struct_val);
......@@ -849,11 +867,6 @@ static bool is_slice(ZigType *type) {
849867 return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialSlice;
850868}
851869
852static bool slice_is_const(ZigType *type) {
853 assert(is_slice(type));
854 return type->data.structure.fields[slice_ptr_index]->type_entry->data.pointer.is_const;
855}
856
857870// This function returns true when you can change the type of a ZigValue and the
858871// value remains meaningful.
859872static 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
37193732}
37203733
37213734static IrInstGen *ir_build_slice_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *slice_type,
3722 IrInstGen *ptr, IrInstGen *start, IrInstGen *end, bool safety_check_on, IrInstGen *result_loc)
3735 IrInstGen *ptr, IrInstGen *start, IrInstGen *end, bool safety_check_on, IrInstGen *result_loc,
3736 ZigValue *sentinel)
37233737{
37243738 IrInstGenSlice *instruction = ir_build_inst_gen<IrInstGenSlice>(
37253739 &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,
37293743 instruction->end = end;
37303744 instruction->safety_check_on = safety_check_on;
37313745 instruction->result_loc = result_loc;
3746 instruction->sentinel = sentinel;
37323747
37333748 ir_ref_inst_gen(ptr, ira->new_irb.current_basic_block);
37343749 ir_ref_inst_gen(start, ira->new_irb.current_basic_block);
3735 if (end) ir_ref_inst_gen(end, ira->new_irb.current_basic_block);
3736 ir_ref_inst_gen(result_loc, ira->new_irb.current_basic_block);
3750 if (end != nullptr) ir_ref_inst_gen(end, ira->new_irb.current_basic_block);
3751 if (result_loc != nullptr) ir_ref_inst_gen(result_loc, ira->new_irb.current_basic_block);
37373752
37383753 return &instruction->base;
37393754}
......@@ -12644,41 +12659,80 @@ static IrInstGen *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInst* sourc
1264412659 Error err;
1264512660
1264612661 assert(array_ptr->value->type->id == ZigTypeIdPointer);
12662 assert(array_ptr->value->type->data.pointer.child_type->id == ZigTypeIdArray);
12663
12664 ZigType *array_type = array_ptr->value->type->data.pointer.child_type;
12665 size_t array_len = array_type->data.array.len;
12666
12667 // A zero-sized array can be casted regardless of the destination alignment, or
12668 // whether the pointer is undefined, and the result is always comptime known.
12669 // TODO However, this is exposing a result location bug that I failed to solve on the first try.
12670 // If you want to try to fix the bug, uncomment this block and get the tests passing.
12671 //if (array_len == 0 && array_type->data.array.sentinel == nullptr) {
12672 // ZigValue *undef_array = ira->codegen->pass1_arena->create<ZigValue>();
12673 // undef_array->special = ConstValSpecialUndef;
12674 // undef_array->type = array_type;
12675
12676 // IrInstGen *result = ir_const(ira, source_instr, wanted_type);
12677 // init_const_slice(ira->codegen, result->value, undef_array, 0, 0, false);
12678 // result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutComptimeConst;
12679 // result->value->type = wanted_type;
12680 // return result;
12681 //}
1264712682
1264812683 if ((err = type_resolve(ira->codegen, array_ptr->value->type, ResolveStatusAlignmentKnown))) {
1264912684 return ira->codegen->invalid_inst_gen;
1265012685 }
1265112686
12652 assert(array_ptr->value->type->data.pointer.child_type->id == ZigTypeIdArray);
12653
12654 const size_t array_len = array_ptr->value->type->data.pointer.child_type->data.array.len;
12655
12656 // A zero-sized array can always be casted irregardless of the destination
12657 // alignment
1265812687 if (array_len != 0) {
1265912688 wanted_type = adjust_slice_align(ira->codegen, wanted_type,
1266012689 get_ptr_align(ira->codegen, array_ptr->value->type));
1266112690 }
1266212691
1266312692 if (instr_is_comptime(array_ptr)) {
12664 ZigValue *array_ptr_val = ir_resolve_const(ira, array_ptr, UndefBad);
12693 UndefAllowed undef_allowed = (array_len == 0) ? UndefOk : UndefBad;
12694 ZigValue *array_ptr_val = ir_resolve_const(ira, array_ptr, undef_allowed);
1266512695 if (array_ptr_val == nullptr)
1266612696 return ira->codegen->invalid_inst_gen;
12667 ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, array_ptr_val, source_instr->source_node);
12668 if (pointee == nullptr)
12669 return ira->codegen->invalid_inst_gen;
12670 if (pointee->special != ConstValSpecialRuntime) {
12671 assert(array_ptr_val->type->id == ZigTypeIdPointer);
12672 ZigType *array_type = array_ptr_val->type->data.pointer.child_type;
12673 assert(is_slice(wanted_type));
12674 bool is_const = wanted_type->data.structure.fields[slice_ptr_index]->type_entry->data.pointer.is_const;
12697 ir_assert(is_slice(wanted_type), source_instr);
12698 if (array_ptr_val->special == ConstValSpecialUndef) {
12699 ZigValue *undef_array = ira->codegen->pass1_arena->create<ZigValue>();
12700 undef_array->special = ConstValSpecialUndef;
12701 undef_array->type = array_type;
1267512702
1267612703 IrInstGen *result = ir_const(ira, source_instr, wanted_type);
12677 init_const_slice(ira->codegen, result->value, pointee, 0, array_type->data.array.len, is_const);
12678 result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut;
12704 init_const_slice(ira->codegen, result->value, undef_array, 0, 0, false);
12705 result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutComptimeConst;
1267912706 result->value->type = wanted_type;
1268012707 return result;
1268112708 }
12709 bool wanted_const = wanted_type->data.structure.fields[slice_ptr_index]->type_entry->data.pointer.is_const;
12710 // Optimization to avoid creating unnecessary ZigValue in const_ptr_pointee
12711 if (array_ptr_val->data.x_ptr.special == ConstPtrSpecialSubArray) {
12712 ZigValue *array_val = array_ptr_val->data.x_ptr.data.base_array.array_val;
12713 if (array_val->special != ConstValSpecialRuntime) {
12714 IrInstGen *result = ir_const(ira, source_instr, wanted_type);
12715 init_const_slice(ira->codegen, result->value, array_val,
12716 array_ptr_val->data.x_ptr.data.base_array.elem_index,
12717 array_type->data.array.len, wanted_const);
12718 result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut;
12719 result->value->type = wanted_type;
12720 return result;
12721 }
12722 } else {
12723 ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, array_ptr_val, source_instr->source_node);
12724 if (pointee == nullptr)
12725 return ira->codegen->invalid_inst_gen;
12726 if (pointee->special != ConstValSpecialRuntime) {
12727 assert(array_ptr_val->type->id == ZigTypeIdPointer);
12728
12729 IrInstGen *result = ir_const(ira, source_instr, wanted_type);
12730 init_const_slice(ira->codegen, result->value, pointee, 0, array_type->data.array.len, wanted_const);
12731 result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut;
12732 result->value->type = wanted_type;
12733 return result;
12734 }
12735 }
1268212736 }
1268312737
1268412738 if (result_loc == nullptr) result_loc = no_result_loc();
......@@ -14548,7 +14602,7 @@ static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr,
1454814602 return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
1454914603 }
1455014604
14551 // *[N]T to ?[]const T
14605 // *[N]T to ?[]T
1455214606 if (wanted_type->id == ZigTypeIdOptional &&
1455314607 is_slice(wanted_type->data.maybe.child_type) &&
1455414608 actual_type->id == ZigTypeIdPointer &&
......@@ -19884,6 +19938,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source
1988419938 buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf), pointee);
1988519939 if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val)))
1988619940 return err;
19941 buf_deinit(&buf);
1988719942 return ErrorNone;
1988819943 }
1988919944
......@@ -19903,6 +19958,31 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source
1990319958 dst_size, buf_ptr(&pointee->type->name), src_size));
1990419959 return ErrorSemanticAnalyzeFail;
1990519960 }
19961 case ConstPtrSpecialSubArray: {
19962 ZigValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val;
19963 assert(array_val->type->id == ZigTypeIdArray);
19964 if (array_val->data.x_array.special != ConstArraySpecialNone)
19965 zig_panic("TODO");
19966 if (dst_size > src_size) {
19967 size_t elem_index = ptr_val->data.x_ptr.data.base_array.elem_index;
19968 opt_ir_add_error_node(ira, codegen, source_node,
19969 buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from %s at index %" ZIG_PRI_usize " which is %" ZIG_PRI_usize " bytes",
19970 dst_size, buf_ptr(&array_val->type->name), elem_index, src_size));
19971 return ErrorSemanticAnalyzeFail;
19972 }
19973 size_t elem_size = src_size;
19974 size_t elem_count = (dst_size % elem_size == 0) ? (dst_size / elem_size) : (dst_size / elem_size + 1);
19975 Buf buf = BUF_INIT;
19976 buf_resize(&buf, elem_count * elem_size);
19977 for (size_t i = 0; i < elem_count; i += 1) {
19978 ZigValue *elem_val = &array_val->data.x_array.data.s_none.elements[i];
19979 buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf) + (i * elem_size), elem_val);
19980 }
19981 if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val)))
19982 return err;
19983 buf_deinit(&buf);
19984 return ErrorNone;
19985 }
1990619986 case ConstPtrSpecialBaseArray: {
1990719987 ZigValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val;
1990819988 assert(array_val->type->id == ZigTypeIdArray);
......@@ -19926,6 +20006,7 @@ static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source
1992620006 }
1992720007 if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val)))
1992820008 return err;
20009 buf_deinit(&buf);
1992920010 return ErrorNone;
1993020011 }
1993120012 case ConstPtrSpecialBaseStruct:
......@@ -20505,6 +20586,44 @@ static ZigType *adjust_ptr_allow_zero(CodeGen *g, ZigType *ptr_type, bool allow_
2050520586 allow_zero);
2050620587}
2050720588
20589static Error compute_elem_align(IrAnalyze *ira, ZigType *elem_type, uint32_t base_ptr_align,
20590 uint64_t elem_index, uint32_t *result)
20591{
20592 Error err;
20593
20594 if (base_ptr_align == 0) {
20595 *result = 0;
20596 return ErrorNone;
20597 }
20598
20599 // figure out the largest alignment possible
20600 if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown)))
20601 return err;
20602
20603 uint64_t elem_size = type_size(ira->codegen, elem_type);
20604 uint64_t abi_align = get_abi_alignment(ira->codegen, elem_type);
20605 uint64_t ptr_align = base_ptr_align;
20606
20607 uint64_t chosen_align = abi_align;
20608 if (ptr_align >= abi_align) {
20609 while (ptr_align > abi_align) {
20610 if ((elem_index * elem_size) % ptr_align == 0) {
20611 chosen_align = ptr_align;
20612 break;
20613 }
20614 ptr_align >>= 1;
20615 }
20616 } else if (elem_size >= ptr_align && elem_size % ptr_align == 0) {
20617 chosen_align = ptr_align;
20618 } else {
20619 // can't get here because guaranteed elem_size >= abi_align
20620 zig_unreachable();
20621 }
20622
20623 *result = chosen_align;
20624 return ErrorNone;
20625}
20626
2050820627static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemPtr *elem_ptr_instruction) {
2050920628 Error err;
2051020629 IrInstGen *array_ptr = elem_ptr_instruction->array_ptr->child;
......@@ -20545,11 +20664,6 @@ static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemP
2054520664 }
2054620665
2054720666 if (array_type->id == ZigTypeIdArray) {
20548 if (array_type->data.array.len == 0) {
20549 ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node,
20550 buf_sprintf("index 0 outside array of size 0"));
20551 return ira->codegen->invalid_inst_gen;
20552 }
2055320667 ZigType *child_type = array_type->data.array.child_type;
2055420668 if (ptr_type->data.pointer.host_int_bytes == 0) {
2055520669 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
2064820762 get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, (uint32_t)index,
2064920763 nullptr, nullptr);
2065020764 } else if (return_type->data.pointer.explicit_alignment != 0) {
20651 // figure out the largest alignment possible
20652
20653 if ((err = type_resolve(ira->codegen, return_type->data.pointer.child_type, ResolveStatusSizeKnown)))
20765 uint32_t chosen_align;
20766 if ((err = compute_elem_align(ira, return_type->data.pointer.child_type,
20767 return_type->data.pointer.explicit_alignment, index, &chosen_align)))
20768 {
2065420769 return ira->codegen->invalid_inst_gen;
20655
20656 uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type);
20657 uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type);
20658 uint64_t ptr_align = get_ptr_align(ira->codegen, return_type);
20659
20660 uint64_t chosen_align = abi_align;
20661 if (ptr_align >= abi_align) {
20662 while (ptr_align > abi_align) {
20663 if ((index * elem_size) % ptr_align == 0) {
20664 chosen_align = ptr_align;
20665 break;
20666 }
20667 ptr_align >>= 1;
20668 }
20669 } else if (elem_size >= ptr_align && elem_size % ptr_align == 0) {
20670 chosen_align = ptr_align;
20671 } else {
20672 // can't get here because guaranteed elem_size >= abi_align
20673 zig_unreachable();
2067420770 }
2067520771 return_type = adjust_ptr_align(ira->codegen, return_type, chosen_align);
2067620772 }
......@@ -20791,6 +20887,7 @@ static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemP
2079120887 }
2079220888 break;
2079320889 case ConstPtrSpecialBaseArray:
20890 case ConstPtrSpecialSubArray:
2079420891 {
2079520892 size_t offset = array_ptr_val->data.x_ptr.data.base_array.elem_index;
2079620893 new_index = offset + index;
......@@ -20861,6 +20958,7 @@ static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemP
2086120958 out_val->data.x_ptr.special = ConstPtrSpecialRef;
2086220959 out_val->data.x_ptr.data.ref.pointee = ptr_field->data.x_ptr.data.ref.pointee;
2086320960 break;
20961 case ConstPtrSpecialSubArray:
2086420962 case ConstPtrSpecialBaseArray:
2086520963 {
2086620964 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
2541225510static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) {
2541325511 Error err;
2541425512
25415 ZigType *ptr_type = get_src_ptr_type(ty);
25513 ZigType *ptr_type;
25514 if (is_slice(ty)) {
25515 TypeStructField *ptr_field = ty->data.structure.fields[slice_ptr_index];
25516 ptr_type = resolve_struct_field_type(ira->codegen, ptr_field);
25517 } else {
25518 ptr_type = get_src_ptr_type(ty);
25519 }
2541625520 assert(ptr_type != nullptr);
2541725521 if (ptr_type->id == ZigTypeIdPointer) {
2541825522 if ((err = type_resolve(ira->codegen, ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
2541925523 return err;
25524 } else if (is_slice(ptr_type)) {
25525 TypeStructField *ptr_field = ptr_type->data.structure.fields[slice_ptr_index];
25526 ZigType *slice_ptr_type = resolve_struct_field_type(ira->codegen, ptr_field);
25527 if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
25528 return err;
2542025529 }
2542125530
2542225531 *result_align = get_ptr_align(ira->codegen, ty);
......@@ -25871,6 +25980,7 @@ static IrInstGen *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstSrcMemset
2587125980 start = 0;
2587225981 bound_end = 1;
2587325982 break;
25983 case ConstPtrSpecialSubArray:
2587425984 case ConstPtrSpecialBaseArray:
2587525985 {
2587625986 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
2600426114 dest_start = 0;
2600526115 dest_end = 1;
2600626116 break;
26117 case ConstPtrSpecialSubArray:
2600726118 case ConstPtrSpecialBaseArray:
2600826119 {
2600926120 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
2604726158 src_start = 0;
2604826159 src_end = 1;
2604926160 break;
26161 case ConstPtrSpecialSubArray:
2605026162 case ConstPtrSpecialBaseArray:
2605126163 {
2605226164 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
2609026202 return ir_build_memcpy_gen(ira, &instruction->base.base, casted_dest_ptr, casted_src_ptr, casted_count);
2609126203}
2609226204
26205static ZigType *get_result_loc_type(IrAnalyze *ira, ResultLoc *result_loc) {
26206 if (result_loc == nullptr) return nullptr;
26207
26208 if (result_loc->id == ResultLocIdCast) {
26209 return ir_resolve_type(ira, result_loc->source_instruction->child);
26210 }
26211
26212 return nullptr;
26213}
26214
2609326215static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *instruction) {
26216 Error err;
26217
2609426218 IrInstGen *ptr_ptr = instruction->ptr->child;
2609526219 if (type_is_invalid(ptr_ptr->value->type))
2609626220 return ira->codegen->invalid_inst_gen;
......@@ -26120,6 +26244,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
2612026244 end = nullptr;
2612126245 }
2612226246
26247 ZigValue *slice_sentinel_val = nullptr;
2612326248 ZigType *non_sentinel_slice_ptr_type;
2612426249 ZigType *elem_type;
2612526250
......@@ -26170,6 +26295,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
2617026295 }
2617126296 } else if (is_slice(array_type)) {
2617226297 ZigType *maybe_sentineled_slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry;
26298 slice_sentinel_val = maybe_sentineled_slice_ptr_type->data.pointer.sentinel;
2617326299 non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr);
2617426300 elem_type = non_sentinel_slice_ptr_type->data.pointer.child_type;
2617526301 } else {
......@@ -26178,7 +26304,6 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
2617826304 return ira->codegen->invalid_inst_gen;
2617926305 }
2618026306
26181 ZigType *return_type;
2618226307 ZigValue *sentinel_val = nullptr;
2618326308 if (instruction->sentinel) {
2618426309 IrInstGen *uncasted_sentinel = instruction->sentinel->child;
......@@ -26190,11 +26315,76 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
2619026315 sentinel_val = ir_resolve_const(ira, sentinel, UndefBad);
2619126316 if (sentinel_val == nullptr)
2619226317 return ira->codegen->invalid_inst_gen;
26193 ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, sentinel_val);
26318 }
26319
26320 ZigType *child_array_type = (array_type->id == ZigTypeIdPointer &&
26321 array_type->data.pointer.ptr_len == PtrLenSingle) ? array_type->data.pointer.child_type : array_type;
26322
26323 ZigType *return_type;
26324
26325 // If start index and end index are both comptime known, then the result type is a pointer to array
26326 // not a slice. However, if the start or end index is a lazy value, and the result location is a slice,
26327 // then the pointer-to-array would be casted to a slice anyway. So, we preserve the laziness of these
26328 // values by making the return type a slice.
26329 ZigType *res_loc_type = get_result_loc_type(ira, instruction->result_loc);
26330 bool result_loc_is_slice = (res_loc_type != nullptr && is_slice(res_loc_type));
26331 bool end_is_known = !result_loc_is_slice &&
26332 ((end != nullptr && value_is_comptime(end->value)) ||
26333 (end == nullptr && child_array_type->id == ZigTypeIdArray));
26334
26335 ZigValue *array_sentinel = sentinel_val;
26336 if (end_is_known) {
26337 uint64_t end_scalar;
26338 if (end != nullptr) {
26339 ZigValue *end_val = ir_resolve_const(ira, end, UndefBad);
26340 if (!end_val)
26341 return ira->codegen->invalid_inst_gen;
26342 end_scalar = bigint_as_u64(&end_val->data.x_bigint);
26343 } else {
26344 end_scalar = child_array_type->data.array.len;
26345 }
26346 array_sentinel = (child_array_type->id == ZigTypeIdArray && end_scalar == child_array_type->data.array.len)
26347 ? child_array_type->data.array.sentinel : sentinel_val;
26348
26349 if (value_is_comptime(casted_start->value)) {
26350 ZigValue *start_val = ir_resolve_const(ira, casted_start, UndefBad);
26351 if (!start_val)
26352 return ira->codegen->invalid_inst_gen;
26353
26354 uint64_t start_scalar = bigint_as_u64(&start_val->data.x_bigint);
26355
26356 if (start_scalar > end_scalar) {
26357 ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds slice"));
26358 return ira->codegen->invalid_inst_gen;
26359 }
26360
26361 uint32_t base_ptr_align = non_sentinel_slice_ptr_type->data.pointer.explicit_alignment;
26362 uint32_t ptr_byte_alignment = 0;
26363 if (end_scalar > start_scalar) {
26364 if ((err = compute_elem_align(ira, elem_type, base_ptr_align, start_scalar, &ptr_byte_alignment)))
26365 return ira->codegen->invalid_inst_gen;
26366 }
26367
26368 ZigType *return_array_type = get_array_type(ira->codegen, elem_type, end_scalar - start_scalar,
26369 array_sentinel);
26370 return_type = get_pointer_to_type_extra(ira->codegen, return_array_type,
26371 non_sentinel_slice_ptr_type->data.pointer.is_const,
26372 non_sentinel_slice_ptr_type->data.pointer.is_volatile,
26373 PtrLenSingle, ptr_byte_alignment, 0, 0, false);
26374 goto done_with_return_type;
26375 }
26376 } else if (array_sentinel == nullptr && end == nullptr) {
26377 array_sentinel = slice_sentinel_val;
26378 }
26379 if (array_sentinel != nullptr) {
26380 // TODO deal with non-abi-alignment here
26381 ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, array_sentinel);
2619426382 return_type = get_slice_type(ira->codegen, slice_ptr_type);
2619526383 } else {
26384 // TODO deal with non-abi-alignment here
2619626385 return_type = get_slice_type(ira->codegen, non_sentinel_slice_ptr_type);
2619726386 }
26387done_with_return_type:
2619826388
2619926389 if (instr_is_comptime(ptr_ptr) &&
2620026390 value_is_comptime(casted_start->value) &&
......@@ -26205,12 +26395,8 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
2620526395 size_t abs_offset;
2620626396 size_t rel_end;
2620726397 bool ptr_is_undef = false;
26208 if (array_type->id == ZigTypeIdArray ||
26209 (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle))
26210 {
26398 if (child_array_type->id == ZigTypeIdArray) {
2621126399 if (array_type->id == ZigTypeIdPointer) {
26212 ZigType *child_array_type = array_type->data.pointer.child_type;
26213 assert(child_array_type->id == ZigTypeIdArray);
2621426400 parent_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node);
2621526401 if (parent_ptr == nullptr)
2621626402 return ira->codegen->invalid_inst_gen;
......@@ -26221,6 +26407,10 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
2622126407 abs_offset = 0;
2622226408 rel_end = SIZE_MAX;
2622326409 ptr_is_undef = true;
26410 } else if (parent_ptr->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
26411 array_val = nullptr;
26412 abs_offset = 0;
26413 rel_end = SIZE_MAX;
2622426414 } else {
2622526415 array_val = const_ptr_pointee(ira, ira->codegen, parent_ptr, instruction->base.base.source_node);
2622626416 if (array_val == nullptr)
......@@ -26263,6 +26453,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
2626326453 rel_end = 1;
2626426454 }
2626526455 break;
26456 case ConstPtrSpecialSubArray:
2626626457 case ConstPtrSpecialBaseArray:
2626726458 array_val = parent_ptr->data.x_ptr.data.base_array.array_val;
2626826459 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
2631326504 abs_offset = SIZE_MAX;
2631426505 rel_end = 1;
2631526506 break;
26507 case ConstPtrSpecialSubArray:
2631626508 case ConstPtrSpecialBaseArray:
2631726509 array_val = parent_ptr->data.x_ptr.data.base_array.array_val;
2631826510 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
2637326565 }
2637426566
2637526567 IrInstGen *result = ir_const(ira, &instruction->base.base, return_type);
26376 ZigValue *out_val = result->value;
26377 out_val->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, 2);
2637826568
26379 ZigValue *ptr_val = out_val->data.x_struct.fields[slice_ptr_index];
26569 ZigValue *ptr_val;
26570 if (return_type->id == ZigTypeIdPointer) {
26571 // pointer to array
26572 ptr_val = result->value;
26573 } else {
26574 // slice
26575 result->value->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, 2);
26576
26577 ptr_val = result->value->data.x_struct.fields[slice_ptr_index];
2638026578
26579 ZigValue *len_val = result->value->data.x_struct.fields[slice_len_index];
26580 init_const_usize(ira->codegen, len_val, end_scalar - start_scalar);
26581 }
26582
26583 bool return_type_is_const = non_sentinel_slice_ptr_type->data.pointer.is_const;
2638126584 if (array_val) {
2638226585 size_t index = abs_offset + start_scalar;
26383 bool is_const = slice_is_const(return_type);
26384 init_const_ptr_array(ira->codegen, ptr_val, array_val, index, is_const, PtrLenUnknown);
26586 init_const_ptr_array(ira->codegen, ptr_val, array_val, index, return_type_is_const, PtrLenUnknown);
26587 if (return_type->id == ZigTypeIdPointer) {
26588 ptr_val->data.x_ptr.special = ConstPtrSpecialSubArray;
26589 }
2638526590 if (array_type->id == ZigTypeIdArray) {
2638626591 ptr_val->data.x_ptr.mut = ptr_ptr->value->data.x_ptr.mut;
2638726592 } else if (is_slice(array_type)) {
......@@ -26391,16 +26596,17 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
2639126596 }
2639226597 } else if (ptr_is_undef) {
2639326598 ptr_val->type = get_pointer_to_type(ira->codegen, parent_ptr->type->data.pointer.child_type,
26394 slice_is_const(return_type));
26599 return_type_is_const);
2639526600 ptr_val->special = ConstValSpecialUndef;
2639626601 } else switch (parent_ptr->data.x_ptr.special) {
2639726602 case ConstPtrSpecialInvalid:
2639826603 case ConstPtrSpecialDiscard:
2639926604 zig_unreachable();
2640026605 case ConstPtrSpecialRef:
26401 init_const_ptr_ref(ira->codegen, ptr_val,
26402 parent_ptr->data.x_ptr.data.ref.pointee, slice_is_const(return_type));
26606 init_const_ptr_ref(ira->codegen, ptr_val, parent_ptr->data.x_ptr.data.ref.pointee,
26607 return_type_is_const);
2640326608 break;
26609 case ConstPtrSpecialSubArray:
2640426610 case ConstPtrSpecialBaseArray:
2640526611 zig_unreachable();
2640626612 case ConstPtrSpecialBaseStruct:
......@@ -26415,7 +26621,7 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
2641526621 init_const_ptr_hard_coded_addr(ira->codegen, ptr_val,
2641626622 parent_ptr->type->data.pointer.child_type,
2641726623 parent_ptr->data.x_ptr.data.hard_coded_addr.addr + start_scalar,
26418 slice_is_const(return_type));
26624 return_type_is_const);
2641926625 break;
2642026626 case ConstPtrSpecialFunction:
2642126627 zig_panic("TODO");
......@@ -26423,26 +26629,11 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
2642326629 zig_panic("TODO");
2642426630 }
2642526631
26426 ZigValue *len_val = out_val->data.x_struct.fields[slice_len_index];
26427 init_const_usize(ira->codegen, len_val, end_scalar - start_scalar);
26428
26632 // In the case of pointer-to-array, we must restore this because above it overwrites ptr_val->type
26633 result->value->type = return_type;
2642926634 return result;
2643026635 }
2643126636
26432 IrInstGen *result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc,
26433 return_type, nullptr, true, true);
26434 if (result_loc != nullptr) {
26435 if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) {
26436 return result_loc;
26437 }
26438 IrInstGen *dummy_value = ir_const(ira, &instruction->base.base, return_type);
26439 dummy_value->value->special = ConstValSpecialRuntime;
26440 IrInstGen *dummy_result = ir_implicit_cast2(ira, &instruction->base.base,
26441 dummy_value, result_loc->value->type->data.pointer.child_type);
26442 if (type_is_invalid(dummy_result->value->type))
26443 return ira->codegen->invalid_inst_gen;
26444 }
26445
2644626637 if (generate_non_null_assert) {
2644726638 IrInstGen *ptr_val = ir_get_deref(ira, &instruction->base.base, ptr_ptr, nullptr);
2644826639
......@@ -26452,8 +26643,26 @@ static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *i
2645226643 ir_build_assert_non_null(ira, &instruction->base.base, ptr_val);
2645326644 }
2645426645
26646 IrInstGen *result_loc = nullptr;
26647
26648 if (return_type->id != ZigTypeIdPointer) {
26649 result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc,
26650 return_type, nullptr, true, true);
26651 if (result_loc != nullptr) {
26652 if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) {
26653 return result_loc;
26654 }
26655 IrInstGen *dummy_value = ir_const(ira, &instruction->base.base, return_type);
26656 dummy_value->value->special = ConstValSpecialRuntime;
26657 IrInstGen *dummy_result = ir_implicit_cast2(ira, &instruction->base.base,
26658 dummy_value, result_loc->value->type->data.pointer.child_type);
26659 if (type_is_invalid(dummy_result->value->type))
26660 return ira->codegen->invalid_inst_gen;
26661 }
26662 }
26663
2645526664 return ir_build_slice_gen(ira, &instruction->base.base, return_type, ptr_ptr,
26456 casted_start, end, instruction->safety_check_on, result_loc);
26665 casted_start, end, instruction->safety_check_on, result_loc, sentinel_val);
2645726666}
2645826667
2645926668static 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
2747927688 // We have a check for zero bits later so we use get_src_ptr_type to
2748027689 // validate src_type and dest_type.
2748127690
27482 ZigType *src_ptr_type = get_src_ptr_type(src_type);
27483 if (src_ptr_type == nullptr) {
27484 ir_add_error(ira, ptr_src, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name)));
27485 return ira->codegen->invalid_inst_gen;
27691 ZigType *if_slice_ptr_type;
27692 if (is_slice(src_type)) {
27693 TypeStructField *ptr_field = src_type->data.structure.fields[slice_ptr_index];
27694 if_slice_ptr_type = resolve_struct_field_type(ira->codegen, ptr_field);
27695 } else {
27696 if_slice_ptr_type = src_type;
27697
27698 ZigType *src_ptr_type = get_src_ptr_type(src_type);
27699 if (src_ptr_type == nullptr) {
27700 ir_add_error(ira, ptr_src, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name)));
27701 return ira->codegen->invalid_inst_gen;
27702 }
2748627703 }
2748727704
2748827705 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
2749227709 return ira->codegen->invalid_inst_gen;
2749327710 }
2749427711
27495 if (get_ptr_const(src_type) && !get_ptr_const(dest_type)) {
27712 if (get_ptr_const(ira->codegen, src_type) && !get_ptr_const(ira->codegen, dest_type)) {
2749627713 ir_add_error(ira, source_instr, buf_sprintf("cast discards const qualifier"));
2749727714 return ira->codegen->invalid_inst_gen;
2749827715 }
......@@ -27510,7 +27727,10 @@ static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrIn
2751027727 if ((err = type_resolve(ira->codegen, src_type, ResolveStatusZeroBitsKnown)))
2751127728 return ira->codegen->invalid_inst_gen;
2751227729
27513 if (type_has_bits(ira->codegen, dest_type) && !type_has_bits(ira->codegen, src_type) && safety_check_on) {
27730 if (safety_check_on &&
27731 type_has_bits(ira->codegen, dest_type) &&
27732 !type_has_bits(ira->codegen, if_slice_ptr_type))
27733 {
2751427734 ErrorMsg *msg = ir_add_error(ira, source_instr,
2751527735 buf_sprintf("'%s' and '%s' do not have the same in-memory representation",
2751627736 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
2752127741 return ira->codegen->invalid_inst_gen;
2752227742 }
2752327743
27744 // For slices, follow the `ptr` field.
27745 if (is_slice(src_type)) {
27746 TypeStructField *ptr_field = src_type->data.structure.fields[slice_ptr_index];
27747 IrInstGen *ptr_ref = ir_get_ref(ira, source_instr, ptr, true, false);
27748 IrInstGen *ptr_ptr = ir_analyze_struct_field_ptr(ira, source_instr, ptr_field, ptr_ref, src_type, false);
27749 ptr = ir_get_deref(ira, source_instr, ptr_ptr, nullptr);
27750 }
27751
2752427752 if (instr_is_comptime(ptr)) {
2752527753 bool dest_allows_addr_zero = ptr_allows_addr_zero(dest_type);
2752627754 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
2762427852 buf_write_value_bytes(codegen, &buf[buf_i], elem);
2762527853 buf_i += type_size(codegen, elem->type);
2762627854 }
27855 if (val->type->id == ZigTypeIdArray && val->type->data.array.sentinel != nullptr) {
27856 buf_write_value_bytes(codegen, &buf[buf_i], val->type->data.array.sentinel);
27857 }
2762727858}
2762827859
2762927860static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ZigValue *val) {
src/link.cpp+1
......@@ -566,6 +566,7 @@ static const char *build_libc_object(CodeGen *parent_gen, const char *name, CFil
566566 Stage2ProgressNode *progress_node)
567567{
568568 CodeGen *child_gen = create_child_codegen(parent_gen, nullptr, OutTypeObj, nullptr, name, progress_node);
569 child_gen->root_out_name = buf_create_from_str(name);
569570 ZigList<CFile *> c_source_files = {0};
570571 c_source_files.append(c_file);
571572 child_gen->c_source_files = c_source_files;
src/main.cpp+2-1
......@@ -1291,6 +1291,7 @@ static int main0(int argc, char **argv) {
12911291 if (g->enable_cache) {
12921292#if defined(ZIG_OS_WINDOWS)
12931293 buf_replace(&g->bin_file_output_path, '/', '\\');
1294 buf_replace(g->output_dir, '/', '\\');
12941295#endif
12951296 if (final_output_dir_step != nullptr) {
12961297 Buf *dest_basename = buf_alloc();
......@@ -1304,7 +1305,7 @@ static int main0(int argc, char **argv) {
13041305 return main_exit(root_progress_node, EXIT_FAILURE);
13051306 }
13061307 } else {
1307 if (printf("%s\n", buf_ptr(&g->bin_file_output_path)) < 0)
1308 if (printf("%s\n", buf_ptr(g->output_dir)) < 0)
13081309 return main_exit(root_progress_node, EXIT_FAILURE);
13091310 }
13101311 }
test/cli.zig+1-1
......@@ -36,7 +36,7 @@ pub fn main() !void {
3636 testMissingOutputPath,
3737 };
3838 for (test_fns) |testFn| {
39 try fs.deleteTree(dir_path);
39 try fs.cwd().deleteTree(dir_path);
4040 try fs.cwd().makeDir(dir_path);
4141 try testFn(zig_exe, dir_path);
4242 }
test/compare_output.zig+1-1
......@@ -292,7 +292,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
292292 \\pub export fn main() c_int {
293293 \\ var array = [_]u32{ 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 };
294294 \\
295 \\ c.qsort(@ptrCast(?*c_void, array[0..].ptr), @intCast(c_ulong, array.len), @sizeOf(i32), compare_fn);
295 \\ c.qsort(@ptrCast(?*c_void, &array), @intCast(c_ulong, array.len), @sizeOf(i32), compare_fn);
296296 \\
297297 \\ for (array) |item, i| {
298298 \\ if (item != i) {
test/compile_errors.zig+2-14
......@@ -103,18 +103,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
103103 "tmp.zig:3:23: error: pointer to size 0 type has no address",
104104 });
105105
106 cases.addTest("slice to pointer conversion mismatch",
107 \\pub fn bytesAsSlice(bytes: var) [*]align(1) const u16 {
108 \\ return @ptrCast([*]align(1) const u16, bytes.ptr)[0..1];
109 \\}
110 \\test "bytesAsSlice" {
111 \\ const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
112 \\ const slice = bytesAsSlice(bytes[0..]);
113 \\}
114 , &[_][]const u8{
115 "tmp.zig:2:54: error: expected type '[*]align(1) const u16', found '[]align(1) const u16'",
116 });
117
118106 cases.addTest("access invalid @typeInfo decl",
119107 \\const A = B;
120108 \\test "Crash" {
......@@ -1918,8 +1906,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
19181906 cases.add("reading past end of pointer casted array",
19191907 \\comptime {
19201908 \\ const array: [4]u8 = "aoeu".*;
1921 \\ const slice = array[1..];
1922 \\ const int_ptr = @ptrCast(*const u24, slice.ptr);
1909 \\ const sub_array = array[1..];
1910 \\ const int_ptr = @ptrCast(*const u24, sub_array);
19231911 \\ const deref = int_ptr.*;
19241912 \\}
19251913 , &[_][]const u8{
test/runtime_safety.zig+1-1
......@@ -69,7 +69,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
6969 \\}
7070 \\pub fn main() void {
7171 \\ var buf: [4]u8 = undefined;
72 \\ const ptr = buf[0..].ptr;
72 \\ const ptr: [*]u8 = &buf;
7373 \\ const slice = ptr[0..3 :0];
7474 \\}
7575 );
test/stage1/behavior/align.zig+22-14
......@@ -5,10 +5,17 @@ const builtin = @import("builtin");
55var foo: u8 align(4) = 100;
66
77test "global variable alignment" {
8 expect(@TypeOf(&foo).alignment == 4);
9 expect(@TypeOf(&foo) == *align(4) u8);
10 const slice = @as(*[1]u8, &foo)[0..];
11 expect(@TypeOf(slice) == []align(4) u8);
8 comptime expect(@TypeOf(&foo).alignment == 4);
9 comptime expect(@TypeOf(&foo) == *align(4) u8);
10 {
11 const slice = @as(*[1]u8, &foo)[0..];
12 comptime expect(@TypeOf(slice) == *align(4) [1]u8);
13 }
14 {
15 var runtime_zero: usize = 0;
16 const slice = @as(*[1]u8, &foo)[runtime_zero..];
17 comptime expect(@TypeOf(slice) == []align(4) u8);
18 }
1219}
1320
1421fn derp() align(@sizeOf(usize) * 2) i32 {
......@@ -171,18 +178,19 @@ test "runtime known array index has best alignment possible" {
171178
172179 // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2
173180 var smaller align(2) = [_]u32{ 1, 2, 3, 4 };
174 comptime expect(@TypeOf(smaller[0..]) == []align(2) u32);
175 comptime expect(@TypeOf(smaller[0..].ptr) == [*]align(2) u32);
176 testIndex(smaller[0..].ptr, 0, *align(2) u32);
177 testIndex(smaller[0..].ptr, 1, *align(2) u32);
178 testIndex(smaller[0..].ptr, 2, *align(2) u32);
179 testIndex(smaller[0..].ptr, 3, *align(2) u32);
181 var runtime_zero: usize = 0;
182 comptime expect(@TypeOf(smaller[runtime_zero..]) == []align(2) u32);
183 comptime expect(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32);
184 testIndex(smaller[runtime_zero..].ptr, 0, *align(2) u32);
185 testIndex(smaller[runtime_zero..].ptr, 1, *align(2) u32);
186 testIndex(smaller[runtime_zero..].ptr, 2, *align(2) u32);
187 testIndex(smaller[runtime_zero..].ptr, 3, *align(2) u32);
180188
181189 // has to use ABI alignment because index known at runtime only
182 testIndex2(array[0..].ptr, 0, *u8);
183 testIndex2(array[0..].ptr, 1, *u8);
184 testIndex2(array[0..].ptr, 2, *u8);
185 testIndex2(array[0..].ptr, 3, *u8);
190 testIndex2(array[runtime_zero..].ptr, 0, *u8);
191 testIndex2(array[runtime_zero..].ptr, 1, *u8);
192 testIndex2(array[runtime_zero..].ptr, 2, *u8);
193 testIndex2(array[runtime_zero..].ptr, 3, *u8);
186194}
187195fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) void {
188196 comptime expect(@TypeOf(&smaller[index]) == T);
test/stage1/behavior/array.zig+38
......@@ -28,6 +28,24 @@ fn getArrayLen(a: []const u32) usize {
2828 return a.len;
2929}
3030
31test "array with sentinels" {
32 const S = struct {
33 fn doTheTest(is_ct: bool) void {
34 var zero_sized: [0:0xde]u8 = [_:0xde]u8{};
35 expectEqual(@as(u8, 0xde), zero_sized[0]);
36 // Disabled at runtime because of
37 // https://github.com/ziglang/zig/issues/4372
38 if (is_ct) {
39 var reinterpreted = @ptrCast(*[1]u8, &zero_sized);
40 expectEqual(@as(u8, 0xde), reinterpreted[0]);
41 }
42 }
43 };
44
45 S.doTheTest(false);
46 comptime S.doTheTest(true);
47}
48
3149test "void arrays" {
3250 var array: [4]void = undefined;
3351 array[0] = void{};
......@@ -376,3 +394,23 @@ test "type deduction for array subscript expression" {
376394 S.doTheTest();
377395 comptime S.doTheTest();
378396}
397
398test "sentinel element count towards the ABI size calculation" {
399 const S = struct {
400 fn doTheTest() void {
401 const T = packed struct {
402 fill_pre: u8 = 0x55,
403 data: [0:0]u8 = undefined,
404 fill_post: u8 = 0xAA,
405 };
406 var x = T{};
407 var as_slice = mem.asBytes(&x);
408 expectEqual(@as(usize, 3), as_slice.len);
409 expectEqual(@as(u8, 0x55), as_slice[0]);
410 expectEqual(@as(u8, 0xAA), as_slice[2]);
411 }
412 };
413
414 S.doTheTest();
415 comptime S.doTheTest();
416}
test/stage1/behavior/cast.zig+2-1
......@@ -431,7 +431,8 @@ fn incrementVoidPtrValue(value: ?*c_void) void {
431431
432432test "implicit cast from [*]T to ?*c_void" {
433433 var a = [_]u8{ 3, 2, 1 };
434 incrementVoidPtrArray(a[0..].ptr, 3);
434 var runtime_zero: usize = 0;
435 incrementVoidPtrArray(a[runtime_zero..].ptr, 3);
435436 expect(std.mem.eql(u8, &a, &[_]u8{ 4, 3, 2 }));
436437}
437438
test/stage1/behavior/eval.zig+1-1
......@@ -524,7 +524,7 @@ test "comptime slice of slice preserves comptime var" {
524524test "comptime slice of pointer preserves comptime var" {
525525 comptime {
526526 var buff: [10]u8 = undefined;
527 var a = buff[0..].ptr;
527 var a = @ptrCast([*]u8, &buff);
528528 a[0..1][0] = 1;
529529 expect(buff[0..][0..][0] == 1);
530530 }
test/stage1/behavior/misc.zig+9-5
......@@ -102,8 +102,8 @@ test "memcpy and memset intrinsics" {
102102 var foo: [20]u8 = undefined;
103103 var bar: [20]u8 = undefined;
104104
105 @memset(foo[0..].ptr, 'A', foo.len);
106 @memcpy(bar[0..].ptr, foo[0..].ptr, bar.len);
105 @memset(&foo, 'A', foo.len);
106 @memcpy(&bar, &foo, bar.len);
107107
108108 if (bar[11] != 'A') unreachable;
109109}
......@@ -565,12 +565,16 @@ test "volatile load and store" {
565565 expect(ptr.* == 1235);
566566}
567567
568test "slice string literal has type []const u8" {
568test "slice string literal has correct type" {
569569 comptime {
570 expect(@TypeOf("aoeu"[0..]) == []const u8);
570 expect(@TypeOf("aoeu"[0..]) == *const [4:0]u8);
571571 const array = [_]i32{ 1, 2, 3, 4 };
572 expect(@TypeOf(array[0..]) == []const i32);
572 expect(@TypeOf(array[0..]) == *const [4]i32);
573573 }
574 var runtime_zero: usize = 0;
575 comptime expect(@TypeOf("aoeu"[runtime_zero..]) == [:0]const u8);
576 const array = [_]i32{ 1, 2, 3, 4 };
577 comptime expect(@TypeOf(array[runtime_zero..]) == []const i32);
574578}
575579
576580test "pointer child field" {
test/stage1/behavior/pointers.zig+5-4
......@@ -159,12 +159,13 @@ test "allowzero pointer and slice" {
159159 var opt_ptr: ?[*]allowzero i32 = ptr;
160160 expect(opt_ptr != null);
161161 expect(@ptrToInt(ptr) == 0);
162 var slice = ptr[0..10];
163 expect(@TypeOf(slice) == []allowzero i32);
162 var runtime_zero: usize = 0;
163 var slice = ptr[runtime_zero..10];
164 comptime expect(@TypeOf(slice) == []allowzero i32);
164165 expect(@ptrToInt(&slice[5]) == 20);
165166
166 expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero);
167 expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero);
167 comptime expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero);
168 comptime expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero);
168169}
169170
170171test "assign null directly to C pointer and test null equality" {
test/stage1/behavior/ptrcast.zig+1-1
......@@ -13,7 +13,7 @@ fn testReinterpretBytesAsInteger() void {
1313 builtin.Endian.Little => 0xab785634,
1414 builtin.Endian.Big => 0x345678ab,
1515 };
16 expect(@ptrCast(*align(1) const u32, bytes[1..5].ptr).* == expected);
16 expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected);
1717}
1818
1919test "reinterpret bytes of an array into an extern struct" {
test/stage1/behavior/slice.zig+155-4
......@@ -7,10 +7,10 @@ const mem = std.mem;
77const x = @intToPtr([*]i32, 0x1000)[0..0x500];
88const y = x[0x100..];
99test "compile time slice of pointer to hard coded address" {
10 expect(@ptrToInt(x.ptr) == 0x1000);
10 expect(@ptrToInt(x) == 0x1000);
1111 expect(x.len == 0x500);
1212
13 expect(@ptrToInt(y.ptr) == 0x1100);
13 expect(@ptrToInt(y) == 0x1100);
1414 expect(y.len == 0x400);
1515}
1616
......@@ -47,7 +47,9 @@ test "C pointer slice access" {
4747 var buf: [10]u32 = [1]u32{42} ** 10;
4848 const c_ptr = @ptrCast([*c]const u32, &buf);
4949
50 comptime expectEqual([]const u32, @TypeOf(c_ptr[0..1]));
50 var runtime_zero: usize = 0;
51 comptime expectEqual([]const u32, @TypeOf(c_ptr[runtime_zero..1]));
52 comptime expectEqual(*const [1]u32, @TypeOf(c_ptr[0..1]));
5153
5254 for (c_ptr[0..5]) |*cl| {
5355 expectEqual(@as(u32, 42), cl.*);
......@@ -107,7 +109,9 @@ test "obtaining a null terminated slice" {
107109 const ptr2 = buf[0..runtime_len :0];
108110 // ptr2 is a null-terminated slice
109111 comptime expect(@TypeOf(ptr2) == [:0]u8);
110 comptime expect(@TypeOf(ptr2[0..2]) == []u8);
112 comptime expect(@TypeOf(ptr2[0..2]) == *[2]u8);
113 var runtime_zero: usize = 0;
114 comptime expect(@TypeOf(ptr2[runtime_zero..2]) == []u8);
111115}
112116
113117test "empty array to slice" {
......@@ -126,3 +130,150 @@ test "empty array to slice" {
126130 S.doTheTest();
127131 comptime S.doTheTest();
128132}
133
134test "@ptrCast slice to pointer" {
135 const S = struct {
136 fn doTheTest() void {
137 var array align(@alignOf(u16)) = [5]u8{ 0xff, 0xff, 0xff, 0xff, 0xff };
138 var slice: []u8 = &array;
139 var ptr = @ptrCast(*u16, slice);
140 expect(ptr.* == 65535);
141 }
142 };
143
144 S.doTheTest();
145 comptime S.doTheTest();
146}
147
148test "slice syntax resulting in pointer-to-array" {
149 const S = struct {
150 fn doTheTest() void {
151 testArray();
152 testArrayZ();
153 testArray0();
154 testArrayAlign();
155 testPointer();
156 testPointerZ();
157 testPointer0();
158 testPointerAlign();
159 testSlice();
160 testSliceZ();
161 testSlice0();
162 testSliceAlign();
163 }
164
165 fn testArray() void {
166 var array = [5]u8{ 1, 2, 3, 4, 5 };
167 var slice = array[1..3];
168 comptime expect(@TypeOf(slice) == *[2]u8);
169 expect(slice[0] == 2);
170 expect(slice[1] == 3);
171 }
172
173 fn testArrayZ() void {
174 var array = [5:0]u8{ 1, 2, 3, 4, 5 };
175 comptime expect(@TypeOf(array[1..3]) == *[2]u8);
176 comptime expect(@TypeOf(array[1..5]) == *[4:0]u8);
177 comptime expect(@TypeOf(array[1..]) == *[4:0]u8);
178 comptime expect(@TypeOf(array[1..3 :4]) == *[2:4]u8);
179 }
180
181 fn testArray0() void {
182 {
183 var array = [0]u8{};
184 var slice = array[0..0];
185 comptime expect(@TypeOf(slice) == *[0]u8);
186 }
187 {
188 var array = [0:0]u8{};
189 var slice = array[0..0];
190 comptime expect(@TypeOf(slice) == *[0:0]u8);
191 expect(slice[0] == 0);
192 }
193 }
194
195 fn testArrayAlign() void {
196 var array align(4) = [5]u8{ 1, 2, 3, 4, 5 };
197 var slice = array[4..5];
198 comptime expect(@TypeOf(slice) == *align(4) [1]u8);
199 expect(slice[0] == 5);
200 comptime expect(@TypeOf(array[0..2]) == *align(4) [2]u8);
201 }
202
203 fn testPointer() void {
204 var array = [5]u8{ 1, 2, 3, 4, 5 };
205 var pointer: [*]u8 = &array;
206 var slice = pointer[1..3];
207 comptime expect(@TypeOf(slice) == *[2]u8);
208 expect(slice[0] == 2);
209 expect(slice[1] == 3);
210 }
211
212 fn testPointerZ() void {
213 var array = [5:0]u8{ 1, 2, 3, 4, 5 };
214 var pointer: [*:0]u8 = &array;
215 comptime expect(@TypeOf(pointer[1..3]) == *[2]u8);
216 comptime expect(@TypeOf(pointer[1..3 :4]) == *[2:4]u8);
217 }
218
219 fn testPointer0() void {
220 var pointer: [*]u0 = &[1]u0{0};
221 var slice = pointer[0..1];
222 comptime expect(@TypeOf(slice) == *[1]u0);
223 expect(slice[0] == 0);
224 }
225
226 fn testPointerAlign() void {
227 var array align(4) = [5]u8{ 1, 2, 3, 4, 5 };
228 var pointer: [*]align(4) u8 = &array;
229 var slice = pointer[4..5];
230 comptime expect(@TypeOf(slice) == *align(4) [1]u8);
231 expect(slice[0] == 5);
232 comptime expect(@TypeOf(pointer[0..2]) == *align(4) [2]u8);
233 }
234
235 fn testSlice() void {
236 var array = [5]u8{ 1, 2, 3, 4, 5 };
237 var src_slice: []u8 = &array;
238 var slice = src_slice[1..3];
239 comptime expect(@TypeOf(slice) == *[2]u8);
240 expect(slice[0] == 2);
241 expect(slice[1] == 3);
242 }
243
244 fn testSliceZ() void {
245 var array = [5:0]u8{ 1, 2, 3, 4, 5 };
246 var slice: [:0]u8 = &array;
247 comptime expect(@TypeOf(slice[1..3]) == *[2]u8);
248 comptime expect(@TypeOf(slice[1..]) == [:0]u8);
249 comptime expect(@TypeOf(slice[1..3 :4]) == *[2:4]u8);
250 }
251
252 fn testSlice0() void {
253 {
254 var array = [0]u8{};
255 var src_slice: []u8 = &array;
256 var slice = src_slice[0..0];
257 comptime expect(@TypeOf(slice) == *[0]u8);
258 }
259 {
260 var array = [0:0]u8{};
261 var src_slice: [:0]u8 = &array;
262 var slice = src_slice[0..0];
263 comptime expect(@TypeOf(slice) == *[0]u8);
264 }
265 }
266
267 fn testSliceAlign() void {
268 var array align(4) = [5]u8{ 1, 2, 3, 4, 5 };
269 var src_slice: []align(4) u8 = &array;
270 var slice = src_slice[4..5];
271 comptime expect(@TypeOf(slice) == *align(4) [1]u8);
272 expect(slice[0] == 5);
273 comptime expect(@TypeOf(src_slice[0..2]) == *align(4) [2]u8);
274 }
275 };
276
277 S.doTheTest();
278 comptime S.doTheTest();
279}
test/stage1/behavior/struct.zig+2-2
......@@ -409,8 +409,8 @@ const Bitfields = packed struct {
409409test "native bit field understands endianness" {
410410 var all: u64 = 0x7765443322221111;
411411 var bytes: [8]u8 = undefined;
412 @memcpy(bytes[0..].ptr, @ptrCast([*]u8, &all), 8);
413 var bitfields = @ptrCast(*Bitfields, bytes[0..].ptr).*;
412 @memcpy(&bytes, @ptrCast([*]u8, &all), 8);
413 var bitfields = @ptrCast(*Bitfields, &bytes).*;
414414
415415 expect(bitfields.f1 == 0x1111);
416416 expect(bitfields.f2 == 0x2222);