authorgravatar for xq@random-projects.netFelix "xq" Queißner <xq@random-projects.net> 2019-10-22 20:29:25+02:00
committergravatar for xq@random-projects.netFelix "xq" Queißner <xq@random-projects.net> 2019-10-22 20:29:25+02:00
log03f1ad5007fd747bf386058222f9dfb9a925ef02
tree15add24f509ca3c5b2efc08d62419858f2d79c4c
parent5456eb11078a630afc21d52ebb515ac753764a84
parente839250c5156d438f76e7b08e7053e9087fae77c

Merge branch 'master' of https://github.com/ziglang/zig into markdown-renderer


53 files changed, 1983 insertions(+), 822 deletions(-)

CMakeLists.txt+5
...@@ -46,6 +46,7 @@ message("Configuring zig version ${ZIG_VERSION}")...@@ -46,6 +46,7 @@ message("Configuring zig version ${ZIG_VERSION}")
46set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not compatible with glibc)")46set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not compatible with glibc)")
47set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries")47set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries")
48set(ZIG_SKIP_INSTALL_LIB_FILES off CACHE BOOL "Disable copying lib/ files to install prefix")48set(ZIG_SKIP_INSTALL_LIB_FILES off CACHE BOOL "Disable copying lib/ files to install prefix")
49set(ZIG_ENABLE_MEM_PROFILE off CACHE BOOL "Activate memory usage instrumentation")
4950
50if(ZIG_STATIC)51if(ZIG_STATIC)
51 set(ZIG_STATIC_LLVM "on")52 set(ZIG_STATIC_LLVM "on")
...@@ -455,6 +456,7 @@ set(ZIG_SOURCES...@@ -455,6 +456,7 @@ set(ZIG_SOURCES
455 "${CMAKE_SOURCE_DIR}/src/ir_print.cpp"456 "${CMAKE_SOURCE_DIR}/src/ir_print.cpp"
456 "${CMAKE_SOURCE_DIR}/src/libc_installation.cpp"457 "${CMAKE_SOURCE_DIR}/src/libc_installation.cpp"
457 "${CMAKE_SOURCE_DIR}/src/link.cpp"458 "${CMAKE_SOURCE_DIR}/src/link.cpp"
459 "${CMAKE_SOURCE_DIR}/src/memory_profiling.cpp"
458 "${CMAKE_SOURCE_DIR}/src/os.cpp"460 "${CMAKE_SOURCE_DIR}/src/os.cpp"
459 "${CMAKE_SOURCE_DIR}/src/parser.cpp"461 "${CMAKE_SOURCE_DIR}/src/parser.cpp"
460 "${CMAKE_SOURCE_DIR}/src/range_set.cpp"462 "${CMAKE_SOURCE_DIR}/src/range_set.cpp"
...@@ -628,5 +630,8 @@ set_target_properties(zig PROPERTIES...@@ -628,5 +630,8 @@ set_target_properties(zig PROPERTIES
628 LINK_FLAGS ${EXE_LDFLAGS}630 LINK_FLAGS ${EXE_LDFLAGS}
629)631)
630target_link_libraries(zig compiler "${LIBUSERLAND}")632target_link_libraries(zig compiler "${LIBUSERLAND}")
633if(MSVC)
634 target_link_libraries(zig ntdll.lib)
635endif()
631add_dependencies(zig zig_build_libuserland)636add_dependencies(zig zig_build_libuserland)
632install(TARGETS zig DESTINATION bin)637install(TARGETS zig DESTINATION bin)
doc/docgen.zig+1-1
...@@ -51,7 +51,7 @@ pub fn main() !void {...@@ -51,7 +51,7 @@ pub fn main() !void {
51 var toc = try genToc(allocator, &tokenizer);51 var toc = try genToc(allocator, &tokenizer);
5252
53 try fs.makePath(allocator, tmp_dir_name);53 try fs.makePath(allocator, tmp_dir_name);
54 defer fs.deleteTree(allocator, tmp_dir_name) catch {};54 defer fs.deleteTree(tmp_dir_name) catch {};
5555
56 try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe);56 try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe);
57 try buffered_out_stream.flush();57 try buffered_out_stream.flush();
doc/langref.html.in+2-2
...@@ -10086,8 +10086,8 @@ ContainerMembers...@@ -10086,8 +10086,8 @@ ContainerMembers
10086 &lt;- TestDecl ContainerMembers10086 &lt;- TestDecl ContainerMembers
10087 / TopLevelComptime ContainerMembers10087 / TopLevelComptime ContainerMembers
10088 / KEYWORD_pub? TopLevelDecl ContainerMembers10088 / KEYWORD_pub? TopLevelDecl ContainerMembers
10089 / KEYWORD_pub? ContainerField COMMA ContainerMembers10089 / ContainerField COMMA ContainerMembers
10090 / KEYWORD_pub? ContainerField10090 / ContainerField
10091 /10091 /
1009210092
10093TestDecl &lt;- KEYWORD_test STRINGLITERAL Block10093TestDecl &lt;- KEYWORD_test STRINGLITERAL Block
lib/std/build.zig+18-2
...@@ -331,7 +331,7 @@ pub const Builder = struct {...@@ -331,7 +331,7 @@ pub const Builder = struct {
331 if (self.verbose) {331 if (self.verbose) {
332 warn("rm {}\n", full_path);332 warn("rm {}\n", full_path);
333 }333 }
334 fs.deleteTree(self.allocator, full_path) catch {};334 fs.deleteTree(full_path) catch {};
335 }335 }
336336
337 // TODO remove empty directories337 // TODO remove empty directories
...@@ -1491,6 +1491,8 @@ pub const LibExeObjStep = struct {...@@ -1491,6 +1491,8 @@ pub const LibExeObjStep = struct {
1491 /// Position Independent Code1491 /// Position Independent Code
1492 force_pic: ?bool = null,1492 force_pic: ?bool = null,
14931493
1494 subsystem: ?builtin.SubSystem = null,
1495
1494 const LinkObject = union(enum) {1496 const LinkObject = union(enum) {
1495 StaticPath: []const u8,1497 StaticPath: []const u8,
1496 OtherStep: *LibExeObjStep,1498 OtherStep: *LibExeObjStep,
...@@ -2325,6 +2327,20 @@ pub const LibExeObjStep = struct {...@@ -2325,6 +2327,20 @@ pub const LibExeObjStep = struct {
2325 }2327 }
2326 }2328 }
23272329
2330 if (self.subsystem) |subsystem| {
2331 try zig_args.append("--subsystem");
2332 try zig_args.append(switch (subsystem) {
2333 .Console => "console",
2334 .Windows => "windows",
2335 .Posix => "posix",
2336 .Native => "native",
2337 .EfiApplication => "efi_application",
2338 .EfiBootServiceDriver => "efi_boot_service_driver",
2339 .EfiRom => "efi_rom",
2340 .EfiRuntimeDriver => "efi_runtime_driver",
2341 });
2342 }
2343
2328 if (self.kind == Kind.Test) {2344 if (self.kind == Kind.Test) {
2329 try builder.spawnChild(zig_args.toSliceConst());2345 try builder.spawnChild(zig_args.toSliceConst());
2330 } else {2346 } else {
...@@ -2671,7 +2687,7 @@ pub const RemoveDirStep = struct {...@@ -2671,7 +2687,7 @@ pub const RemoveDirStep = struct {
2671 const self = @fieldParentPtr(RemoveDirStep, "step", step);2687 const self = @fieldParentPtr(RemoveDirStep, "step", step);
26722688
2673 const full_path = self.builder.pathFromRoot(self.dir_path);2689 const full_path = self.builder.pathFromRoot(self.dir_path);
2674 fs.deleteTree(self.builder.allocator, full_path) catch |err| {2690 fs.deleteTree(full_path) catch |err| {
2675 warn("Unable to remove {}: {}\n", full_path, @errorName(err));2691 warn("Unable to remove {}: {}\n", full_path, @errorName(err));
2676 return err;2692 return err;
2677 };2693 };
lib/std/c.zig+1
...@@ -80,6 +80,7 @@ pub extern "c" fn mmap(addr: ?*align(page_size) c_void, len: usize, prot: c_uint...@@ -80,6 +80,7 @@ pub extern "c" fn mmap(addr: ?*align(page_size) c_void, len: usize, prot: c_uint
80pub extern "c" fn munmap(addr: *align(page_size) c_void, len: usize) c_int;80pub extern "c" fn munmap(addr: *align(page_size) c_void, len: usize) c_int;
81pub extern "c" fn mprotect(addr: *align(page_size) c_void, len: usize, prot: c_uint) c_int;81pub extern "c" fn mprotect(addr: *align(page_size) c_void, len: usize, prot: c_uint) c_int;
82pub extern "c" fn unlink(path: [*]const u8) c_int;82pub extern "c" fn unlink(path: [*]const u8) c_int;
83pub extern "c" fn unlinkat(dirfd: fd_t, path: [*]const u8, flags: c_uint) c_int;
83pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8;84pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8;
84pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_uint, options: c_uint) c_int;85pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_uint, options: c_uint) c_int;
85pub extern "c" fn fork() c_int;86pub extern "c" fn fork() c_int;
lib/std/child_process.zig+16-16
...@@ -17,35 +17,35 @@ const TailQueue = std.TailQueue;...@@ -17,35 +17,35 @@ const TailQueue = std.TailQueue;
17const maxInt = std.math.maxInt;17const maxInt = std.math.maxInt;
1818
19pub const ChildProcess = struct {19pub const ChildProcess = struct {
20 pub pid: if (os.windows.is_the_target) void else i32,20 pid: if (os.windows.is_the_target) void else i32,
21 pub handle: if (os.windows.is_the_target) windows.HANDLE else void,21 handle: if (os.windows.is_the_target) windows.HANDLE else void,
22 pub thread_handle: if (os.windows.is_the_target) windows.HANDLE else void,22 thread_handle: if (os.windows.is_the_target) windows.HANDLE else void,
2323
24 pub allocator: *mem.Allocator,24 allocator: *mem.Allocator,
2525
26 pub stdin: ?File,26 stdin: ?File,
27 pub stdout: ?File,27 stdout: ?File,
28 pub stderr: ?File,28 stderr: ?File,
2929
30 pub term: ?(SpawnError!Term),30 term: ?(SpawnError!Term),
3131
32 pub argv: []const []const u8,32 argv: []const []const u8,
3333
34 /// Leave as null to use the current env map using the supplied allocator.34 /// Leave as null to use the current env map using the supplied allocator.
35 pub env_map: ?*const BufMap,35 env_map: ?*const BufMap,
3636
37 pub stdin_behavior: StdIo,37 stdin_behavior: StdIo,
38 pub stdout_behavior: StdIo,38 stdout_behavior: StdIo,
39 pub stderr_behavior: StdIo,39 stderr_behavior: StdIo,
4040
41 /// Set to change the user id when spawning the child process.41 /// Set to change the user id when spawning the child process.
42 pub uid: if (os.windows.is_the_target) void else ?u32,42 uid: if (os.windows.is_the_target) void else ?u32,
4343
44 /// Set to change the group id when spawning the child process.44 /// Set to change the group id when spawning the child process.
45 pub gid: if (os.windows.is_the_target) void else ?u32,45 gid: if (os.windows.is_the_target) void else ?u32,
4646
47 /// Set to change the current working directory when spawning the child process.47 /// Set to change the current working directory when spawning the child process.
48 pub cwd: ?[]const u8,48 cwd: ?[]const u8,
4949
50 err_pipe: if (os.windows.is_the_target) void else [2]os.fd_t,50 err_pipe: if (os.windows.is_the_target) void else [2]os.fd_t,
51 llnode: if (os.windows.is_the_target) void else TailQueue(*ChildProcess).Node,51 llnode: if (os.windows.is_the_target) void else TailQueue(*ChildProcess).Node,
lib/std/event/fs.zig+1-1
...@@ -1312,7 +1312,7 @@ const test_tmp_dir = "std_event_fs_test";...@@ -1312,7 +1312,7 @@ const test_tmp_dir = "std_event_fs_test";
1312//1312//
1313// // TODO move this into event loop too1313// // TODO move this into event loop too
1314// try os.makePath(allocator, test_tmp_dir);1314// try os.makePath(allocator, test_tmp_dir);
1315// defer os.deleteTree(allocator, test_tmp_dir) catch {};1315// defer os.deleteTree(test_tmp_dir) catch {};
1316//1316//
1317// var loop: Loop = undefined;1317// var loop: Loop = undefined;
1318// try loop.initMultiThreaded(allocator);1318// try loop.initMultiThreaded(allocator);
lib/std/fs.zig+653-383
...@@ -335,444 +335,708 @@ pub fn deleteDirW(dir_path: [*]const u16) !void {...@@ -335,444 +335,708 @@ pub fn deleteDirW(dir_path: [*]const u16) !void {
335 return os.rmdirW(dir_path);335 return os.rmdirW(dir_path);
336}336}
337337
338const DeleteTreeError = error{338/// Removes a symlink, file, or directory.
339 OutOfMemory,339/// If `full_path` is relative, this is equivalent to `Dir.deleteTree` with the
340 AccessDenied,340/// current working directory as the open directory handle.
341 FileTooBig,341/// If `full_path` is absolute, this is equivalent to `Dir.deleteTree` with the
342 IsDir,342/// base directory.
343 SymLinkLoop,343pub fn deleteTree(full_path: []const u8) !void {
344 ProcessFdQuotaExceeded,344 if (path.isAbsolute(full_path)) {
345 NameTooLong,345 const dirname = path.dirname(full_path) orelse return error{
346 SystemFdQuotaExceeded,346 /// Attempt to remove the root file system path.
347 NoDevice,347 /// This error is unreachable if `full_path` is relative.
348 SystemResources,348 CannotDeleteRootDirectory,
349 NoSpaceLeft,349 }.CannotDeleteRootDirectory;
350 PathAlreadyExists,350
351 ReadOnlyFileSystem,351 var dir = try Dir.open(dirname);
352 NotDir,352 defer dir.close();
353 FileNotFound,353
354 FileSystem,354 return dir.deleteTree(path.basename(full_path));
355 FileBusy,355 } else {
356 DirNotEmpty,356 return Dir.cwd().deleteTree(full_path);
357 DeviceBusy,
358
359 /// On Windows, file paths must be valid Unicode.
360 InvalidUtf8,
361
362 /// On Windows, file paths cannot contain these characters:
363 /// '/', '*', '?', '"', '<', '>', '|'
364 BadPathName,
365
366 Unexpected,
367};
368
369/// Whether `full_path` describes a symlink, file, or directory, this function
370/// removes it. If it cannot be removed because it is a non-empty directory,
371/// this function recursively removes its entries and then tries again.
372/// TODO determine if we can remove the allocator requirement
373/// https://github.com/ziglang/zig/issues/2886
374pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void {
375 start_over: while (true) {
376 var got_access_denied = false;
377 // First, try deleting the item as a file. This way we don't follow sym links.
378 if (deleteFile(full_path)) {
379 return;
380 } else |err| switch (err) {
381 error.FileNotFound => return,
382 error.IsDir => {},
383 error.AccessDenied => got_access_denied = true,
384
385 error.InvalidUtf8,
386 error.SymLinkLoop,
387 error.NameTooLong,
388 error.SystemResources,
389 error.ReadOnlyFileSystem,
390 error.NotDir,
391 error.FileSystem,
392 error.FileBusy,
393 error.BadPathName,
394 error.Unexpected,
395 => return err,
396 }
397 {
398 var dir = Dir.open(allocator, full_path) catch |err| switch (err) {
399 error.NotDir => {
400 if (got_access_denied) {
401 return error.AccessDenied;
402 }
403 continue :start_over;
404 },
405
406 error.OutOfMemory,
407 error.AccessDenied,
408 error.FileTooBig,
409 error.IsDir,
410 error.SymLinkLoop,
411 error.ProcessFdQuotaExceeded,
412 error.NameTooLong,
413 error.SystemFdQuotaExceeded,
414 error.NoDevice,
415 error.FileNotFound,
416 error.SystemResources,
417 error.NoSpaceLeft,
418 error.PathAlreadyExists,
419 error.Unexpected,
420 error.InvalidUtf8,
421 error.BadPathName,
422 error.DeviceBusy,
423 => return err,
424 };
425 defer dir.close();
426
427 var full_entry_buf = std.ArrayList(u8).init(allocator);
428 defer full_entry_buf.deinit();
429
430 while (try dir.next()) |entry| {
431 try full_entry_buf.resize(full_path.len + entry.name.len + 1);
432 const full_entry_path = full_entry_buf.toSlice();
433 mem.copy(u8, full_entry_path, full_path);
434 full_entry_path[full_path.len] = path.sep;
435 mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name);
436
437 try deleteTree(allocator, full_entry_path);
438 }
439 }
440 return deleteDir(full_path);
441 }357 }
442}358}
443359
444/// TODO: separate this API into the one that opens directory handles to then subsequently open
445/// files, and into the one that reads files from an open directory handle.
446pub const Dir = struct {360pub const Dir = struct {
447 handle: Handle,361 fd: os.fd_t,
448 allocator: *Allocator,362
363 pub const Entry = struct {
364 name: []const u8,
365 kind: Kind,
449366
450 pub const Handle = switch (builtin.os) {367 pub const Kind = enum {
368 BlockDevice,
369 CharacterDevice,
370 Directory,
371 NamedPipe,
372 SymLink,
373 File,
374 UnixDomainSocket,
375 Whiteout,
376 Unknown,
377 };
378 };
379
380 const IteratorError = error{AccessDenied} || os.UnexpectedError;
381
382 pub const Iterator = switch (builtin.os) {
451 .macosx, .ios, .freebsd, .netbsd => struct {383 .macosx, .ios, .freebsd, .netbsd => struct {
452 fd: i32,384 dir: Dir,
453 seek: i64,385 seek: i64,
454 buf: []u8,386 buf: [8192]u8, // TODO align(@alignOf(os.dirent)),
455 index: usize,387 index: usize,
456 end_index: usize,388 end_index: usize,
389
390 const Self = @This();
391
392 pub const Error = IteratorError;
393
394 /// Memory such as file names referenced in this returned entry becomes invalid
395 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
396 pub fn next(self: *Self) Error!?Entry {
397 switch (builtin.os) {
398 .macosx, .ios => return self.nextDarwin(),
399 .freebsd, .netbsd => return self.nextBsd(),
400 else => @compileError("unimplemented"),
401 }
402 }
403
404 fn nextDarwin(self: *Self) !?Entry {
405 start_over: while (true) {
406 if (self.index >= self.end_index) {
407 const rc = os.system.__getdirentries64(
408 self.dir.fd,
409 &self.buf,
410 self.buf.len,
411 &self.seek,
412 );
413 if (rc == 0) return null;
414 if (rc < 0) {
415 switch (os.errno(rc)) {
416 os.EBADF => unreachable,
417 os.EFAULT => unreachable,
418 os.ENOTDIR => unreachable,
419 os.EINVAL => unreachable,
420 else => |err| return os.unexpectedErrno(err),
421 }
422 }
423 self.index = 0;
424 self.end_index = @intCast(usize, rc);
425 }
426 const darwin_entry = @ptrCast(*align(1) os.dirent, &self.buf[self.index]);
427 const next_index = self.index + darwin_entry.d_reclen;
428 self.index = next_index;
429
430 const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen];
431
432 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
433 continue :start_over;
434 }
435
436 const entry_kind = switch (darwin_entry.d_type) {
437 os.DT_BLK => Entry.Kind.BlockDevice,
438 os.DT_CHR => Entry.Kind.CharacterDevice,
439 os.DT_DIR => Entry.Kind.Directory,
440 os.DT_FIFO => Entry.Kind.NamedPipe,
441 os.DT_LNK => Entry.Kind.SymLink,
442 os.DT_REG => Entry.Kind.File,
443 os.DT_SOCK => Entry.Kind.UnixDomainSocket,
444 os.DT_WHT => Entry.Kind.Whiteout,
445 else => Entry.Kind.Unknown,
446 };
447 return Entry{
448 .name = name,
449 .kind = entry_kind,
450 };
451 }
452 }
453
454 fn nextBsd(self: *Self) !?Entry {
455 start_over: while (true) {
456 if (self.index >= self.end_index) {
457 const rc = os.system.getdirentries(
458 self.dir.fd,
459 self.buf[0..].ptr,
460 self.buf.len,
461 &self.seek,
462 );
463 switch (os.errno(rc)) {
464 0 => {},
465 os.EBADF => unreachable,
466 os.EFAULT => unreachable,
467 os.ENOTDIR => unreachable,
468 os.EINVAL => unreachable,
469 else => |err| return os.unexpectedErrno(err),
470 }
471 if (rc == 0) return null;
472 self.index = 0;
473 self.end_index = @intCast(usize, rc);
474 }
475 const freebsd_entry = @ptrCast(*align(1) os.dirent, &self.buf[self.index]);
476 const next_index = self.index + freebsd_entry.d_reclen;
477 self.index = next_index;
478
479 const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen];
480
481 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
482 continue :start_over;
483 }
484
485 const entry_kind = switch (freebsd_entry.d_type) {
486 os.DT_BLK => Entry.Kind.BlockDevice,
487 os.DT_CHR => Entry.Kind.CharacterDevice,
488 os.DT_DIR => Entry.Kind.Directory,
489 os.DT_FIFO => Entry.Kind.NamedPipe,
490 os.DT_LNK => Entry.Kind.SymLink,
491 os.DT_REG => Entry.Kind.File,
492 os.DT_SOCK => Entry.Kind.UnixDomainSocket,
493 os.DT_WHT => Entry.Kind.Whiteout,
494 else => Entry.Kind.Unknown,
495 };
496 return Entry{
497 .name = name,
498 .kind = entry_kind,
499 };
500 }
501 }
457 },502 },
458 .linux => struct {503 .linux => struct {
459 fd: i32,504 dir: Dir,
460 buf: []u8,505 buf: [8192]u8, // TODO align(@alignOf(os.dirent64)),
461 index: usize,506 index: usize,
462 end_index: usize,507 end_index: usize,
508
509 const Self = @This();
510
511 pub const Error = IteratorError;
512
513 /// Memory such as file names referenced in this returned entry becomes invalid
514 /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
515 pub fn next(self: *Self) Error!?Entry {
516 start_over: while (true) {
517 if (self.index >= self.end_index) {
518 const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len);
519 switch (os.linux.getErrno(rc)) {
520 0 => {},
521 os.EBADF => unreachable,
522 os.EFAULT => unreachable,
523 os.ENOTDIR => unreachable,
524 os.EINVAL => unreachable,
525 else => |err| return os.unexpectedErrno(err),
526 }
527 if (rc == 0) return null;
528 self.index = 0;
529 self.end_index = rc;
530 }
531 const linux_entry = @ptrCast(*align(1) os.dirent64, &self.buf[self.index]);
532 const next_index = self.index + linux_entry.d_reclen;
533 self.index = next_index;
534
535 const name = mem.toSlice(u8, @ptrCast([*]u8, &linux_entry.d_name));
536
537 // skip . and .. entries
538 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
539 continue :start_over;
540 }
541
542 const entry_kind = switch (linux_entry.d_type) {
543 os.DT_BLK => Entry.Kind.BlockDevice,
544 os.DT_CHR => Entry.Kind.CharacterDevice,
545 os.DT_DIR => Entry.Kind.Directory,
546 os.DT_FIFO => Entry.Kind.NamedPipe,
547 os.DT_LNK => Entry.Kind.SymLink,
548 os.DT_REG => Entry.Kind.File,
549 os.DT_SOCK => Entry.Kind.UnixDomainSocket,
550 else => Entry.Kind.Unknown,
551 };
552 return Entry{
553 .name = name,
554 .kind = entry_kind,
555 };
556 }
557 }
463 },558 },
464 .windows => struct {559 .windows => struct {
465 handle: os.windows.HANDLE,560 dir: Dir,
466 find_file_data: os.windows.WIN32_FIND_DATAW,561 buf: [8192]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)),
562 index: usize,
563 end_index: usize,
467 first: bool,564 first: bool,
468 name_data: [256]u8,565 name_data: [256]u8,
566
567 const Self = @This();
568
569 pub const Error = IteratorError;
570
571 pub fn next(self: *Self) Error!?Entry {
572 start_over: while (true) {
573 const w = os.windows;
574 if (self.index >= self.end_index) {
575 var io: w.IO_STATUS_BLOCK = undefined;
576 const rc = w.ntdll.NtQueryDirectoryFile(
577 self.dir.fd,
578 null,
579 null,
580 null,
581 &io,
582 &self.buf,
583 self.buf.len,
584 .FileBothDirectoryInformation,
585 w.FALSE,
586 null,
587 if (self.first) w.BOOLEAN(w.TRUE) else w.BOOLEAN(w.FALSE),
588 );
589 self.first = false;
590 if (io.Information == 0) return null;
591 self.index = 0;
592 self.end_index = io.Information;
593 switch (rc) {
594 w.STATUS.SUCCESS => {},
595 w.STATUS.ACCESS_DENIED => return error.AccessDenied,
596 else => return w.unexpectedStatus(rc),
597 }
598 }
599
600 const aligned_ptr = @alignCast(@alignOf(w.FILE_BOTH_DIR_INFORMATION), &self.buf[self.index]);
601 const dir_info = @ptrCast(*w.FILE_BOTH_DIR_INFORMATION, aligned_ptr);
602 if (dir_info.NextEntryOffset != 0) {
603 self.index += dir_info.NextEntryOffset;
604 } else {
605 self.index = self.buf.len;
606 }
607
608 const name_utf16le = @ptrCast([*]u16, &dir_info.FileName)[0 .. dir_info.FileNameLength / 2];
609
610 if (mem.eql(u16, name_utf16le, [_]u16{'.'}) or mem.eql(u16, name_utf16le, [_]u16{ '.', '.' }))
611 continue;
612 // Trust that Windows gives us valid UTF-16LE
613 const name_utf8_len = std.unicode.utf16leToUtf8(self.name_data[0..], name_utf16le) catch unreachable;
614 const name_utf8 = self.name_data[0..name_utf8_len];
615 const kind = blk: {
616 const attrs = dir_info.FileAttributes;
617 if (attrs & w.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
618 if (attrs & w.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink;
619 break :blk Entry.Kind.File;
620 };
621 return Entry{
622 .name = name_utf8,
623 .kind = kind,
624 };
625 }
626 }
469 },627 },
470 else => @compileError("unimplemented"),628 else => @compileError("unimplemented"),
471 };629 };
472630
473 pub const Entry = struct {631 pub fn iterate(self: Dir) Iterator {
474 name: []const u8,632 switch (builtin.os) {
475 kind: Kind,633 .macosx, .ios, .freebsd, .netbsd => return Iterator{
634 .dir = self,
635 .seek = 0,
636 .index = 0,
637 .end_index = 0,
638 .buf = undefined,
639 },
640 .linux => return Iterator{
641 .dir = self,
642 .index = 0,
643 .end_index = 0,
644 .buf = undefined,
645 },
646 .windows => return Iterator{
647 .dir = self,
648 .index = 0,
649 .end_index = 0,
650 .first = true,
651 .buf = undefined,
652 .name_data = undefined,
653 },
654 else => @compileError("unimplemented"),
655 }
656 }
476657
477 pub const Kind = enum {658 /// Returns an open handle to the current working directory.
478 BlockDevice,659 /// Closing the returned `Dir` is checked illegal behavior.
479 CharacterDevice,660 /// On POSIX targets, this function is comptime-callable.
480 Directory,661 pub fn cwd() Dir {
481 NamedPipe,662 if (os.windows.is_the_target) {
482 SymLink,663 return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
483 File,664 } else {
484 UnixDomainSocket,665 return Dir{ .fd = os.AT_FDCWD };
485 Whiteout,666 }
486 Unknown,667 }
487 };
488 };
489668
490 pub const OpenError = error{669 pub const OpenError = error{
491 FileNotFound,670 FileNotFound,
492 NotDir,671 NotDir,
493 AccessDenied,672 AccessDenied,
494 FileTooBig,
495 IsDir,
496 SymLinkLoop,673 SymLinkLoop,
497 ProcessFdQuotaExceeded,674 ProcessFdQuotaExceeded,
498 NameTooLong,675 NameTooLong,
499 SystemFdQuotaExceeded,676 SystemFdQuotaExceeded,
500 NoDevice,677 NoDevice,
501 SystemResources,678 SystemResources,
502 NoSpaceLeft,
503 PathAlreadyExists,
504 OutOfMemory,
505 InvalidUtf8,679 InvalidUtf8,
506 BadPathName,680 BadPathName,
507 DeviceBusy,681 DeviceBusy,
682 } || os.UnexpectedError;
508683
509 Unexpected,684 /// Call `close` to free the directory handle.
510 };685 pub fn open(dir_path: []const u8) OpenError!Dir {
511686 return cwd().openDir(dir_path);
512 /// Call close when done.
513 /// TODO remove the allocator requirement from this API
514 /// https://github.com/ziglang/zig/issues/2885
515 pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {
516 return Dir{
517 .allocator = allocator,
518 .handle = switch (builtin.os) {
519 .windows => blk: {
520 var find_file_data: os.windows.WIN32_FIND_DATAW = undefined;
521 const handle = try os.windows.FindFirstFile(dir_path, &find_file_data);
522 break :blk Handle{
523 .handle = handle,
524 .find_file_data = find_file_data, // TODO guaranteed copy elision
525 .first = true,
526 .name_data = undefined,
527 };
528 },
529 .macosx, .ios, .freebsd, .netbsd => Handle{
530 .fd = try os.open(dir_path, os.O_RDONLY | os.O_NONBLOCK | os.O_DIRECTORY | os.O_CLOEXEC, 0),
531 .seek = 0,
532 .index = 0,
533 .end_index = 0,
534 .buf = [_]u8{},
535 },
536 .linux => Handle{
537 .fd = try os.open(dir_path, os.O_RDONLY | os.O_DIRECTORY | os.O_CLOEXEC, 0),
538 .index = 0,
539 .end_index = 0,
540 .buf = [_]u8{},
541 },
542 else => @compileError("unimplemented"),
543 },
544 };
545 }687 }
546688
547 pub fn close(self: *Dir) void {689 /// Same as `open` except the parameter is null-terminated.
548 if (os.windows.is_the_target) {690 pub fn openC(dir_path_c: [*]const u8) OpenError!Dir {
549 return os.windows.FindClose(self.handle.handle);691 return cwd().openDirC(dir_path_c);
550 }
551 self.allocator.free(self.handle.buf);
552 os.close(self.handle.fd);
553 }692 }
554693
555 /// Memory such as file names referenced in this returned entry becomes invalid694 pub fn close(self: *Dir) void {
556 /// with subsequent calls to next, as well as when this `Dir` is deinitialized.695 os.close(self.fd);
557 pub fn next(self: *Dir) !?Entry {696 self.* = undefined;
558 switch (builtin.os) {
559 .linux => return self.nextLinux(),
560 .macosx, .ios => return self.nextDarwin(),
561 .windows => return self.nextWindows(),
562 .freebsd => return self.nextBsd(),
563 .netbsd => return self.nextBsd(),
564 else => @compileError("unimplemented"),
565 }
566 }697 }
567698
568 pub fn openRead(self: Dir, file_path: []const u8) os.OpenError!File {699 /// Call `File.close` on the result when done.
569 const path_c = try os.toPosixPath(file_path);700 pub fn openRead(self: Dir, sub_path: []const u8) File.OpenError!File {
701 const path_c = try os.toPosixPath(sub_path);
570 return self.openReadC(&path_c);702 return self.openReadC(&path_c);
571 }703 }
572704
573 pub fn openReadC(self: Dir, file_path: [*]const u8) OpenError!File {705 /// Call `File.close` on the result when done.
706 pub fn openReadC(self: Dir, sub_path: [*]const u8) File.OpenError!File {
574 const flags = os.O_LARGEFILE | os.O_RDONLY;707 const flags = os.O_LARGEFILE | os.O_RDONLY;
575 const fd = try os.openatC(self.handle.fd, file_path, flags, 0);708 const fd = try os.openatC(self.fd, sub_path, flags, 0);
576 return File.openHandle(fd);709 return File.openHandle(fd);
577 }710 }
578711
579 fn nextDarwin(self: *Dir) !?Entry {712 /// Call `close` on the result when done.
580 start_over: while (true) {713 pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir {
581 if (self.handle.index >= self.handle.end_index) {714 if (os.windows.is_the_target) {
582 if (self.handle.buf.len == 0) {715 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
583 self.handle.buf = try self.allocator.alloc(u8, mem.page_size);716 return self.openDirW(&sub_path_w);
584 }717 }
585
586 while (true) {
587 const rc = os.system.__getdirentries64(
588 self.handle.fd,
589 self.handle.buf.ptr,
590 self.handle.buf.len,
591 &self.handle.seek,
592 );
593 if (rc == 0) return null;
594 if (rc < 0) {
595 switch (os.errno(rc)) {
596 os.EBADF => unreachable,
597 os.EFAULT => unreachable,
598 os.ENOTDIR => unreachable,
599 os.EINVAL => {
600 self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2);
601 continue;
602 },
603 else => |err| return os.unexpectedErrno(err),
604 }
605 }
606 self.handle.index = 0;
607 self.handle.end_index = @intCast(usize, rc);
608 break;
609 }
610 }
611 const darwin_entry = @ptrCast(*align(1) os.dirent, &self.handle.buf[self.handle.index]);
612 const next_index = self.handle.index + darwin_entry.d_reclen;
613 self.handle.index = next_index;
614
615 const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen];
616718
617 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {719 const sub_path_c = try os.toPosixPath(sub_path);
618 continue :start_over;720 return self.openDirC(&sub_path_c);
619 }721 }
620722
621 const entry_kind = switch (darwin_entry.d_type) {723 /// Same as `openDir` except the parameter is null-terminated.
622 os.DT_BLK => Entry.Kind.BlockDevice,724 pub fn openDirC(self: Dir, sub_path_c: [*]const u8) OpenError!Dir {
623 os.DT_CHR => Entry.Kind.CharacterDevice,725 if (os.windows.is_the_target) {
624 os.DT_DIR => Entry.Kind.Directory,726 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
625 os.DT_FIFO => Entry.Kind.NamedPipe,727 return self.openDirW(&sub_path_w);
626 os.DT_LNK => Entry.Kind.SymLink,
627 os.DT_REG => Entry.Kind.File,
628 os.DT_SOCK => Entry.Kind.UnixDomainSocket,
629 os.DT_WHT => Entry.Kind.Whiteout,
630 else => Entry.Kind.Unknown,
631 };
632 return Entry{
633 .name = name,
634 .kind = entry_kind,
635 };
636 }728 }
729
730 const flags = os.O_RDONLY | os.O_DIRECTORY | os.O_CLOEXEC;
731 const fd = os.openatC(self.fd, sub_path_c, flags, 0) catch |err| switch (err) {
732 error.FileTooBig => unreachable, // can't happen for directories
733 error.IsDir => unreachable, // we're providing O_DIRECTORY
734 error.NoSpaceLeft => unreachable, // not providing O_CREAT
735 error.PathAlreadyExists => unreachable, // not providing O_CREAT
736 else => |e| return e,
737 };
738 return Dir{ .fd = fd };
637 }739 }
638740
639 fn nextWindows(self: *Dir) !?Entry {741 /// Same as `openDir` except the path parameter is UTF16LE, NT-prefixed.
640 while (true) {742 /// This function is Windows-only.
641 if (self.handle.first) {743 pub fn openDirW(self: Dir, sub_path_w: [*]const u16) OpenError!Dir {
642 self.handle.first = false;744 const w = os.windows;
643 } else {745
644 if (!try os.windows.FindNextFile(self.handle.handle, &self.handle.find_file_data))746 var result = Dir{
645 return null;747 .fd = undefined,
646 }748 };
647 const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr);749
648 if (mem.eql(u16, name_utf16le, [_]u16{'.'}) or mem.eql(u16, name_utf16le, [_]u16{ '.', '.' }))750 const path_len_bytes = @intCast(u16, mem.toSliceConst(u16, sub_path_w).len * 2);
649 continue;751 var nt_name = w.UNICODE_STRING{
650 // Trust that Windows gives us valid UTF-16LE752 .Length = path_len_bytes,
651 const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable;753 .MaximumLength = path_len_bytes,
652 const name_utf8 = self.handle.name_data[0..name_utf8_len];754 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
653 const kind = blk: {755 };
654 const attrs = self.handle.find_file_data.dwFileAttributes;756 var attr = w.OBJECT_ATTRIBUTES{
655 if (attrs & os.windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;757 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
656 if (attrs & os.windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink;758 .RootDirectory = if (path.isAbsoluteW(sub_path_w)) null else self.fd,
657 break :blk Entry.Kind.File;759 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
658 };760 .ObjectName = &nt_name,
659 return Entry{761 .SecurityDescriptor = null,
660 .name = name_utf8,762 .SecurityQualityOfService = null,
661 .kind = kind,763 };
662 };764 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
765 // Windows does not recognize this, but it does work with empty string.
766 nt_name.Length = 0;
767 }
768 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
769 // If you're looking to contribute to zig and fix this, see here for an example of how to
770 // implement this: https://git.midipix.org/ntapi/tree/src/fs/ntapi_tt_open_physical_parent_directory.c
771 @panic("TODO opening '..' with a relative directory handle is not yet implemented on Windows");
772 }
773 var io: w.IO_STATUS_BLOCK = undefined;
774 const rc = w.ntdll.NtCreateFile(
775 &result.fd,
776 w.GENERIC_READ | w.SYNCHRONIZE,
777 &attr,
778 &io,
779 null,
780 0,
781 w.FILE_SHARE_READ | w.FILE_SHARE_WRITE,
782 w.FILE_OPEN,
783 w.FILE_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT | w.FILE_OPEN_FOR_BACKUP_INTENT,
784 null,
785 0,
786 );
787 switch (rc) {
788 w.STATUS.SUCCESS => return result,
789 w.STATUS.OBJECT_NAME_INVALID => unreachable,
790 w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
791 w.STATUS.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
792 w.STATUS.INVALID_PARAMETER => unreachable,
793 else => return w.unexpectedStatus(rc),
663 }794 }
664 }795 }
665796
666 fn nextLinux(self: *Dir) !?Entry {797 pub const DeleteFileError = os.UnlinkError;
667 start_over: while (true) {
668 if (self.handle.index >= self.handle.end_index) {
669 if (self.handle.buf.len == 0) {
670 self.handle.buf = try self.allocator.alloc(u8, mem.page_size);
671 }
672798
673 while (true) {799 /// Delete a file name and possibly the file it refers to, based on an open directory handle.
674 const rc = os.linux.getdents64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len);800 pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
675 switch (os.linux.getErrno(rc)) {801 const sub_path_c = try os.toPosixPath(sub_path);
676 0 => {},802 return self.deleteFileC(&sub_path_c);
677 os.EBADF => unreachable,803 }
678 os.EFAULT => unreachable,
679 os.ENOTDIR => unreachable,
680 os.EINVAL => {
681 self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2);
682 continue;
683 },
684 else => |err| return os.unexpectedErrno(err),
685 }
686 if (rc == 0) return null;
687 self.handle.index = 0;
688 self.handle.end_index = rc;
689 break;
690 }
691 }
692 const linux_entry = @ptrCast(*align(1) os.dirent64, &self.handle.buf[self.handle.index]);
693 const next_index = self.handle.index + linux_entry.d_reclen;
694 self.handle.index = next_index;
695804
696 const name = mem.toSlice(u8, @ptrCast([*]u8, &linux_entry.d_name));805 /// Same as `deleteFile` except the parameter is null-terminated.
806 pub fn deleteFileC(self: Dir, sub_path_c: [*]const u8) DeleteFileError!void {
807 os.unlinkatC(self.fd, sub_path_c, 0) catch |err| switch (err) {
808 error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
809 else => |e| return e,
810 };
811 }
697812
698 // skip . and .. entries813 pub const DeleteDirError = error{
699 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {814 DirNotEmpty,
700 continue :start_over;815 FileNotFound,
701 }816 AccessDenied,
817 FileBusy,
818 FileSystem,
819 SymLinkLoop,
820 NameTooLong,
821 NotDir,
822 SystemResources,
823 ReadOnlyFileSystem,
824 InvalidUtf8,
825 BadPathName,
826 Unexpected,
827 };
702828
703 const entry_kind = switch (linux_entry.d_type) {829 /// Returns `error.DirNotEmpty` if the directory is not empty.
704 os.DT_BLK => Entry.Kind.BlockDevice,830 /// To delete a directory recursively, see `deleteTree`.
705 os.DT_CHR => Entry.Kind.CharacterDevice,831 pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
706 os.DT_DIR => Entry.Kind.Directory,832 if (os.windows.is_the_target) {
707 os.DT_FIFO => Entry.Kind.NamedPipe,833 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
708 os.DT_LNK => Entry.Kind.SymLink,834 return self.deleteDirW(&sub_path_w);
709 os.DT_REG => Entry.Kind.File,
710 os.DT_SOCK => Entry.Kind.UnixDomainSocket,
711 else => Entry.Kind.Unknown,
712 };
713 return Entry{
714 .name = name,
715 .kind = entry_kind,
716 };
717 }835 }
836 const sub_path_c = try os.toPosixPath(sub_path);
837 return self.deleteDirC(&sub_path_c);
718 }838 }
719839
720 fn nextBsd(self: *Dir) !?Entry {840 /// Same as `deleteDir` except the parameter is null-terminated.
721 start_over: while (true) {841 pub fn deleteDirC(self: Dir, sub_path_c: [*]const u8) DeleteDirError!void {
722 if (self.handle.index >= self.handle.end_index) {842 os.unlinkatC(self.fd, sub_path_c, os.AT_REMOVEDIR) catch |err| switch (err) {
723 if (self.handle.buf.len == 0) {843 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
724 self.handle.buf = try self.allocator.alloc(u8, mem.page_size);844 else => |e| return e,
725 }845 };
846 }
726847
727 while (true) {848 /// Same as `deleteDir` except the parameter is UTF16LE, NT prefixed.
728 const rc = os.system.getdirentries(849 /// This function is Windows-only.
729 self.handle.fd,850 pub fn deleteDirW(self: Dir, sub_path_w: [*]const u16) DeleteDirError!void {
730 self.handle.buf.ptr,851 os.unlinkatW(self.fd, sub_path_w, os.AT_REMOVEDIR) catch |err| switch (err) {
731 self.handle.buf.len,852 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
732 &self.handle.seek,853 else => |e| return e,
733 );854 };
734 switch (os.errno(rc)) {855 }
735 0 => {},
736 os.EBADF => unreachable,
737 os.EFAULT => unreachable,
738 os.ENOTDIR => unreachable,
739 os.EINVAL => {
740 self.handle.buf = try self.allocator.realloc(self.handle.buf, self.handle.buf.len * 2);
741 continue;
742 },
743 else => |err| return os.unexpectedErrno(err),
744 }
745 if (rc == 0) return null;
746 self.handle.index = 0;
747 self.handle.end_index = @intCast(usize, rc);
748 break;
749 }
750 }
751 const freebsd_entry = @ptrCast(*align(1) os.dirent, &self.handle.buf[self.handle.index]);
752 const next_index = self.handle.index + freebsd_entry.d_reclen;
753 self.handle.index = next_index;
754856
755 const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen];857 /// Read value of a symbolic link.
858 /// The return value is a slice of `buffer`, from index `0`.
859 pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
860 const sub_path_c = try os.toPosixPath(sub_path);
861 return self.readLinkC(&sub_path_c, buffer);
862 }
756863
757 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {864 /// Same as `readLink`, except the `pathname` parameter is null-terminated.
758 continue :start_over;865 pub fn readLinkC(self: Dir, sub_path_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
866 return os.readlinkatC(self.fd, sub_path_c, buffer);
867 }
868
869 pub const DeleteTreeError = error{
870 AccessDenied,
871 FileTooBig,
872 SymLinkLoop,
873 ProcessFdQuotaExceeded,
874 NameTooLong,
875 SystemFdQuotaExceeded,
876 NoDevice,
877 SystemResources,
878 ReadOnlyFileSystem,
879 FileSystem,
880 FileBusy,
881 DeviceBusy,
882
883 /// One of the path components was not a directory.
884 /// This error is unreachable if `sub_path` does not contain a path separator.
885 NotDir,
886
887 /// On Windows, file paths must be valid Unicode.
888 InvalidUtf8,
889
890 /// On Windows, file paths cannot contain these characters:
891 /// '/', '*', '?', '"', '<', '>', '|'
892 BadPathName,
893 } || os.UnexpectedError;
894
895 /// Whether `full_path` describes a symlink, file, or directory, this function
896 /// removes it. If it cannot be removed because it is a non-empty directory,
897 /// this function recursively removes its entries and then tries again.
898 /// This operation is not atomic on most file systems.
899 pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void {
900 start_over: while (true) {
901 var got_access_denied = false;
902 // First, try deleting the item as a file. This way we don't follow sym links.
903 if (self.deleteFile(sub_path)) {
904 return;
905 } else |err| switch (err) {
906 error.FileNotFound => return,
907 error.IsDir => {},
908 error.AccessDenied => got_access_denied = true,
909
910 error.InvalidUtf8,
911 error.SymLinkLoop,
912 error.NameTooLong,
913 error.SystemResources,
914 error.ReadOnlyFileSystem,
915 error.NotDir,
916 error.FileSystem,
917 error.FileBusy,
918 error.BadPathName,
919 error.Unexpected,
920 => |e| return e,
759 }921 }
922 var dir = self.openDir(sub_path) catch |err| switch (err) {
923 error.NotDir => {
924 if (got_access_denied) {
925 return error.AccessDenied;
926 }
927 continue :start_over;
928 },
929 error.FileNotFound => {
930 // That's fine, we were trying to remove this directory anyway.
931 continue :start_over;
932 },
760933
761 const entry_kind = switch (freebsd_entry.d_type) {934 error.AccessDenied,
762 os.DT_BLK => Entry.Kind.BlockDevice,935 error.SymLinkLoop,
763 os.DT_CHR => Entry.Kind.CharacterDevice,936 error.ProcessFdQuotaExceeded,
764 os.DT_DIR => Entry.Kind.Directory,937 error.NameTooLong,
765 os.DT_FIFO => Entry.Kind.NamedPipe,938 error.SystemFdQuotaExceeded,
766 os.DT_LNK => Entry.Kind.SymLink,939 error.NoDevice,
767 os.DT_REG => Entry.Kind.File,940 error.SystemResources,
768 os.DT_SOCK => Entry.Kind.UnixDomainSocket,941 error.Unexpected,
769 os.DT_WHT => Entry.Kind.Whiteout,942 error.InvalidUtf8,
770 else => Entry.Kind.Unknown,943 error.BadPathName,
771 };944 error.DeviceBusy,
772 return Entry{945 => |e| return e,
773 .name = name,
774 .kind = entry_kind,
775 };946 };
947 var cleanup_dir_parent: ?Dir = null;
948 defer if (cleanup_dir_parent) |*d| d.close();
949
950 var cleanup_dir = true;
951 defer if (cleanup_dir) dir.close();
952
953 var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined;
954 var dir_name: []const u8 = sub_path;
955 var parent_dir = self;
956
957 // Here we must avoid recursion, in order to provide O(1) memory guarantee of this function.
958 // Go through each entry and if it is not a directory, delete it. If it is a directory,
959 // open it, and close the original directory. Repeat. Then start the entire operation over.
960
961 scan_dir: while (true) {
962 var dir_it = dir.iterate();
963 while (try dir_it.next()) |entry| {
964 if (dir.deleteFile(entry.name)) {
965 continue;
966 } else |err| switch (err) {
967 error.FileNotFound => continue,
968
969 // Impossible because we do not pass any path separators.
970 error.NotDir => unreachable,
971
972 error.IsDir => {},
973 error.AccessDenied => got_access_denied = true,
974
975 error.InvalidUtf8,
976 error.SymLinkLoop,
977 error.NameTooLong,
978 error.SystemResources,
979 error.ReadOnlyFileSystem,
980 error.FileSystem,
981 error.FileBusy,
982 error.BadPathName,
983 error.Unexpected,
984 => |e| return e,
985 }
986
987 const new_dir = dir.openDir(entry.name) catch |err| switch (err) {
988 error.NotDir => {
989 if (got_access_denied) {
990 return error.AccessDenied;
991 }
992 continue :scan_dir;
993 },
994 error.FileNotFound => {
995 // That's fine, we were trying to remove this directory anyway.
996 continue :scan_dir;
997 },
998
999 error.AccessDenied,
1000 error.SymLinkLoop,
1001 error.ProcessFdQuotaExceeded,
1002 error.NameTooLong,
1003 error.SystemFdQuotaExceeded,
1004 error.NoDevice,
1005 error.SystemResources,
1006 error.Unexpected,
1007 error.InvalidUtf8,
1008 error.BadPathName,
1009 error.DeviceBusy,
1010 => |e| return e,
1011 };
1012 if (cleanup_dir_parent) |*d| d.close();
1013 cleanup_dir_parent = dir;
1014 dir = new_dir;
1015 mem.copy(u8, &dir_name_buf, entry.name);
1016 dir_name = dir_name_buf[0..entry.name.len];
1017 continue :scan_dir;
1018 }
1019 // Reached the end of the directory entries, which means we successfully deleted all of them.
1020 // Now to remove the directory itself.
1021 dir.close();
1022 cleanup_dir = false;
1023
1024 if (cleanup_dir_parent) |d| {
1025 d.deleteDir(dir_name) catch |err| switch (err) {
1026 // These two things can happen due to file system race conditions.
1027 error.FileNotFound, error.DirNotEmpty => continue :start_over,
1028 else => |e| return e,
1029 };
1030 continue :start_over;
1031 } else {
1032 self.deleteDir(sub_path) catch |err| switch (err) {
1033 error.FileNotFound => return,
1034 error.DirNotEmpty => continue :start_over,
1035 else => |e| return e,
1036 };
1037 return;
1038 }
1039 }
776 }1040 }
777 }1041 }
778};1042};
...@@ -782,13 +1046,18 @@ pub const Walker = struct {...@@ -782,13 +1046,18 @@ pub const Walker = struct {
782 name_buffer: std.Buffer,1046 name_buffer: std.Buffer,
7831047
784 pub const Entry = struct {1048 pub const Entry = struct {
785 path: []const u8,1049 /// The containing directory. This can be used to operate directly on `basename`
1050 /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths.
1051 /// The directory remains open until `next` or `deinit` is called.
1052 dir: Dir,
786 basename: []const u8,1053 basename: []const u8,
1054
1055 path: []const u8,
787 kind: Dir.Entry.Kind,1056 kind: Dir.Entry.Kind,
788 };1057 };
7891058
790 const StackItem = struct {1059 const StackItem = struct {
791 dir_it: Dir,1060 dir_it: Dir.Iterator,
792 dirname_len: usize,1061 dirname_len: usize,
793 };1062 };
7941063
...@@ -806,23 +1075,26 @@ pub const Walker = struct {...@@ -806,23 +1075,26 @@ pub const Walker = struct {
806 try self.name_buffer.appendByte(path.sep);1075 try self.name_buffer.appendByte(path.sep);
807 try self.name_buffer.append(base.name);1076 try self.name_buffer.append(base.name);
808 if (base.kind == .Directory) {1077 if (base.kind == .Directory) {
809 // TODO https://github.com/ziglang/zig/issues/28881078 var new_dir = top.dir_it.dir.openDir(base.name) catch |err| switch (err) {
810 var new_dir = try Dir.open(self.stack.allocator, self.name_buffer.toSliceConst());1079 error.NameTooLong => unreachable, // no path sep in base.name
1080 else => |e| return e,
1081 };
811 {1082 {
812 errdefer new_dir.close();1083 errdefer new_dir.close();
813 try self.stack.append(StackItem{1084 try self.stack.append(StackItem{
814 .dir_it = new_dir,1085 .dir_it = new_dir.iterate(),
815 .dirname_len = self.name_buffer.len(),1086 .dirname_len = self.name_buffer.len(),
816 });1087 });
817 }1088 }
818 }1089 }
819 return Entry{1090 return Entry{
1091 .dir = top.dir_it.dir,
820 .basename = self.name_buffer.toSliceConst()[dirname_len + 1 ..],1092 .basename = self.name_buffer.toSliceConst()[dirname_len + 1 ..],
821 .path = self.name_buffer.toSliceConst(),1093 .path = self.name_buffer.toSliceConst(),
822 .kind = base.kind,1094 .kind = base.kind,
823 };1095 };
824 } else {1096 } else {
825 self.stack.pop().dir_it.close();1097 self.stack.pop().dir_it.dir.close();
826 }1098 }
827 }1099 }
828 }1100 }
...@@ -837,12 +1109,12 @@ pub const Walker = struct {...@@ -837,12 +1109,12 @@ pub const Walker = struct {
837/// Recursively iterates over a directory.1109/// Recursively iterates over a directory.
838/// Must call `Walker.deinit` when done.1110/// Must call `Walker.deinit` when done.
839/// `dir_path` must not end in a path separator.1111/// `dir_path` must not end in a path separator.
840/// TODO: https://github.com/ziglang/zig/issues/28881112/// The order of returned file system entries is undefined.
841pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {1113pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
842 assert(!mem.endsWith(u8, dir_path, path.sep_str));1114 assert(!mem.endsWith(u8, dir_path, path.sep_str));
8431115
844 var dir_it = try Dir.open(allocator, dir_path);1116 var dir = try Dir.open(dir_path);
845 errdefer dir_it.close();1117 errdefer dir.close();
8461118
847 var name_buffer = try std.Buffer.init(allocator, dir_path);1119 var name_buffer = try std.Buffer.init(allocator, dir_path);
848 errdefer name_buffer.deinit();1120 errdefer name_buffer.deinit();
...@@ -853,7 +1125,7 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {...@@ -853,7 +1125,7 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
853 };1125 };
8541126
855 try walker.stack.append(Walker.StackItem{1127 try walker.stack.append(Walker.StackItem{
856 .dir_it = dir_it,1128 .dir_it = dir.iterate(),
857 .dirname_len = dir_path.len,1129 .dirname_len = dir_path.len,
858 });1130 });
8591131
...@@ -862,15 +1134,13 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {...@@ -862,15 +1134,13 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
8621134
863/// Read value of a symbolic link.1135/// Read value of a symbolic link.
864/// The return value is a slice of buffer, from index `0`.1136/// The return value is a slice of buffer, from index `0`.
865/// TODO https://github.com/ziglang/zig/issues/28881137pub fn readLink(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
866pub fn readLink(pathname: []const u8, buffer: *[os.PATH_MAX]u8) ![]u8 {
867 return os.readlink(pathname, buffer);1138 return os.readlink(pathname, buffer);
868}1139}
8691140
870/// Same as `readLink`, except the `pathname` parameter is null-terminated.1141/// Same as `readLink`, except the parameter is null-terminated.
871/// TODO https://github.com/ziglang/zig/issues/28881142pub fn readLinkC(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
872pub fn readLinkC(pathname: [*]const u8, buffer: *[os.PATH_MAX]u8) ![]u8 {1143 return os.readlinkC(pathname_c, buffer);
873 return os.readlinkC(pathname, buffer);
874}1144}
8751145
876pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError;1146pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError;
lib/std/fs/file.zig+1
...@@ -243,6 +243,7 @@ pub const File = struct {...@@ -243,6 +243,7 @@ pub const File = struct {
243 switch (rc) {243 switch (rc) {
244 windows.STATUS.SUCCESS => {},244 windows.STATUS.SUCCESS => {},
245 windows.STATUS.BUFFER_OVERFLOW => {},245 windows.STATUS.BUFFER_OVERFLOW => {},
246 windows.STATUS.INVALID_PARAMETER => unreachable,
246 else => return windows.unexpectedStatus(rc),247 else => return windows.unexpectedStatus(rc),
247 }248 }
248 return Stat{249 return Stat{
lib/std/fs/path.zig+19
...@@ -136,6 +136,25 @@ pub fn isAbsolute(path: []const u8) bool {...@@ -136,6 +136,25 @@ pub fn isAbsolute(path: []const u8) bool {
136 }136 }
137}137}
138138
139pub fn isAbsoluteW(path_w: [*]const u16) bool {
140 if (path_w[0] == '/')
141 return true;
142
143 if (path_w[0] == '\\') {
144 return true;
145 }
146 if (path_w[0] == 0 or path_w[1] == 0 or path_w[2] == 0) {
147 return false;
148 }
149 if (path_w[1] == ':') {
150 if (path_w[2] == '/')
151 return true;
152 if (path_w[2] == '\\')
153 return true;
154 }
155 return false;
156}
157
139pub fn isAbsoluteWindows(path: []const u8) bool {158pub fn isAbsoluteWindows(path: []const u8) bool {
140 if (path[0] == '/')159 if (path[0] == '/')
141 return true;160 return true;
lib/std/heap.zig+1-1
...@@ -338,7 +338,7 @@ pub const HeapAllocator = switch (builtin.os) {...@@ -338,7 +338,7 @@ pub const HeapAllocator = switch (builtin.os) {
338/// This allocator takes an existing allocator, wraps it, and provides an interface338/// This allocator takes an existing allocator, wraps it, and provides an interface
339/// where you can allocate without freeing, and then free it all together.339/// where you can allocate without freeing, and then free it all together.
340pub const ArenaAllocator = struct {340pub const ArenaAllocator = struct {
341 pub allocator: Allocator,341 allocator: Allocator,
342342
343 child_allocator: *Allocator,343 child_allocator: *Allocator,
344 buffer_list: std.SinglyLinkedList([]u8),344 buffer_list: std.SinglyLinkedList([]u8),
lib/std/http/headers.zig+3-3
...@@ -28,9 +28,9 @@ fn never_index_default(name: []const u8) bool {...@@ -28,9 +28,9 @@ fn never_index_default(name: []const u8) bool {
2828
29const HeaderEntry = struct {29const HeaderEntry = struct {
30 allocator: *Allocator,30 allocator: *Allocator,
31 pub name: []const u8,31 name: []const u8,
32 pub value: []u8,32 value: []u8,
33 pub never_index: bool,33 never_index: bool,
3434
35 const Self = @This();35 const Self = @This();
3636
lib/std/io.zig+13-10
...@@ -127,6 +127,7 @@ pub fn OutStream(comptime WriteError: type) type {...@@ -127,6 +127,7 @@ pub fn OutStream(comptime WriteError: type) type {
127 };127 };
128}128}
129129
130/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
130pub fn writeFile(path: []const u8, data: []const u8) !void {131pub fn writeFile(path: []const u8, data: []const u8) !void {
131 var file = try File.openWrite(path);132 var file = try File.openWrite(path);
132 defer file.close();133 defer file.close();
...@@ -134,11 +135,13 @@ pub fn writeFile(path: []const u8, data: []const u8) !void {...@@ -134,11 +135,13 @@ pub fn writeFile(path: []const u8, data: []const u8) !void {
134}135}
135136
136/// On success, caller owns returned buffer.137/// On success, caller owns returned buffer.
138/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
137pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {139pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
138 return readFileAllocAligned(allocator, path, @alignOf(u8));140 return readFileAllocAligned(allocator, path, @alignOf(u8));
139}141}
140142
141/// On success, caller owns returned buffer.143/// On success, caller owns returned buffer.
144/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
142pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 {145pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 {
143 var file = try File.openRead(path);146 var file = try File.openRead(path);
144 defer file.close();147 defer file.close();
...@@ -161,7 +164,7 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type)...@@ -161,7 +164,7 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type)
161 const Self = @This();164 const Self = @This();
162 const Stream = InStream(Error);165 const Stream = InStream(Error);
163166
164 pub stream: Stream,167 stream: Stream,
165168
166 unbuffered_in_stream: *Stream,169 unbuffered_in_stream: *Stream,
167170
...@@ -273,7 +276,7 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ...@@ -273,7 +276,7 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ
273 pub const Error = InStreamError;276 pub const Error = InStreamError;
274 pub const Stream = InStream(Error);277 pub const Stream = InStream(Error);
275278
276 pub stream: Stream,279 stream: Stream,
277 base: *Stream,280 base: *Stream,
278281
279 // Right now the look-ahead space is statically allocated, but a version with dynamic allocation282 // Right now the look-ahead space is statically allocated, but a version with dynamic allocation
...@@ -336,7 +339,7 @@ pub const SliceInStream = struct {...@@ -336,7 +339,7 @@ pub const SliceInStream = struct {
336 pub const Error = error{};339 pub const Error = error{};
337 pub const Stream = InStream(Error);340 pub const Stream = InStream(Error);
338341
339 pub stream: Stream,342 stream: Stream,
340343
341 pos: usize,344 pos: usize,
342 slice: []const u8,345 slice: []const u8,
...@@ -514,9 +517,9 @@ pub const SliceOutStream = struct {...@@ -514,9 +517,9 @@ pub const SliceOutStream = struct {
514 pub const Error = error{OutOfSpace};517 pub const Error = error{OutOfSpace};
515 pub const Stream = OutStream(Error);518 pub const Stream = OutStream(Error);
516519
517 pub stream: Stream,520 stream: Stream,
518521
519 pub pos: usize,522 pos: usize,
520 slice: []u8,523 slice: []u8,
521524
522 pub fn init(slice: []u8) SliceOutStream {525 pub fn init(slice: []u8) SliceOutStream {
...@@ -571,7 +574,7 @@ pub const NullOutStream = struct {...@@ -571,7 +574,7 @@ pub const NullOutStream = struct {
571 pub const Error = error{};574 pub const Error = error{};
572 pub const Stream = OutStream(Error);575 pub const Stream = OutStream(Error);
573576
574 pub stream: Stream,577 stream: Stream,
575578
576 pub fn init() NullOutStream {579 pub fn init() NullOutStream {
577 return NullOutStream{580 return NullOutStream{
...@@ -595,8 +598,8 @@ pub fn CountingOutStream(comptime OutStreamError: type) type {...@@ -595,8 +598,8 @@ pub fn CountingOutStream(comptime OutStreamError: type) type {
595 pub const Stream = OutStream(Error);598 pub const Stream = OutStream(Error);
596 pub const Error = OutStreamError;599 pub const Error = OutStreamError;
597600
598 pub stream: Stream,601 stream: Stream,
599 pub bytes_written: u64,602 bytes_written: u64,
600 child_stream: *Stream,603 child_stream: *Stream,
601604
602 pub fn init(child_stream: *Stream) Self {605 pub fn init(child_stream: *Stream) Self {
...@@ -635,7 +638,7 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr...@@ -635,7 +638,7 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr
635 pub const Stream = OutStream(Error);638 pub const Stream = OutStream(Error);
636 pub const Error = OutStreamError;639 pub const Error = OutStreamError;
637640
638 pub stream: Stream,641 stream: Stream,
639642
640 unbuffered_out_stream: *Stream,643 unbuffered_out_stream: *Stream,
641644
...@@ -1084,7 +1087,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,...@@ -1084,7 +1087,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
1084 // safety. If it is bad, it will be caught anyway.1087 // safety. If it is bad, it will be caught anyway.
1085 const TagInt = @TagType(TagType);1088 const TagInt = @TagType(TagType);
1086 const tag = try self.deserializeInt(TagInt);1089 const tag = try self.deserializeInt(TagInt);
1087 1090
1088 inline for (info.fields) |field_info| {1091 inline for (info.fields) |field_info| {
1089 if (field_info.enum_field.?.value == tag) {1092 if (field_info.enum_field.?.value == tag) {
1090 const name = field_info.name;1093 const name = field_info.name;
lib/std/io/seekable_stream.zig+2-2
...@@ -39,8 +39,8 @@ pub const SliceSeekableInStream = struct {...@@ -39,8 +39,8 @@ pub const SliceSeekableInStream = struct {
39 pub const Stream = InStream(Error);39 pub const Stream = InStream(Error);
40 pub const SeekableInStream = SeekableStream(SeekError, GetSeekPosError);40 pub const SeekableInStream = SeekableStream(SeekError, GetSeekPosError);
4141
42 pub stream: Stream,42 stream: Stream,
43 pub seekable_stream: SeekableInStream,43 seekable_stream: SeekableInStream,
4444
45 pos: usize,45 pos: usize,
46 slice: []const u8,46 slice: []const u8,
lib/std/os.zig+181-36
...@@ -529,22 +529,36 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void...@@ -529,22 +529,36 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void
529529
530pub const OpenError = error{530pub const OpenError = error{
531 AccessDenied,531 AccessDenied,
532 FileTooBig,
533 IsDir,
534 SymLinkLoop,532 SymLinkLoop,
535 ProcessFdQuotaExceeded,533 ProcessFdQuotaExceeded,
536 NameTooLong,
537 SystemFdQuotaExceeded,534 SystemFdQuotaExceeded,
538 NoDevice,535 NoDevice,
539 FileNotFound,536 FileNotFound,
540537
538 /// The path exceeded `MAX_PATH_BYTES` bytes.
539 NameTooLong,
540
541 /// Insufficient kernel memory was available, or541 /// Insufficient kernel memory was available, or
542 /// the named file is a FIFO and per-user hard limit on542 /// the named file is a FIFO and per-user hard limit on
543 /// memory allocation for pipes has been reached.543 /// memory allocation for pipes has been reached.
544 SystemResources,544 SystemResources,
545545
546 /// The file is too large to be opened. This error is unreachable
547 /// for 64-bit targets, as well as when opening directories.
548 FileTooBig,
549
550 /// The path refers to directory but the `O_DIRECTORY` flag was not provided.
551 IsDir,
552
553 /// A new path cannot be created because the device has no room for the new file.
554 /// This error is only reachable when the `O_CREAT` flag is provided.
546 NoSpaceLeft,555 NoSpaceLeft,
556
557 /// A component used as a directory in the path was not, in fact, a directory, or
558 /// `O_DIRECTORY` was specified and the path was not a directory.
547 NotDir,559 NotDir,
560
561 /// The path already exists and the `O_CREAT` and `O_EXCL` flags were provided.
548 PathAlreadyExists,562 PathAlreadyExists,
549 DeviceBusy,563 DeviceBusy,
550} || UnexpectedError;564} || UnexpectedError;
...@@ -978,6 +992,114 @@ pub fn unlinkC(file_path: [*]const u8) UnlinkError!void {...@@ -978,6 +992,114 @@ pub fn unlinkC(file_path: [*]const u8) UnlinkError!void {
978 }992 }
979}993}
980994
995pub const UnlinkatError = UnlinkError || error{
996 /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty.
997 DirNotEmpty,
998};
999
1000/// Delete a file name and possibly the file it refers to, based on an open directory handle.
1001pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
1002 if (windows.is_the_target) {
1003 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
1004 return unlinkatW(dirfd, &file_path_w, flags);
1005 }
1006 const file_path_c = try toPosixPath(file_path);
1007 return unlinkatC(dirfd, &file_path_c, flags);
1008}
1009
1010/// Same as `unlinkat` but `file_path` is a null-terminated string.
1011pub fn unlinkatC(dirfd: fd_t, file_path_c: [*]const u8, flags: u32) UnlinkatError!void {
1012 if (windows.is_the_target) {
1013 const file_path_w = try windows.cStrToPrefixedFileW(file_path_c);
1014 return unlinkatW(dirfd, &file_path_w, flags);
1015 }
1016 switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
1017 0 => return,
1018 EACCES => return error.AccessDenied,
1019 EPERM => return error.AccessDenied,
1020 EBUSY => return error.FileBusy,
1021 EFAULT => unreachable,
1022 EIO => return error.FileSystem,
1023 EISDIR => return error.IsDir,
1024 ELOOP => return error.SymLinkLoop,
1025 ENAMETOOLONG => return error.NameTooLong,
1026 ENOENT => return error.FileNotFound,
1027 ENOTDIR => return error.NotDir,
1028 ENOMEM => return error.SystemResources,
1029 EROFS => return error.ReadOnlyFileSystem,
1030 ENOTEMPTY => return error.DirNotEmpty,
1031
1032 EINVAL => unreachable, // invalid flags, or pathname has . as last component
1033 EBADF => unreachable, // always a race condition
1034
1035 else => |err| return unexpectedErrno(err),
1036 }
1037}
1038
1039/// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only.
1040pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*]const u16, flags: u32) UnlinkatError!void {
1041 const w = windows;
1042
1043 const want_rmdir_behavior = (flags & AT_REMOVEDIR) != 0;
1044 const create_options_flags = if (want_rmdir_behavior)
1045 w.ULONG(w.FILE_DELETE_ON_CLOSE)
1046 else
1047 w.ULONG(w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE);
1048
1049 const path_len_bytes = @intCast(u16, mem.toSliceConst(u16, sub_path_w).len * 2);
1050 var nt_name = w.UNICODE_STRING{
1051 .Length = path_len_bytes,
1052 .MaximumLength = path_len_bytes,
1053 // The Windows API makes this mutable, but it will not mutate here.
1054 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
1055 };
1056
1057 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
1058 // Windows does not recognize this, but it does work with empty string.
1059 nt_name.Length = 0;
1060 }
1061 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
1062 // Can't remove the parent directory with an open handle.
1063 return error.FileBusy;
1064 }
1065
1066
1067 var attr = w.OBJECT_ATTRIBUTES{
1068 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
1069 .RootDirectory = dirfd,
1070 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
1071 .ObjectName = &nt_name,
1072 .SecurityDescriptor = null,
1073 .SecurityQualityOfService = null,
1074 };
1075 var io: w.IO_STATUS_BLOCK = undefined;
1076 var tmp_handle: w.HANDLE = undefined;
1077 var rc = w.ntdll.NtCreateFile(
1078 &tmp_handle,
1079 w.SYNCHRONIZE | w.DELETE,
1080 &attr,
1081 &io,
1082 null,
1083 0,
1084 w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE,
1085 w.FILE_OPEN,
1086 create_options_flags,
1087 null,
1088 0,
1089 );
1090 if (rc == w.STATUS.SUCCESS) {
1091 rc = w.ntdll.NtClose(tmp_handle);
1092 }
1093 switch (rc) {
1094 w.STATUS.SUCCESS => return,
1095 w.STATUS.OBJECT_NAME_INVALID => unreachable,
1096 w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
1097 w.STATUS.INVALID_PARAMETER => unreachable,
1098 w.STATUS.FILE_IS_A_DIRECTORY => return error.IsDir,
1099 else => return w.unexpectedStatus(rc),
1100 }
1101}
1102
981const RenameError = error{1103const RenameError = error{
982 AccessDenied,1104 AccessDenied,
983 FileBusy,1105 FileBusy,
...@@ -1237,6 +1359,27 @@ pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {...@@ -1237,6 +1359,27 @@ pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {
1237 }1359 }
1238}1360}
12391361
1362pub fn readlinkatC(dirfd: fd_t, file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {
1363 if (windows.is_the_target) {
1364 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
1365 @compileError("TODO implement readlink for Windows");
1366 }
1367 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
1368 switch (errno(rc)) {
1369 0 => return out_buffer[0..@bitCast(usize, rc)],
1370 EACCES => return error.AccessDenied,
1371 EFAULT => unreachable,
1372 EINVAL => unreachable,
1373 EIO => return error.FileSystem,
1374 ELOOP => return error.SymLinkLoop,
1375 ENAMETOOLONG => return error.NameTooLong,
1376 ENOENT => return error.FileNotFound,
1377 ENOMEM => return error.SystemResources,
1378 ENOTDIR => return error.NotDir,
1379 else => |err| return unexpectedErrno(err),
1380 }
1381}
1382
1240pub const SetIdError = error{1383pub const SetIdError = error{
1241 ResourceLimitReached,1384 ResourceLimitReached,
1242 InvalidUserId,1385 InvalidUserId,
...@@ -1476,18 +1619,46 @@ pub const AcceptError = error{...@@ -1476,18 +1619,46 @@ pub const AcceptError = error{
1476 BlockedByFirewall,1619 BlockedByFirewall,
1477} || UnexpectedError;1620} || UnexpectedError;
14781621
1479/// Accept a connection on a socket. `fd` must be opened in blocking mode.1622/// Accept a connection on a socket.
1480/// See also `accept4_async`.1623/// If the application has a global event loop enabled, EAGAIN is handled
1481pub fn accept4(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 {1624/// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
1625pub fn accept4(
1626 /// This argument is a socket that has been created with `socket`, bound to a local address
1627 /// with `bind`, and is listening for connections after a `listen`.
1628 sockfd: i32,
1629 /// This argument is a pointer to a sockaddr structure. This structure is filled in with the
1630 /// address of the peer socket, as known to the communications layer. The exact format of the
1631 /// address returned addr is determined by the socket's address family (see `socket` and the
1632 /// respective protocol man pages).
1633 addr: *sockaddr,
1634 /// This argument is a value-result argument: the caller must initialize it to contain the
1635 /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size
1636 /// of the peer address.
1637 ///
1638 /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size`
1639 /// will return a value greater than was supplied to the call.
1640 addr_size: *usize,
1641 /// If flags is 0, then `accept4` is the same as `accept`. The following values can be bitwise
1642 /// ORed in flags to obtain different behavior:
1643 /// * `SOCK_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the open file description (see `open`)
1644 /// referred to by the new file descriptor. Using this flag saves extra calls to `fcntl` to achieve
1645 /// the same result.
1646 /// * `SOCK_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the
1647 /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful.
1648 flags: u32,
1649) AcceptError!i32 {
1482 while (true) {1650 while (true) {
1483 var sockaddr_size = u32(@sizeOf(sockaddr));1651 const rc = system.accept4(sockfd, addr, addr_size, flags);
1484 const rc = system.accept4(fd, addr, &sockaddr_size, flags);
1485 switch (errno(rc)) {1652 switch (errno(rc)) {
1486 0 => return @intCast(i32, rc),1653 0 => return @intCast(i32, rc),
1487 EINTR => continue,1654 EINTR => continue,
1488 else => |err| return unexpectedErrno(err),
14891655
1490 EAGAIN => unreachable, // This function is for blocking only.1656 EAGAIN => if (std.event.Loop.instance) |loop| {
1657 loop.waitUntilFdReadable(sockfd) catch return error.WouldBlock;
1658 continue;
1659 } else {
1660 return error.WouldBlock;
1661 },
1491 EBADF => unreachable, // always a race condition1662 EBADF => unreachable, // always a race condition
1492 ECONNABORTED => return error.ConnectionAborted,1663 ECONNABORTED => return error.ConnectionAborted,
1493 EFAULT => unreachable,1664 EFAULT => unreachable,
...@@ -1500,34 +1671,8 @@ pub fn accept4(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 {...@@ -1500,34 +1671,8 @@ pub fn accept4(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 {
1500 EOPNOTSUPP => return error.OperationNotSupported,1671 EOPNOTSUPP => return error.OperationNotSupported,
1501 EPROTO => return error.ProtocolFailure,1672 EPROTO => return error.ProtocolFailure,
1502 EPERM => return error.BlockedByFirewall,1673 EPERM => return error.BlockedByFirewall,
1503 }
1504 }
1505}
15061674
1507/// This is the same as `accept4` except `fd` is expected to be non-blocking.
1508/// Returns -1 if would block.
1509pub fn accept4_async(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 {
1510 while (true) {
1511 var sockaddr_size = u32(@sizeOf(sockaddr));
1512 const rc = system.accept4(fd, addr, &sockaddr_size, flags);
1513 switch (errno(rc)) {
1514 0 => return @intCast(i32, rc),
1515 EINTR => continue,
1516 else => |err| return unexpectedErrno(err),1675 else => |err| return unexpectedErrno(err),
1517
1518 EAGAIN => return -1,
1519 EBADF => unreachable, // always a race condition
1520 ECONNABORTED => return error.ConnectionAborted,
1521 EFAULT => unreachable,
1522 EINVAL => unreachable,
1523 EMFILE => return error.ProcessFdQuotaExceeded,
1524 ENFILE => return error.SystemFdQuotaExceeded,
1525 ENOBUFS => return error.SystemResources,
1526 ENOMEM => return error.SystemResources,
1527 ENOTSOCK => return error.FileDescriptorNotASocket,
1528 EOPNOTSUPP => return error.OperationNotSupported,
1529 EPROTO => return error.ProtocolFailure,
1530 EPERM => return error.BlockedByFirewall,
1531 }1676 }
1532 }1677 }
1533}1678}
lib/std/os/bits/darwin.zig+14
...@@ -1178,3 +1178,17 @@ pub fn S_IWHT(m: u32) bool {...@@ -1178,3 +1178,17 @@ pub fn S_IWHT(m: u32) bool {
1178 return m & S_IFMT == S_IFWHT;1178 return m & S_IFMT == S_IFWHT;
1179}1179}
1180pub const HOST_NAME_MAX = 72;1180pub const HOST_NAME_MAX = 72;
1181
1182pub const AT_FDCWD = -2;
1183
1184/// Use effective ids in access check
1185pub const AT_EACCESS = 0x0010;
1186
1187/// Act on the symlink itself not the target
1188pub const AT_SYMLINK_NOFOLLOW = 0x0020;
1189
1190/// Act on target of symlink
1191pub const AT_SYMLINK_FOLLOW = 0x0040;
1192
1193/// Path refers to directory
1194pub const AT_REMOVEDIR = 0x0080;
lib/std/os/bits/freebsd.zig+20
...@@ -939,3 +939,23 @@ pub fn S_IWHT(m: u32) bool {...@@ -939,3 +939,23 @@ pub fn S_IWHT(m: u32) bool {
939}939}
940940
941pub const HOST_NAME_MAX = 255;941pub const HOST_NAME_MAX = 255;
942
943/// Magic value that specify the use of the current working directory
944/// to determine the target of relative file paths in the openat() and
945/// similar syscalls.
946pub const AT_FDCWD = -100;
947
948/// Check access using effective user and group ID
949pub const AT_EACCESS = 0x0100;
950
951/// Do not follow symbolic links
952pub const AT_SYMLINK_NOFOLLOW = 0x0200;
953
954/// Follow symbolic link
955pub const AT_SYMLINK_FOLLOW = 0x0400;
956
957/// Remove directory instead of file
958pub const AT_REMOVEDIR = 0x0800;
959
960/// Fail if not under dirfd
961pub const AT_BENEATH = 0x1000;
lib/std/os/bits/windows.zig+3
...@@ -158,3 +158,6 @@ pub const EWOULDBLOCK = 140;...@@ -158,3 +158,6 @@ pub const EWOULDBLOCK = 140;
158pub const EDQUOT = 10069;158pub const EDQUOT = 10069;
159159
160pub const F_OK = 0;160pub const F_OK = 0;
161
162/// Remove directory instead of unlinking file
163pub const AT_REMOVEDIR = 0x200;
lib/std/os/test.zig+3-3
...@@ -19,8 +19,8 @@ test "makePath, put some files in it, deleteTree" {...@@ -19,8 +19,8 @@ test "makePath, put some files in it, deleteTree" {
19 try fs.makePath(a, "os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");19 try fs.makePath(a, "os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");
20 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");20 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");
21 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");21 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");
22 try fs.deleteTree(a, "os_test_tmp");22 try fs.deleteTree("os_test_tmp");
23 if (fs.Dir.open(a, "os_test_tmp")) |dir| {23 if (fs.Dir.open("os_test_tmp")) |dir| {
24 @panic("expected error");24 @panic("expected error");
25 } else |err| {25 } else |err| {
26 expect(err == error.FileNotFound);26 expect(err == error.FileNotFound);
...@@ -37,7 +37,7 @@ test "access file" {...@@ -37,7 +37,7 @@ test "access file" {
3737
38 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", "");38 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", "");
39 try os.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", os.F_OK);39 try os.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", os.F_OK);
40 try fs.deleteTree(a, "os_test_tmp");40 try fs.deleteTree("os_test_tmp");
41}41}
4242
43fn testThreadIdFn(thread_id: *Thread.Id) void {43fn testThreadIdFn(thread_id: *Thread.Id) void {
lib/std/os/windows.zig+24-4
...@@ -20,6 +20,8 @@ pub const shell32 = @import("windows/shell32.zig");...@@ -20,6 +20,8 @@ pub const shell32 = @import("windows/shell32.zig");
2020
21pub usingnamespace @import("windows/bits.zig");21pub usingnamespace @import("windows/bits.zig");
2222
23pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));
24
23/// `builtin` is missing `subsystem` when the subsystem is automatically detected,25/// `builtin` is missing `subsystem` when the subsystem is automatically detected,
24/// so Zig standard library has the subsystem detection logic here. This should generally be26/// so Zig standard library has the subsystem detection logic here. This should generally be
25/// used rather than `builtin.subsystem`.27/// used rather than `builtin.subsystem`.
...@@ -42,7 +44,6 @@ pub const subsystem: ?builtin.SubSystem = blk: {...@@ -42,7 +44,6 @@ pub const subsystem: ?builtin.SubSystem = blk: {
42 break :blk builtin.SubSystem.Console;44 break :blk builtin.SubSystem.Console;
43 }45 }
44 },46 },
45 .uefi => break :blk builtin.SubSystem.EfiApplication,
46 else => break :blk null,47 else => break :blk null,
47 }48 }
48};49};
...@@ -792,6 +793,25 @@ pub fn SetFileTime(...@@ -792,6 +793,25 @@ pub fn SetFileTime(
792 }793 }
793}794}
794795
796pub fn peb() *PEB {
797 switch (builtin.arch) {
798 .i386 => {
799 return asm (
800 \\ mov %%fs:0x18, %[ptr]
801 \\ mov %%ds:0x30(%[ptr]), %[ptr]
802 : [ptr] "=r" (-> *PEB)
803 );
804 },
805 .x86_64 => {
806 return asm (
807 \\ mov %%gs:0x60, %[ptr]
808 : [ptr] "=r" (-> *PEB)
809 );
810 },
811 else => @compileError("unsupported architecture"),
812 }
813}
814
795/// A file time is a 64-bit value that represents the number of 100-nanosecond815/// A file time is a 64-bit value that represents the number of 100-nanosecond
796/// intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated816/// intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated
797/// Universal Time (UTC).817/// Universal Time (UTC).
...@@ -844,8 +864,8 @@ pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16)...@@ -844,8 +864,8 @@ pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16)
844 else => {},864 else => {},
845 }865 }
846 }866 }
847 const start_index = if (mem.startsWith(u8, s, "\\\\") or !std.fs.path.isAbsolute(s)) 0 else blk: {867 const start_index = if (mem.startsWith(u8, s, "\\?") or !std.fs.path.isAbsolute(s)) 0 else blk: {
848 const prefix = [_]u16{ '\\', '\\', '?', '\\' };868 const prefix = [_]u16{ '\\', '?', '?', '\\' };
849 mem.copy(u16, result[0..], prefix);869 mem.copy(u16, result[0..], prefix);
850 break :blk prefix.len;870 break :blk prefix.len;
851 };871 };
...@@ -879,7 +899,7 @@ pub fn unexpectedError(err: DWORD) std.os.UnexpectedError {...@@ -879,7 +899,7 @@ pub fn unexpectedError(err: DWORD) std.os.UnexpectedError {
879/// and you get an unexpected status.899/// and you get an unexpected status.
880pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError {900pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError {
881 if (std.os.unexpected_error_tracing) {901 if (std.os.unexpected_error_tracing) {
882 std.debug.warn("error.Unexpected NTSTATUS={}\n", status);902 std.debug.warn("error.Unexpected NTSTATUS=0x{x}\n", status);
883 std.debug.dumpCurrentStackTrace(null);903 std.debug.dumpCurrentStackTrace(null);
884 }904 }
885 return error.Unexpected;905 return error.Unexpected;
lib/std/os/windows/bits.zig+145-3
...@@ -300,6 +300,44 @@ pub const FILE_SHARE_DELETE = 0x00000004;...@@ -300,6 +300,44 @@ pub const FILE_SHARE_DELETE = 0x00000004;
300pub const FILE_SHARE_READ = 0x00000001;300pub const FILE_SHARE_READ = 0x00000001;
301pub const FILE_SHARE_WRITE = 0x00000002;301pub const FILE_SHARE_WRITE = 0x00000002;
302302
303pub const DELETE = 0x00010000;
304pub const READ_CONTROL = 0x00020000;
305pub const WRITE_DAC = 0x00040000;
306pub const WRITE_OWNER = 0x00080000;
307pub const SYNCHRONIZE = 0x00100000;
308pub const STANDARD_RIGHTS_REQUIRED = 0x000f0000;
309
310// disposition for NtCreateFile
311pub const FILE_SUPERSEDE = 0;
312pub const FILE_OPEN = 1;
313pub const FILE_CREATE = 2;
314pub const FILE_OPEN_IF = 3;
315pub const FILE_OVERWRITE = 4;
316pub const FILE_OVERWRITE_IF = 5;
317pub const FILE_MAXIMUM_DISPOSITION = 5;
318
319// flags for NtCreateFile and NtOpenFile
320pub const FILE_DIRECTORY_FILE = 0x00000001;
321pub const FILE_WRITE_THROUGH = 0x00000002;
322pub const FILE_SEQUENTIAL_ONLY = 0x00000004;
323pub const FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008;
324pub const FILE_SYNCHRONOUS_IO_ALERT = 0x00000010;
325pub const FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020;
326pub const FILE_NON_DIRECTORY_FILE = 0x00000040;
327pub const FILE_CREATE_TREE_CONNECTION = 0x00000080;
328pub const FILE_COMPLETE_IF_OPLOCKED = 0x00000100;
329pub const FILE_NO_EA_KNOWLEDGE = 0x00000200;
330pub const FILE_OPEN_FOR_RECOVERY = 0x00000400;
331pub const FILE_RANDOM_ACCESS = 0x00000800;
332pub const FILE_DELETE_ON_CLOSE = 0x00001000;
333pub const FILE_OPEN_BY_FILE_ID = 0x00002000;
334pub const FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000;
335pub const FILE_NO_COMPRESSION = 0x00008000;
336pub const FILE_RESERVE_OPFILTER = 0x00100000;
337pub const FILE_TRANSACTED_MODE = 0x00200000;
338pub const FILE_OPEN_OFFLINE_FILE = 0x00400000;
339pub const FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000;
340
303pub const CREATE_ALWAYS = 2;341pub const CREATE_ALWAYS = 2;
304pub const CREATE_NEW = 1;342pub const CREATE_NEW = 1;
305pub const OPEN_ALWAYS = 4;343pub const OPEN_ALWAYS = 4;
...@@ -720,15 +758,119 @@ pub const VECTORED_EXCEPTION_HANDLER = stdcallcc fn (ExceptionInfo: *EXCEPTION_P...@@ -720,15 +758,119 @@ pub const VECTORED_EXCEPTION_HANDLER = stdcallcc fn (ExceptionInfo: *EXCEPTION_P
720758
721pub const OBJECT_ATTRIBUTES = extern struct {759pub const OBJECT_ATTRIBUTES = extern struct {
722 Length: ULONG,760 Length: ULONG,
723 RootDirectory: HANDLE,761 RootDirectory: ?HANDLE,
724 ObjectName: *UNICODE_STRING,762 ObjectName: *UNICODE_STRING,
725 Attributes: ULONG,763 Attributes: ULONG,
726 SecurityDescriptor: ?*c_void,764 SecurityDescriptor: ?*c_void,
727 SecurityQualityOfService: ?*c_void,765 SecurityQualityOfService: ?*c_void,
728};766};
729767
768pub const OBJ_INHERIT = 0x00000002;
769pub const OBJ_PERMANENT = 0x00000010;
770pub const OBJ_EXCLUSIVE = 0x00000020;
771pub const OBJ_CASE_INSENSITIVE = 0x00000040;
772pub const OBJ_OPENIF = 0x00000080;
773pub const OBJ_OPENLINK = 0x00000100;
774pub const OBJ_KERNEL_HANDLE = 0x00000200;
775pub const OBJ_VALID_ATTRIBUTES = 0x000003F2;
776
730pub const UNICODE_STRING = extern struct {777pub const UNICODE_STRING = extern struct {
731 Length: USHORT,778 Length: c_ushort,
732 MaximumLength: USHORT,779 MaximumLength: c_ushort,
733 Buffer: [*]WCHAR,780 Buffer: [*]WCHAR,
734};781};
782
783pub const PEB = extern struct {
784 Reserved1: [2]BYTE,
785 BeingDebugged: BYTE,
786 Reserved2: [1]BYTE,
787 Reserved3: [2]PVOID,
788 Ldr: *PEB_LDR_DATA,
789 ProcessParameters: *RTL_USER_PROCESS_PARAMETERS,
790 Reserved4: [3]PVOID,
791 AtlThunkSListPtr: PVOID,
792 Reserved5: PVOID,
793 Reserved6: ULONG,
794 Reserved7: PVOID,
795 Reserved8: ULONG,
796 AtlThunkSListPtr32: ULONG,
797 Reserved9: [45]PVOID,
798 Reserved10: [96]BYTE,
799 PostProcessInitRoutine: PPS_POST_PROCESS_INIT_ROUTINE,
800 Reserved11: [128]BYTE,
801 Reserved12: [1]PVOID,
802 SessionId: ULONG,
803};
804
805pub const PEB_LDR_DATA = extern struct {
806 Reserved1: [8]BYTE,
807 Reserved2: [3]PVOID,
808 InMemoryOrderModuleList: LIST_ENTRY,
809};
810
811pub const RTL_USER_PROCESS_PARAMETERS = extern struct {
812 AllocationSize: ULONG,
813 Size: ULONG,
814 Flags: ULONG,
815 DebugFlags: ULONG,
816 ConsoleHandle: HANDLE,
817 ConsoleFlags: ULONG,
818 hStdInput: HANDLE,
819 hStdOutput: HANDLE,
820 hStdError: HANDLE,
821 CurrentDirectory: CURDIR,
822 DllPath: UNICODE_STRING,
823 ImagePathName: UNICODE_STRING,
824 CommandLine: UNICODE_STRING,
825 Environment: [*]WCHAR,
826 dwX: ULONG,
827 dwY: ULONG,
828 dwXSize: ULONG,
829 dwYSize: ULONG,
830 dwXCountChars: ULONG,
831 dwYCountChars: ULONG,
832 dwFillAttribute: ULONG,
833 dwFlags: ULONG,
834 dwShowWindow: ULONG,
835 WindowTitle: UNICODE_STRING,
836 Desktop: UNICODE_STRING,
837 ShellInfo: UNICODE_STRING,
838 RuntimeInfo: UNICODE_STRING,
839 DLCurrentDirectory: [0x20]RTL_DRIVE_LETTER_CURDIR,
840};
841
842pub const RTL_DRIVE_LETTER_CURDIR = extern struct {
843 Flags: c_ushort,
844 Length: c_ushort,
845 TimeStamp: ULONG,
846 DosPath: UNICODE_STRING,
847};
848
849pub const PPS_POST_PROCESS_INIT_ROUTINE = ?extern fn () void;
850
851pub const FILE_BOTH_DIR_INFORMATION = extern struct {
852 NextEntryOffset: ULONG,
853 FileIndex: ULONG,
854 CreationTime: LARGE_INTEGER,
855 LastAccessTime: LARGE_INTEGER,
856 LastWriteTime: LARGE_INTEGER,
857 ChangeTime: LARGE_INTEGER,
858 EndOfFile: LARGE_INTEGER,
859 AllocationSize: LARGE_INTEGER,
860 FileAttributes: ULONG,
861 FileNameLength: ULONG,
862 EaSize: ULONG,
863 ShortNameLength: CHAR,
864 ShortName: [12]WCHAR,
865 FileName: [1]WCHAR,
866};
867pub const FILE_BOTH_DIRECTORY_INFORMATION = FILE_BOTH_DIR_INFORMATION;
868
869pub const IO_APC_ROUTINE = extern fn (PVOID, *IO_STATUS_BLOCK, ULONG) void;
870
871pub const CURDIR = extern struct {
872 DosPath: UNICODE_STRING,
873 Handle: HANDLE,
874};
875
876pub const DUPLICATE_SAME_ACCESS = 2;
\ No newline at end of file
lib/std/os/windows/kernel32.zig+2
...@@ -47,6 +47,8 @@ pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_...@@ -47,6 +47,8 @@ pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_
4747
48pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL;48pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL;
4949
50pub extern "kernel32" stdcallcc fn DuplicateHandle(hSourceProcessHandle: HANDLE, hSourceHandle: HANDLE, hTargetProcessHandle: HANDLE, lpTargetHandle: *HANDLE, dwDesiredAccess: DWORD, bInheritHandle: BOOL, dwOptions: DWORD) BOOL;
51
50pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;52pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
5153
52pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE;54pub extern "kernel32" stdcallcc fn FindFirstFileW(lpFileName: [*]const u16, lpFindFileData: *WIN32_FIND_DATAW) HANDLE;
lib/std/os/windows/ntdll.zig+23-2
...@@ -13,12 +13,33 @@ pub extern "NtDll" stdcallcc fn NtCreateFile(...@@ -13,12 +13,33 @@ pub extern "NtDll" stdcallcc fn NtCreateFile(
13 DesiredAccess: ACCESS_MASK,13 DesiredAccess: ACCESS_MASK,
14 ObjectAttributes: *OBJECT_ATTRIBUTES,14 ObjectAttributes: *OBJECT_ATTRIBUTES,
15 IoStatusBlock: *IO_STATUS_BLOCK,15 IoStatusBlock: *IO_STATUS_BLOCK,
16 AllocationSize: *LARGE_INTEGER,16 AllocationSize: ?*LARGE_INTEGER,
17 FileAttributes: ULONG,17 FileAttributes: ULONG,
18 ShareAccess: ULONG,18 ShareAccess: ULONG,
19 CreateDisposition: ULONG,19 CreateDisposition: ULONG,
20 CreateOptions: ULONG,20 CreateOptions: ULONG,
21 EaBuffer: *c_void,21 EaBuffer: ?*c_void,
22 EaLength: ULONG,22 EaLength: ULONG,
23) NTSTATUS;23) NTSTATUS;
24pub extern "NtDll" stdcallcc fn NtClose(Handle: HANDLE) NTSTATUS;24pub extern "NtDll" stdcallcc fn NtClose(Handle: HANDLE) NTSTATUS;
25pub extern "NtDll" stdcallcc fn RtlDosPathNameToNtPathName_U(
26 DosPathName: [*]const u16,
27 NtPathName: *UNICODE_STRING,
28 NtFileNamePart: ?*?[*]const u16,
29 DirectoryInfo: ?*CURDIR,
30) BOOL;
31pub extern "NtDll" stdcallcc fn RtlFreeUnicodeString(UnicodeString: *UNICODE_STRING) void;
32
33pub extern "NtDll" stdcallcc fn NtQueryDirectoryFile(
34 FileHandle: HANDLE,
35 Event: ?HANDLE,
36 ApcRoutine: ?IO_APC_ROUTINE,
37 ApcContext: ?*c_void,
38 IoStatusBlock: *IO_STATUS_BLOCK,
39 FileInformation: *c_void,
40 Length: ULONG,
41 FileInformationClass: FILE_INFORMATION_CLASS,
42 ReturnSingleEntry: BOOLEAN,
43 FileName: ?*UNICODE_STRING,
44 RestartScan: BOOLEAN,
45) NTSTATUS;
lib/std/os/windows/status.zig+8
...@@ -3,3 +3,11 @@ pub const SUCCESS = 0x00000000;...@@ -3,3 +3,11 @@ pub const SUCCESS = 0x00000000;
33
4/// The data was too large to fit into the specified buffer.4/// The data was too large to fit into the specified buffer.
5pub const BUFFER_OVERFLOW = 0x80000005;5pub const BUFFER_OVERFLOW = 0x80000005;
6
7pub const INVALID_PARAMETER = 0xC000000D;
8pub const ACCESS_DENIED = 0xC0000022;
9pub const OBJECT_NAME_INVALID = 0xC0000033;
10pub const OBJECT_NAME_NOT_FOUND = 0xC0000034;
11pub const OBJECT_PATH_NOT_FOUND = 0xC000003A;
12pub const OBJECT_PATH_SYNTAX_BAD = 0xC000003B;
13pub const FILE_IS_A_DIRECTORY = 0xC00000BA;
lib/std/progress.zig+2-2
...@@ -155,11 +155,11 @@ pub const Progress = struct {...@@ -155,11 +155,11 @@ pub const Progress = struct {
155 }155 }
156 if (node.estimated_total_items) |total| {156 if (node.estimated_total_items) |total| {
157 if (need_ellipse) self.bufWrite(&end, " ");157 if (need_ellipse) self.bufWrite(&end, " ");
158 self.bufWrite(&end, "[{}/{}] ", node.completed_items, total);158 self.bufWrite(&end, "[{}/{}] ", node.completed_items + 1, total);
159 need_ellipse = false;159 need_ellipse = false;
160 } else if (node.completed_items != 0) {160 } else if (node.completed_items != 0) {
161 if (need_ellipse) self.bufWrite(&end, " ");161 if (need_ellipse) self.bufWrite(&end, " ");
162 self.bufWrite(&end, "[{}] ", node.completed_items);162 self.bufWrite(&end, "[{}] ", node.completed_items + 1);
163 need_ellipse = false;163 need_ellipse = false;
164 }164 }
165 }165 }
lib/std/special/docs/index.html+394-216
...@@ -5,62 +5,228 @@...@@ -5,62 +5,228 @@
5 <title>Documentation - Zig</title>5 <title>Documentation - Zig</title>
6 <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAgklEQVR4AWMYWuD7EllJIM4G4g4g5oIJ/odhOJ8wToOxSTXgNxDHoeiBMfA4+wGShjyYOCkG/IGqWQziEzYAoUAeiF9D5U+DxEg14DRU7jWIT5IBIOdCxf+A+CQZAAoopEB7QJwBCBwHiip8UYmRdrAlDpIMgApwQZNnNii5Dq0MBgCxxycBnwEd+wAAAABJRU5ErkJggg==">6 <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAgklEQVR4AWMYWuD7EllJIM4G4g4g5oIJ/odhOJ8wToOxSTXgNxDHoeiBMfA4+wGShjyYOCkG/IGqWQziEzYAoUAeiF9D5U+DxEg14DRU7jWIT5IBIOdCxf+A+CQZAAoopEB7QJwBCBwHiip8UYmRdrAlDpIMgApwQZNnNii5Dq0MBgCxxycBnwEd+wAAAABJRU5ErkJggg==">
7 <style type="text/css">7 <style type="text/css">
8 body {8 :root {
9 font-family: system-ui, -apple-system, Roboto, "Segoe UI", sans-serif;9 font-size: 1em;
10 max-width: 60em;10 --ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
11 --mono: "Source Code Pro", monospace;
12 --tx-color: #141414;
13 --bg-color: #ffffff;
14 --link-color: #2A6286;
15 --sidebar-sh-color: rgba(0, 0, 0, 0.09);
16 --sidebar-pkg-bg-color: #f1f1f1;
17 --sidebar-pkglnk-tx-color: #141414;
18 --sidebar-pkglnk-tx-color-hover: #fff;
19 --sidebar-pkglnk-tx-color-active: #000;
20 --sidebar-pkglnk-bg-color: transparent;
21 --sidebar-pkglnk-bg-color-hover: #555;
22 --sidebar-pkglnk-bg-color-active: #FFBB4D;
23 --search-bg-color: #f3f3f3;
24 --search-bg-color-focus: #ffffff;
25 --search-sh-color: rgba(0, 0, 0, 0.18);
26 --help-sh-color: rgba(0, 0, 0, 0.75);
11 }27 }
28
29 a {
30 text-decoration: none;
31 }
32
33 a:hover {
34 text-decoration: underline;
35 }
36
12 .hidden {37 .hidden {
13 display: none;38 display: none;
14 }39 }
15 a {40
16 color: #2A6286;41 /* layout */
42 .canvas {
43 width: 100vw;
44 height: 100vh;
45 overflow: hidden;
46 margin: 0;
47 padding: 0;
48 font-family: var(--ui);
49 color: var(--tx-color);
50 background-color: var(--bg-color);
17 }51 }
18 pre{52
19 font-family:"Source Code Pro",monospace;53 .flex-main {
20 font-size:1em;54 display: flex;
21 background-color:#F5F5F5;55 width: 100%;
22 padding:1em;56 height: 100%;
23 overflow-x: auto;57 justify-content: center;
58
59 z-index: 100;
24 }60 }
25 code {61
26 font-family:"Source Code Pro",monospace;62 .flex-filler {
27 font-size:1em;63 flex-grow: 1;
64 flex-shrink: 1;
28 }65 }
29 nav {66
30 width: 10em;67 .flex-left {
31 position: fixed;68 width: 12rem;
32 left: 0;69 max-width: 15vw;
33 top: 0;70 min-width: 9.5rem;
34 height: 100vh;
35 overflow: auto;71 overflow: auto;
72 overflow-wrap: break-word;
73 flex-shrink: 0;
74 flex-grow: 0;
75
76 z-index: 300;
36 }77 }
37 nav h2 {78
38 font-size: 1.2em;79 .flex-right {
39 text-decoration: underline;80 display: flex;
40 margin: 0;81 overflow: auto;
41 padding: 0.5em 0;82 flex-grow: 1;
42 text-align: center;83 flex-shrink: 1;
84
85 z-index: 200;
86 }
87
88 .flex-right > .wrap {
89 width: 60rem;
90 max-width: 85vw;
91 flex-shrink: 1;
92 }
93
94 .help-modal {
95 z-index: 400;
96 }
97
98 /* sidebar */
99 .sidebar {
100 font-size: 1rem;
101 background-color: var(--bg-color);
102 box-shadow: 0 0 1rem var(--sidebar-sh-color);
103 }
104
105 .sidebar .logo {
106 padding: 1rem 0.35rem 0.35rem 0.35rem;
107 }
108
109 .sidebar .logo > svg {
110 display: block;
111 overflow: visible;
112 }
113
114 .sidebar h2 {
115 margin: 0.5rem;
116 padding: 0;
117 font-size: 1.2rem;
43 }118 }
44 nav p {119
120 .sidebar h2 > span {
121 border-bottom: 0.125rem dotted var(--tx-color);
122 }
123
124 .sidebar .packages {
125 list-style-type: none;
45 margin: 0;126 margin: 0;
46 padding: 0;127 padding: 0;
47 text-align: center;128 background-color: var(--sidebar-pkg-bg-color);
129 }
130
131 .sidebar .packages > li > a {
132 display: block;
133 padding: 0.5rem 1rem;
134 color: var(--sidebar-pkglnk-tx-color);
135 background-color: var(--sidebar-pkglnk-bg-color);
136 text-decoration: none;
137 }
138
139 .sidebar .packages > li > a:hover {
140 color: var(--sidebar-pkglnk-tx-color-hover);
141 background-color: var(--sidebar-pkglnk-bg-color-hover);
142 }
143
144 .sidebar .packages > li > a.active {
145 color: var(--sidebar-pkglnk-tx-color-active);
146 background-color: var(--sidebar-pkglnk-bg-color-active);
147 }
148
149 .sidebar p.str {
150 margin: 0.5rem;
151 font-family: var(--mono);
152 }
153
154 /* docs section */
155 .docs {
156 padding: 1rem 0.7rem 2.4rem 1.4rem;
157 font-size: 1rem;
158 background-color: var(--bg-color);
159 overflow-wrap: break-word;
160 }
161
162 .docs .search {
163 width: 100%;
164 margin-bottom: 0.8rem;
165 padding: 0.5rem;
166 font-size: 1rem;
167 font-family: var(--ui);
168 color: var(--tx-color);
169 background-color: var(--search-bg-color);
170 border-top: 0;
171 border-left: 0;
172 border-right: 0;
173 border-bottom-width: 0.125rem;
174 border-bottom-style: solid;
175 border-bottom-color: var(--tx-color);
176 outline: none;
177 transition: border-bottom-color 0.35s, background 0.35s, box-shadow 0.35s;
178 }
179
180 .docs .search:focus {
181 background-color: var(--search-bg-color-focus);
182 border-bottom-color: #ffbb4d;
183 box-shadow: 0 0.3em 1em 0.125em var(--search-sh-color);
184 }
185
186 .docs .search::placeholder {
187 font-size: 1rem;
188 font-family: var(--ui);
189 color: var(--tx-color);
190 opacity: 0.5;
191 }
192
193 .docs a {
194 color: var(--link-color);
195 }
196
197 .docs p {
198 margin: 0.8rem 0;
199 }
200
201 .docs pre {
202 font-family: var(--mono);
203 font-size:1em;
204 background-color:#F5F5F5;
205 padding:1em;
206 overflow-x: auto;
48 }207 }
49 section {208
50 margin-left: 10em;209 .docs code {
210 font-family: var(--mono);
211 font-size: 1em;
51 }212 }
52 section h1 {213
53 border-bottom: 1px dashed;214 .docs h1 {
215 font-size: 1.4em;
216 margin: 0.8em 0;
217 padding: 0;
218 border-bottom: 0.0625rem dashed;
54 }219 }
55 section h2 {220
221 .docs h2 {
56 font-size: 1.3em;222 font-size: 1.3em;
57 margin: 0.5em 0;223 margin: 0.5em 0;
58 padding: 0;224 padding: 0;
59 border-bottom: 1px solid;225 border-bottom: 0.0625rem solid;
60 }226 }
61 #listNav {227 #listNav {
62 list-style-type: none;228 list-style-type: none;
63 margin: 0.5em 0 0 0;229 margin: 0;
64 padding: 0;230 padding: 0;
65 overflow: hidden;231 overflow: hidden;
66 background-color: #f1f1f1;232 background-color: #f1f1f1;
...@@ -84,26 +250,14 @@...@@ -84,26 +250,14 @@
84 color: #000;250 color: #000;
85 }251 }
86252
87 #listPkgs {253 #listSearchResults li.selected {
88 list-style-type: none;254 background-color: #93e196;
89 margin: 0;
90 padding: 0;
91 background-color: #f1f1f1;
92 }
93 #listPkgs li a {
94 display: block;
95 color: #000;
96 padding: 0.5em 1em;
97 text-decoration: none;
98 }
99 #listPkgs li a:hover {
100 background-color: #555;
101 color: #fff;
102 }255 }
103 #listPkgs li a.active {256
104 background-color: #FFBB4D;257 #tableFnErrors dt {
105 color: #000;258 font-weight: bold;
106 }259 }
260
107 #listFnExamples {261 #listFnExamples {
108 list-style-type: none;262 list-style-type: none;
109 margin: 0;263 margin: 0;
...@@ -114,67 +268,76 @@...@@ -114,67 +268,76 @@
114 white-space: nowrap;268 white-space: nowrap;
115 overflow-x: auto;269 overflow-x: auto;
116 }270 }
117 #logo {271
118 width: 8em;272 .docs td {
119 padding: 0.5em 1em;273 vertical-align: top;
274 margin: 0;
275 padding: 0.5em;
276 max-width: 27em;
277 text-overflow: ellipsis;
278 overflow-x: hidden;
120 }279 }
121280
122 #search {281 /* help dialog */
282 .help-modal {
283 display: flex;
123 width: 100%;284 width: 100%;
124 }285 height: 100%;
125
126 #helpDialog {
127 width: 21em;
128 height: 19em;
129 position: fixed;286 position: fixed;
130 top: 0;287 top: 0;
131 left: 0;288 left: 0;
132 background-color: #333;289 justify-content: center;
290 align-items: center;
291 background-color: rgba(0, 0, 0, 0.15);
292 backdrop-filter: blur(0.3em);
293 }
294
295 .help-modal > .dialog {
296 max-width: 97vw;
297 max-height: 97vh;
298 overflow: auto;
299 font-size: 1rem;
133 color: #fff;300 color: #fff;
134 border: 1px solid #fff;301 background-color: #333;
302 border: 0.125rem solid #000;
303 box-shadow: 0 0.5rem 2.5rem 0.3rem var(--help-sh-color);
135 }304 }
136 #helpDialog h1 {305
137 text-align: center;306 .help-modal h1 {
307 margin: 0.75em 2.5em 1em 2.5em;
138 font-size: 1.5em;308 font-size: 1.5em;
309 text-align: center;
139 }310 }
140 #helpDialog dt, #helpDialog dd {311
312 .help-modal dt, .help-modal dd {
141 display: inline;313 display: inline;
142 margin: 0 0.2em;314 margin: 0 0.2em;
143 }315 }
144 kbd {316
317 .help-modal dl {
318 margin-left: 0.5em;
319 margin-right: 0.5em;
320 }
321
322 .help-modal kbd {
323 display: inline-block;
324 padding: 0.3em 0.2em;
325 font-size: 1.2em;
326 font-size: var(--mono);
327 line-height: 0.8em;
328 vertical-align: middle;
145 color: #000;329 color: #000;
146 background-color: #fafbfc;330 background-color: #fafbfc;
147 border-color: #d1d5da;331 border-color: #d1d5da;
148 border-bottom-color: #c6cbd1;332 border-bottom-color: #c6cbd1;
333 border: solid 0.0625em;
334 border-radius: 0.1875em;
149 box-shadow-color: #c6cbd1;335 box-shadow-color: #c6cbd1;
150 display: inline-block;336 box-shadow: inset 0 -0.0625em 0;
151 padding: 0.3em 0.2em;
152 font: 1.2em monospace;
153 line-height: 0.8em;
154 vertical-align: middle;
155 border: solid 1px;
156 border-radius: 3px;
157 box-shadow: inset 0 -1px 0;
158 cursor: default;337 cursor: default;
159 }338 }
160339
161 #listSearchResults li.selected {340 /* tokens */
162 background-color: #93e196;
163 }
164
165 #tableFnErrors dt {
166 font-weight: bold;
167 }
168
169 td {
170 vertical-align: top;
171 margin: 0;
172 padding: 0.5em;
173 max-width: 27em;
174 text-overflow: ellipsis;
175 overflow-x: hidden;
176 }
177
178 .tok-kw {341 .tok-kw {
179 color: #333;342 color: #333;
180 font-weight: bold;343 font-weight: bold;
...@@ -203,16 +366,29 @@...@@ -203,16 +366,29 @@
203 color: #458;366 color: #458;
204 font-weight: bold;367 font-weight: bold;
205 }368 }
206369
370 /* dark mode */
207 @media (prefers-color-scheme: dark) {371 @media (prefers-color-scheme: dark) {
208 body{372
209 background-color: #111;373 :root {
210 color: #bbb;374 --tx-color: #bbb;
375 --bg-color: #111;
376 --link-color: #88f;
377 --sidebar-sh-color: rgba(128, 128, 128, 0.09);
378 --sidebar-pkg-bg-color: #333;
379 --sidebar-pkglnk-tx-color: #fff;
380 --sidebar-pkglnk-tx-color-hover: #fff;
381 --sidebar-pkglnk-tx-color-active: #000;
382 --sidebar-pkglnk-bg-color: transparent;
383 --sidebar-pkglnk-bg-color-hover: #555;
384 --sidebar-pkglnk-bg-color-active: #FFBB4D;
385 --search-bg-color: #3c3c3c;
386 --search-bg-color-focus: #000;
387 --search-sh-color: rgba(255, 255, 255, 0.28);
388 --help-sh-color: rgba(142, 142, 142, 0.5);
211 }389 }
212 a {390
213 color: #88f;391 .docs pre {
214 }
215 pre{
216 background-color:#2A2A2A;392 background-color:#2A2A2A;
217 }393 }
218 #listNav {394 #listNav {
...@@ -229,20 +405,6 @@...@@ -229,20 +405,6 @@
229 background-color: #FFBB4D;405 background-color: #FFBB4D;
230 color: #000;406 color: #000;
231 }407 }
232 #listPkgs {
233 background-color: #333;
234 }
235 #listPkgs li a {
236 color: #fff;
237 }
238 #listPkgs li a:hover {
239 background-color: #555;
240 color: #fff;
241 }
242 #listPkgs li a.active {
243 background-color: #FFBB4D;
244 color: #000;
245 }
246 #listSearchResults li.selected {408 #listSearchResults li.selected {
247 background-color: #000;409 background-color: #000;
248 }410 }
...@@ -273,112 +435,128 @@...@@ -273,112 +435,128 @@
273 .tok-type {435 .tok-type {
274 color: #68f;436 color: #68f;
275 }437 }
438
276 }439 }
277 </style>440 </style>
278 </head>441 </head>
279 <body>442 <body class="canvas">
280 <nav>443 <div class="flex-main">
281 <img alt="ZIG" id="logo" src="data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxNTAgMTAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxnIGZpbGw9IiNmN2E0MWQiPjxwYXRoIGQ9Im0wIDEwdjgwaDE5bDYtMTAgMTItMTBoLTE3di00MGgxNXYtMjB6bTQwIDB2MjBoNjJ2LTIwem05MSAwLTYgMTAtMTIgMTBoMTd2NDBoLTE1djIwaDM1di04MHptLTgzIDYwdjIwaDYydi0yMHoiIHNoYXBlLXJlbmRlcmluZz0iY3Jpc3BFZGdlcyIvPjxwYXRoIGQ9Im0zNyA3MC0xOCAyMHYtMTV6Ii8+PHBhdGggZD0ibTExMyAzMCAxOC0yMHYxNXoiLz48cGF0aCBkPSJtOTYuOTggMTAuNjMgMzYuMjgtMTAuNC04MC4yOSA4OS4xNy0zNi4yOCAxMC40eiIvPjwvZz48L3N2Zz4K"></img>444 <div class="flex-filler"></div>
282 <div id="sectPkgs" class="hidden">445 <div class="flex-left sidebar">
283 <h2>Packages</h2>446 <nav>
284 <ul id="listPkgs">447 <div class="logo">
285 </ul>448 <svg version="1.1" viewBox="0 0 150 80" xmlns="http://www.w3.org/2000/svg">
286 </div>449 <g fill="#f7a41d">
287 <div id="sectInfo" class="hidden">450 <path d="m0,-0.08899l0,80l19,0l6,-10l12,-10l-17,0l0,-40l15,0l0,-20l-35,0zm40,0l0,20l62,0l0,-20l-62,0zm91,0l-6,10l-12,10l17,0l0,40l-15,0l0,20l35,0l0,-80l-19,0zm-83,60l0,20l62,0l0,-20l-62,0z" shape-rendering="crispEdges"></path>
288 <h2>Zig Version</h2>451 <path d="m37,59.91101l-18,20l0,-15l18,-5z"></path>
289 <p id="tdZigVer"></p>452 <path d="m113,19.91101l18,-20l0,15l-18,5z"></path>
290 <h2>Target</h2>453 <path d="m96.98,0.54101l36.28,-10.4l-80.29,89.17l-36.28,10.4l80.29,-89.17z"></path>
291 <p id="tdTarget"></p>454 </g>
455 </svg>
456 </div>
457 <div id="sectPkgs" class="hidden">
458 <h2><span>Packages</span></h2>
459 <ul id="listPkgs" class="packages"></ul>
460 </div>
461 <div id="sectInfo" class="hidden">
462 <h2><span>Zig Version</span></h2>
463 <p class="str" id="tdZigVer"></p>
464 <h2><span>Target</span></h2>
465 <p class="str" id="tdTarget"></p>
466 </div>
467 </nav>
292 </div>468 </div>
293 </nav>469 <div class="flex-right">
294 <section>470 <div class="wrap">
295 <input type="search" id="search" autocomplete="off" spellcheck="false" placeholder="`s` to search, `?` to see more options">471 <section class="docs">
296 <p id="status">Loading...</p>472 <input type="search" class="search" id="search" autocomplete="off" spellcheck="false" placeholder="`s` to search, `?` to see more options">
297 <div id="sectNav" class="hidden"><ul id="listNav"></ul></div>473 <p id="status">Loading...</p>
298 <div id="fnProto" class="hidden">474 <div id="sectNav" class="hidden"><ul id="listNav"></ul></div>
299 <pre id="fnProtoCode"></pre>475 <div id="fnProto" class="hidden">
300 </div>476 <pre id="fnProtoCode"></pre>
301 <h1 id="hdrName" class="hidden"></h1>477 </div>
302 <div id="fnNoExamples" class="hidden">478 <h1 id="hdrName" class="hidden"></h1>
303 <p>This function is not tested or referenced.</p>479 <div id="fnNoExamples" class="hidden">
304 </div>480 <p>This function is not tested or referenced.</p>
305 <div id="declNoRef" class="hidden">481 </div>
306 <p>482 <div id="declNoRef" class="hidden">
307 This declaration is not tested or referenced, and it has therefore not been included in483 <p>
308 semantic analysis, which means the only documentation available is whatever is in the484 This declaration is not tested or referenced, and it has therefore not been included in
309 doc comments.485 semantic analysis, which means the only documentation available is whatever is in the
310 </p>486 doc comments.
311 </div>487 </p>
312 <div id="fnDocs" class="hidden"></div>488 </div>
313 <div id="sectFnErrors" class="hidden">489 <div id="fnDocs" class="hidden"></div>
314 <h2>Errors</h2>490 <div id="sectFnErrors" class="hidden">
315 <div id="fnErrorsAnyError">491 <h2>Errors</h2>
316 <p><span class="tok-type">anyerror</span> means the error set is known only at runtime.</p>492 <div id="fnErrorsAnyError">
317 </div>493 <p><span class="tok-type">anyerror</span> means the error set is known only at runtime.</p>
318 <div id="tableFnErrors"><dl id="listFnErrors"></dl></div>494 </div>
319 </div>495 <div id="tableFnErrors"><dl id="listFnErrors"></dl></div>
320 <div id="sectSearchResults" class="hidden">496 </div>
321 <h2>Search Results</h2>497 <div id="sectSearchResults" class="hidden">
322 <ul id="listSearchResults"></ul>498 <h2>Search Results</h2>
323 </div>499 <ul id="listSearchResults"></ul>
324 <div id="sectSearchNoResults" class="hidden">500 </div>
325 <h2>No Results Found</h2>501 <div id="sectSearchNoResults" class="hidden">
326 <p>Press escape to exit search and then '?' to see more options.</p>502 <h2>No Results Found</h2>
327 </div>503 <p>Press escape to exit search and then '?' to see more options.</p>
328 <div id="sectFields" class="hidden">504 </div>
329 <h2>Fields</h2>505 <div id="sectFields" class="hidden">
330 <div id="listFields">506 <h2>Fields</h2>
507 <div id="listFields"></div>
508 </div>
509 <div id="sectTypes" class="hidden">
510 <h2>Types</h2>
511 <ul id="listTypes"></ul>
512 </div>
513 <div id="sectNamespaces" class="hidden">
514 <h2>Namespaces</h2>
515 <ul id="listNamespaces"></ul>
516 </div>
517 <div id="sectGlobalVars" class="hidden">
518 <h2>Global Variables</h2>
519 <table>
520 <tbody id="listGlobalVars"></tbody>
521 </table>
522 </div>
523 <div id="sectFns" class="hidden">
524 <h2>Functions</h2>
525 <table>
526 <tbody id="listFns"></tbody>
527 </table>
528 </div>
529 <div id="sectValues" class="hidden">
530 <h2>Values</h2>
531 <table>
532 <tbody id="listValues"></tbody>
533 </table>
534 </div>
535 <div id="sectErrSets" class="hidden">
536 <h2>Error Sets</h2>
537 <ul id="listErrSets"></ul>
538 </div>
539 <div id="fnExamples" class="hidden">
540 <h2>Examples</h2>
541 <ul id="listFnExamples"></ul>
542 </div>
543 </section>
544 </div>
545 <div class="flex-filler"></div>
331 </div>546 </div>
332 </div>547 </div>
333 <div id="sectTypes" class="hidden">
334 <h2>Types</h2>
335 <ul id="listTypes">
336 </ul>
337 </div>
338 <div id="sectNamespaces" class="hidden">
339 <h2>Namespaces</h2>
340 <ul id="listNamespaces">
341 </ul>
342 </div>
343 <div id="sectGlobalVars" class="hidden">
344 <h2>Global Variables</h2>
345 <table>
346 <tbody id="listGlobalVars">
347 </tbody>
348 </table>
349 </div>
350 <div id="sectFns" class="hidden">
351 <h2>Functions</h2>
352 <table>
353 <tbody id="listFns">
354 </tbody>
355 </table>
356 </div>
357 <div id="sectValues" class="hidden">
358 <h2>Values</h2>
359 <table>
360 <tbody id="listValues">
361 </tbody>
362 </table>
363 </div>
364 <div id="sectErrSets" class="hidden">
365 <h2>Error Sets</h2>
366 <ul id="listErrSets">
367 </ul>
368 </div>
369 <div id="fnExamples" class="hidden">
370 <h2>Examples</h2>
371 <ul id="listFnExamples"></ul>
372 </div>
373 </section>
374 <div id="helpDialog" class="hidden">548 <div id="helpDialog" class="hidden">
375 <h1>Keyboard Shortcuts</h1>549 <div class="help-modal">
376 <dl><dt><kbd>?</kbd></dt><dd>Show this help dialog</dd></dl>550 <div class="dialog">
377 <dl><dt><kbd>Esc</kbd></dt><dd>Clear focus; close this dialog</dd></dl>551 <h1>Keyboard Shortcuts</h1>
378 <dl><dt><kbd>s</kbd></dt><dd>Focus the search field</dd></dl>552 <dl><dt><kbd>?</kbd></dt><dd>Show this help dialog</dd></dl>
379 <dl><dt><kbd>↑</kbd></dt><dd>Move up in search results</dd></dl>553 <dl><dt><kbd>Esc</kbd></dt><dd>Clear focus; close this dialog</dd></dl>
380 <dl><dt><kbd>↓</kbd></dt><dd>Move down in search results</dd></dl>554 <dl><dt><kbd>s</kbd></dt><dd>Focus the search field</dd></dl>
381 <dl><dt><kbd>⏎</kbd></dt><dd>Go to active search result</dd></dl>555 <dl><dt><kbd>↑</kbd></dt><dd>Move up in search results</dd></dl>
556 <dl><dt><kbd>↓</kbd></dt><dd>Move down in search results</dd></dl>
557 <dl><dt><kbd>⏎</kbd></dt><dd>Go to active search result</dd></dl>
558 </div>
559 </div>
382 </div>560 </div>
383 <script src="data.js"></script>561 <script src="data.js"></script>
384 <script src="main.js"></script>562 <script src="main.js"></script>
lib/std/special/test_runner.zig+12-3
...@@ -15,20 +15,29 @@ pub fn main() anyerror!void {...@@ -15,20 +15,29 @@ pub fn main() anyerror!void {
15 for (test_fn_list) |test_fn, i| {15 for (test_fn_list) |test_fn, i| {
16 var test_node = root_node.start(test_fn.name, null);16 var test_node = root_node.start(test_fn.name, null);
17 test_node.activate();17 test_node.activate();
18 progress.refresh();
19 if (progress.terminal == null) std.debug.warn("{}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
18 if (test_fn.func()) |_| {20 if (test_fn.func()) |_| {
19 ok_count += 1;21 ok_count += 1;
20 test_node.end();22 test_node.end();
23 if (progress.terminal == null) std.debug.warn("OK\n");
21 } else |err| switch (err) {24 } else |err| switch (err) {
22 error.SkipZigTest => {25 error.SkipZigTest => {
23 skip_count += 1;26 skip_count += 1;
24 test_node.end();27 test_node.end();
25 progress.log("{}...SKIP\n", test_fn.name);28 progress.log("{}...SKIP\n", test_fn.name);
29 if (progress.terminal == null) std.debug.warn("SKIP\n");
30 },
31 else => {
32 progress.log("");
33 return err;
26 },34 },
27 else => return err,
28 }35 }
29 }36 }
30 root_node.end();37 root_node.end();
31 if (ok_count != test_fn_list.len) {38 if (ok_count == test_fn_list.len) {
32 progress.log("{} passed; {} skipped.\n", ok_count, skip_count);39 std.debug.warn("All tests passed.\n");
40 } else {
41 std.debug.warn("{} passed; {} skipped.\n", ok_count, skip_count);
33 }42 }
34}43}
lib/std/zig/ast.zig+1-3
...@@ -290,7 +290,7 @@ pub const Error = union(enum) {...@@ -290,7 +290,7 @@ pub const Error = union(enum) {
290 pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{}'");290 pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{}'");
291291
292 pub const ExpectedParamType = SimpleError("Expected parameter type");292 pub const ExpectedParamType = SimpleError("Expected parameter type");
293 pub const ExpectedPubItem = SimpleError("Pub must be followed by fn decl, var decl, or container member");293 pub const ExpectedPubItem = SimpleError("Expected function or variable declaration after pub");
294 pub const UnattachedDocComment = SimpleError("Unattached documentation comment");294 pub const UnattachedDocComment = SimpleError("Unattached documentation comment");
295 pub const ExtraAlignQualifier = SimpleError("Extra align qualifier");295 pub const ExtraAlignQualifier = SimpleError("Extra align qualifier");
296 pub const ExtraConstQualifier = SimpleError("Extra const qualifier");296 pub const ExtraConstQualifier = SimpleError("Extra const qualifier");
...@@ -757,7 +757,6 @@ pub const Node = struct {...@@ -757,7 +757,6 @@ pub const Node = struct {
757 pub const ContainerField = struct {757 pub const ContainerField = struct {
758 base: Node,758 base: Node,
759 doc_comments: ?*DocComment,759 doc_comments: ?*DocComment,
760 visib_token: ?TokenIndex,
761 name_token: TokenIndex,760 name_token: TokenIndex,
762 type_expr: ?*Node,761 type_expr: ?*Node,
763 value_expr: ?*Node,762 value_expr: ?*Node,
...@@ -780,7 +779,6 @@ pub const Node = struct {...@@ -780,7 +779,6 @@ pub const Node = struct {
780 }779 }
781780
782 pub fn firstToken(self: *const ContainerField) TokenIndex {781 pub fn firstToken(self: *const ContainerField) TokenIndex {
783 if (self.visib_token) |visib_token| return visib_token;
784 return self.name_token;782 return self.name_token;
785 }783 }
786784
lib/std/zig/parse.zig+7-9
...@@ -138,9 +138,15 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No...@@ -138,9 +138,15 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No
138 continue;138 continue;
139 }139 }
140140
141 if (visib_token != null) {
142 try tree.errors.push(AstError{
143 .ExpectedPubItem = AstError.ExpectedPubItem{ .token = it.index },
144 });
145 return error.ParseError;
146 }
147
141 if (try parseContainerField(arena, it, tree)) |node| {148 if (try parseContainerField(arena, it, tree)) |node| {
142 const field = node.cast(Node.ContainerField).?;149 const field = node.cast(Node.ContainerField).?;
143 field.visib_token = visib_token;
144 field.doc_comments = doc_comments;150 field.doc_comments = doc_comments;
145 try list.push(node);151 try list.push(node);
146 const comma = eatToken(it, .Comma) orelse break;152 const comma = eatToken(it, .Comma) orelse break;
...@@ -149,13 +155,6 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No...@@ -149,13 +155,6 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No
149 continue;155 continue;
150 }156 }
151157
152 // Dangling pub
153 if (visib_token != null) {
154 try tree.errors.push(AstError{
155 .ExpectedPubItem = AstError.ExpectedPubItem{ .token = it.index },
156 });
157 }
158
159 break;158 break;
160 }159 }
161160
...@@ -407,7 +406,6 @@ fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No...@@ -407,7 +406,6 @@ fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
407 node.* = Node.ContainerField{406 node.* = Node.ContainerField{
408 .base = Node{ .id = .ContainerField },407 .base = Node{ .id = .ContainerField },
409 .doc_comments = null,408 .doc_comments = null,
410 .visib_token = null,
411 .name_token = name_token,409 .name_token = name_token,
412 .type_expr = type_expr,410 .type_expr = type_expr,
413 .value_expr = value_expr,411 .value_expr = value_expr,
lib/std/zig/parser_test.zig+3-3
...@@ -1766,7 +1766,7 @@ test "zig fmt: struct declaration" {...@@ -1766,7 +1766,7 @@ test "zig fmt: struct declaration" {
1766 \\const S = struct {1766 \\const S = struct {
1767 \\ const Self = @This();1767 \\ const Self = @This();
1768 \\ f1: u8,1768 \\ f1: u8,
1769 \\ pub f3: u8,1769 \\ f3: u8,
1770 \\1770 \\
1771 \\ fn method(self: *Self) Self {1771 \\ fn method(self: *Self) Self {
1772 \\ return self.*;1772 \\ return self.*;
...@@ -1777,14 +1777,14 @@ test "zig fmt: struct declaration" {...@@ -1777,14 +1777,14 @@ test "zig fmt: struct declaration" {
1777 \\1777 \\
1778 \\const Ps = packed struct {1778 \\const Ps = packed struct {
1779 \\ a: u8,1779 \\ a: u8,
1780 \\ pub b: u8,1780 \\ b: u8,
1781 \\1781 \\
1782 \\ c: u8,1782 \\ c: u8,
1783 \\};1783 \\};
1784 \\1784 \\
1785 \\const Es = extern struct {1785 \\const Es = extern struct {
1786 \\ a: u8,1786 \\ a: u8,
1787 \\ pub b: u8,1787 \\ b: u8,
1788 \\1788 \\
1789 \\ c: u8,1789 \\ c: u8,
1790 \\};1790 \\};
lib/std/zig/render.zig+2-6
...@@ -254,10 +254,6 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, i...@@ -254,10 +254,6 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, i
254254
255 try renderDocComments(tree, stream, field, indent, start_col);255 try renderDocComments(tree, stream, field, indent, start_col);
256256
257 if (field.visib_token) |visib_token| {
258 try renderToken(tree, stream, visib_token, indent, start_col, Space.Space); // pub
259 }
260
261 if (field.type_expr == null and field.value_expr == null) {257 if (field.type_expr == null and field.value_expr == null) {
262 return renderToken(tree, stream, field.name_token, indent, start_col, Space.Comma); // name,258 return renderToken(tree, stream, field.name_token, indent, start_col, Space.Comma); // name,
263 } else if (field.type_expr != null and field.value_expr == null) {259 } else if (field.type_expr != null and field.value_expr == null) {
...@@ -2206,8 +2202,8 @@ const FindByteOutStream = struct {...@@ -2206,8 +2202,8 @@ const FindByteOutStream = struct {
2206 pub const Error = error{};2202 pub const Error = error{};
2207 pub const Stream = std.io.OutStream(Error);2203 pub const Stream = std.io.OutStream(Error);
22082204
2209 pub stream: Stream,2205 stream: Stream,
2210 pub byte_found: bool,2206 byte_found: bool,
2211 byte: u8,2207 byte: u8,
22122208
2213 pub fn init(byte: u8) Self {2209 pub fn init(byte: u8) Self {
src-self-hosted/main.zig+1-1
...@@ -747,7 +747,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro...@@ -747,7 +747,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
747 )) catch |err| switch (err) {747 )) catch |err| switch (err) {
748 error.IsDir, error.AccessDenied => {748 error.IsDir, error.AccessDenied => {
749 // TODO make event based (and dir.next())749 // TODO make event based (and dir.next())
750 var dir = try fs.Dir.open(fmt.loop.allocator, file_path);750 var dir = try fs.Dir.open(file_path);
751 defer dir.close();751 defer dir.close();
752752
753 var group = event.Group(FmtError!void).init(fmt.loop);753 var group = event.Group(FmtError!void).init(fmt.loop);
src-self-hosted/stage1.zig+5-3
...@@ -283,11 +283,13 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void...@@ -283,11 +283,13 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void
283 const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) {283 const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) {
284 error.IsDir, error.AccessDenied => {284 error.IsDir, error.AccessDenied => {
285 // TODO make event based (and dir.next())285 // TODO make event based (and dir.next())
286 var dir = try fs.Dir.open(fmt.allocator, file_path);286 var dir = try fs.Dir.open(file_path);
287 defer dir.close();287 defer dir.close();
288288
289 while (try dir.next()) |entry| {289 var dir_it = dir.iterate();
290 if (entry.kind == fs.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {290
291 while (try dir_it.next()) |entry| {
292 if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
291 const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name });293 const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name });
292 try fmtPath(fmt, full_path, check_mode);294 try fmtPath(fmt, full_path, check_mode);
293 }295 }
src-self-hosted/test.zig+2-2
...@@ -56,11 +56,11 @@ pub const TestContext = struct {...@@ -56,11 +56,11 @@ pub const TestContext = struct {
56 errdefer allocator.free(self.zig_lib_dir);56 errdefer allocator.free(self.zig_lib_dir);
5757
58 try std.fs.makePath(allocator, tmp_dir_name);58 try std.fs.makePath(allocator, tmp_dir_name);
59 errdefer std.fs.deleteTree(allocator, tmp_dir_name) catch {};59 errdefer std.fs.deleteTree(tmp_dir_name) catch {};
60 }60 }
6161
62 fn deinit(self: *TestContext) void {62 fn deinit(self: *TestContext) void {
63 std.fs.deleteTree(allocator, tmp_dir_name) catch {};63 std.fs.deleteTree(tmp_dir_name) catch {};
64 allocator.free(self.zig_lib_dir);64 allocator.free(self.zig_lib_dir);
65 self.zig_compiler.deinit();65 self.zig_compiler.deinit();
66 self.loop.deinit();66 self.loop.deinit();
src/all_types.hpp+3-5
...@@ -990,8 +990,6 @@ struct AstNodeStructField {...@@ -990,8 +990,6 @@ struct AstNodeStructField {
990 // populated if the "align(A)" is present990 // populated if the "align(A)" is present
991 AstNode *align_expr;991 AstNode *align_expr;
992 Buf doc_comments;992 Buf doc_comments;
993
994 VisibMod visib_mod;
995};993};
996994
997struct AstNodeStringLiteral {995struct AstNodeStringLiteral {
...@@ -2569,12 +2567,12 @@ enum IrInstructionId {...@@ -2569,12 +2567,12 @@ enum IrInstructionId {
2569struct IrInstruction {2567struct IrInstruction {
2570 Scope *scope;2568 Scope *scope;
2571 AstNode *source_node;2569 AstNode *source_node;
2572 ConstExprValue value;
2573 size_t debug_id;
2574 LLVMValueRef llvm_value;2570 LLVMValueRef llvm_value;
2571 ConstExprValue value;
2572 uint32_t debug_id;
2575 // if ref_count is zero and the instruction has no side effects,2573 // if ref_count is zero and the instruction has no side effects,
2576 // the instruction can be omitted in codegen2574 // the instruction can be omitted in codegen
2577 size_t ref_count;2575 uint32_t ref_count;
2578 // When analyzing IR, instructions that point to this instruction in the "old ir"2576 // When analyzing IR, instructions that point to this instruction in the "old ir"
2579 // can find the instruction that corresponds to this value in the "new ir"2577 // can find the instruction that corresponds to this value in the "new ir"
2580 // with this child field.2578 // with this child field.
src/analyze.cpp+2-2
...@@ -5783,8 +5783,8 @@ ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_...@@ -5783,8 +5783,8 @@ ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_
57835783
57845784
5785ConstExprValue *create_const_vals(size_t count) {5785ConstExprValue *create_const_vals(size_t count) {
5786 ConstGlobalRefs *global_refs = allocate<ConstGlobalRefs>(count);5786 ConstGlobalRefs *global_refs = allocate<ConstGlobalRefs>(count, "ConstGlobalRefs");
5787 ConstExprValue *vals = allocate<ConstExprValue>(count);5787 ConstExprValue *vals = allocate<ConstExprValue>(count, "ConstExprValue");
5788 for (size_t i = 0; i < count; i += 1) {5788 for (size_t i = 0; i < count; i += 1) {
5789 vals[i].global_refs = &global_refs[i];5789 vals[i].global_refs = &global_refs[i];
5790 }5790 }
src/codegen.cpp+7-2
...@@ -6355,12 +6355,17 @@ static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *un...@@ -6355,12 +6355,17 @@ static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *un
6355 ConstParent *parent = &union_const_val->parent;6355 ConstParent *parent = &union_const_val->parent;
6356 LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent);6356 LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent);
63576357
6358 // Slot in the structure where the payload is stored, if equal to SIZE_MAX
6359 // the union has no tag and a single field and is collapsed into the field
6360 // itself
6361 size_t union_payload_index = union_const_val->type->data.unionation.gen_union_index;
6362
6358 ZigType *u32 = g->builtin_types.entry_u32;6363 ZigType *u32 = g->builtin_types.entry_u32;
6359 LLVMValueRef indices[] = {6364 LLVMValueRef indices[] = {
6360 LLVMConstNull(get_llvm_type(g, u32)),6365 LLVMConstNull(get_llvm_type(g, u32)),
6361 LLVMConstInt(get_llvm_type(g, u32), 0, false), // TODO test const union with more aligned tag type than payload6366 LLVMConstInt(get_llvm_type(g, u32), union_payload_index, false),
6362 };6367 };
6363 return LLVMConstInBoundsGEP(base_ptr, indices, 2);6368 return LLVMConstInBoundsGEP(base_ptr, indices, (union_payload_index != SIZE_MAX) ? 2 : 1);
6364}6369}
63656370
6366static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ConstExprValue *const_val) {6371static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ConstExprValue *const_val) {
src/config.h.in+2-3
...@@ -13,9 +13,6 @@...@@ -13,9 +13,6 @@
13#define ZIG_VERSION_PATCH @ZIG_VERSION_PATCH@13#define ZIG_VERSION_PATCH @ZIG_VERSION_PATCH@
14#define ZIG_VERSION_STRING "@ZIG_VERSION@"14#define ZIG_VERSION_STRING "@ZIG_VERSION@"
1515
16// Only used for running tests before installing.
17#define ZIG_TEST_DIR "@CMAKE_SOURCE_DIR@/test"
18
19// Used for communicating build information to self hosted build.16// Used for communicating build information to self hosted build.
20#define ZIG_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@"17#define ZIG_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@"
21#define ZIG_CXX_COMPILER "@CMAKE_CXX_COMPILER@"18#define ZIG_CXX_COMPILER "@CMAKE_CXX_COMPILER@"
...@@ -24,4 +21,6 @@...@@ -24,4 +21,6 @@
24#define ZIG_LLVM_CONFIG_EXE "@LLVM_CONFIG_EXE@"21#define ZIG_LLVM_CONFIG_EXE "@LLVM_CONFIG_EXE@"
25#define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@"22#define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@"
2623
24#cmakedefine ZIG_ENABLE_MEM_PROFILE
25
27#endif26#endif
src/dump_analysis.cpp+1-18
...@@ -240,23 +240,6 @@ static void jw_string(JsonWriter *jw, const char *s) {...@@ -240,23 +240,6 @@ static void jw_string(JsonWriter *jw, const char *s) {
240240
241static void tree_print(FILE *f, ZigType *ty, size_t indent);241static void tree_print(FILE *f, ZigType *ty, size_t indent);
242242
243static void pretty_print_bytes(FILE *f, double n) {
244 if (n > 1024.0 * 1024.0 * 1024.0) {
245 fprintf(f, "%.02f GiB", n / 1024.0 / 1024.0 / 1024.0);
246 return;
247 }
248 if (n > 1024.0 * 1024.0) {
249 fprintf(f, "%.02f MiB", n / 1024.0 / 1024.0);
250 return;
251 }
252 if (n > 1024.0) {
253 fprintf(f, "%.02f KiB", n / 1024.0);
254 return;
255 }
256 fprintf(f, "%.02f bytes", n );
257 return;
258}
259
260static int compare_type_abi_sizes_desc(const void *a, const void *b) {243static int compare_type_abi_sizes_desc(const void *a, const void *b) {
261 uint64_t size_a = (*(ZigType * const*)(a))->abi_size;244 uint64_t size_a = (*(ZigType * const*)(a))->abi_size;
262 uint64_t size_b = (*(ZigType * const*)(b))->abi_size;245 uint64_t size_b = (*(ZigType * const*)(b))->abi_size;
...@@ -322,7 +305,7 @@ static void tree_print(FILE *f, ZigType *ty, size_t indent) {...@@ -322,7 +305,7 @@ static void tree_print(FILE *f, ZigType *ty, size_t indent) {
322305
323 start_peer(f, indent);306 start_peer(f, indent);
324 fprintf(f, "\"sizef\": \"");307 fprintf(f, "\"sizef\": \"");
325 pretty_print_bytes(f, ty->abi_size);308 zig_pretty_print_bytes(f, ty->abi_size);
326 fprintf(f, "\"");309 fprintf(f, "\"");
327310
328 start_peer(f, indent);311 start_peer(f, indent);
src/ir.cpp+58-30
...@@ -413,7 +413,7 @@ ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) {...@@ -413,7 +413,7 @@ ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) {
413}413}
414414
415static IrBasicBlock *ir_create_basic_block(IrBuilder *irb, Scope *scope, const char *name_hint) {415static IrBasicBlock *ir_create_basic_block(IrBuilder *irb, Scope *scope, const char *name_hint) {
416 IrBasicBlock *result = allocate<IrBasicBlock>(1);416 IrBasicBlock *result = allocate<IrBasicBlock>(1, "IrBasicBlock");
417 result->scope = scope;417 result->scope = scope;
418 result->name_hint = name_hint;418 result->name_hint = name_hint;
419 result->debug_id = exec_next_debug_id(irb->exec);419 result->debug_id = exec_next_debug_id(irb->exec);
...@@ -1085,13 +1085,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSpillEnd *) {...@@ -1085,13 +1085,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSpillEnd *) {
10851085
1086template<typename T>1086template<typename T>
1087static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {1087static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
1088 T *special_instruction = allocate<T>(1);1088 const char *name = nullptr;
1089#ifdef ZIG_ENABLE_MEM_PROFILE
1090 T *dummy = nullptr;
1091 name = ir_instruction_type_str(ir_instruction_id(dummy));
1092#endif
1093 T *special_instruction = allocate<T>(1, name);
1089 special_instruction->base.id = ir_instruction_id(special_instruction);1094 special_instruction->base.id = ir_instruction_id(special_instruction);
1090 special_instruction->base.scope = scope;1095 special_instruction->base.scope = scope;
1091 special_instruction->base.source_node = source_node;1096 special_instruction->base.source_node = source_node;
1092 special_instruction->base.debug_id = exec_next_debug_id(irb->exec);1097 special_instruction->base.debug_id = exec_next_debug_id(irb->exec);
1093 special_instruction->base.owner_bb = irb->current_basic_block;1098 special_instruction->base.owner_bb = irb->current_basic_block;
1094 special_instruction->base.value.global_refs = allocate<ConstGlobalRefs>(1);1099 special_instruction->base.value.global_refs = allocate<ConstGlobalRefs>(1, "ConstGlobalRefs");
1095 return special_instruction;1100 return special_instruction;
1096}1101}
10971102
...@@ -3569,7 +3574,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,...@@ -3569,7 +3574,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
3569 switch (node->data.return_expr.kind) {3574 switch (node->data.return_expr.kind) {
3570 case ReturnKindUnconditional:3575 case ReturnKindUnconditional:
3571 {3576 {
3572 ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1);3577 ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn");
3573 result_loc_ret->base.id = ResultLocIdReturn;3578 result_loc_ret->base.id = ResultLocIdReturn;
3574 ir_build_reset_result(irb, scope, node, &result_loc_ret->base);3579 ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
35753580
...@@ -3664,7 +3669,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,...@@ -3664,7 +3669,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
3664 ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val, nullptr));3669 ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val, nullptr));
3665 IrInstructionSpillBegin *spill_begin = ir_build_spill_begin(irb, scope, node, err_val,3670 IrInstructionSpillBegin *spill_begin = ir_build_spill_begin(irb, scope, node, err_val,
3666 SpillIdRetErrCode);3671 SpillIdRetErrCode);
3667 ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1);3672 ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn");
3668 result_loc_ret->base.id = ResultLocIdReturn;3673 result_loc_ret->base.id = ResultLocIdReturn;
3669 ir_build_reset_result(irb, scope, node, &result_loc_ret->base);3674 ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
3670 ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base);3675 ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base);
...@@ -3692,7 +3697,7 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s...@@ -3692,7 +3697,7 @@ static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_s
3692 Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime,3697 Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime,
3693 bool skip_name_check)3698 bool skip_name_check)
3694{3699{
3695 ZigVar *variable_entry = allocate<ZigVar>(1);3700 ZigVar *variable_entry = allocate<ZigVar>(1, "ZigVar");
3696 variable_entry->parent_scope = parent_scope;3701 variable_entry->parent_scope = parent_scope;
3697 variable_entry->shadowable = is_shadowable;3702 variable_entry->shadowable = is_shadowable;
3698 variable_entry->mem_slot_index = SIZE_MAX;3703 variable_entry->mem_slot_index = SIZE_MAX;
...@@ -3767,7 +3772,7 @@ static ZigVar *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *n...@@ -3767,7 +3772,7 @@ static ZigVar *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *n
3767}3772}
37683773
3769static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) {3774static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) {
3770 ResultLocPeer *result = allocate<ResultLocPeer>(1);3775 ResultLocPeer *result = allocate<ResultLocPeer>(1, "ResultLocPeer");
3771 result->base.id = ResultLocIdPeer;3776 result->base.id = ResultLocIdPeer;
3772 result->base.source_instruction = peer_parent->base.source_instruction;3777 result->base.source_instruction = peer_parent->base.source_instruction;
3773 result->parent = peer_parent;3778 result->parent = peer_parent;
...@@ -3806,7 +3811,7 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode...@@ -3806,7 +3811,7 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
3806 scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node,3811 scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node,
3807 ir_should_inline(irb->exec, parent_scope));3812 ir_should_inline(irb->exec, parent_scope));
38083813
3809 scope_block->peer_parent = allocate<ResultLocPeerParent>(1);3814 scope_block->peer_parent = allocate<ResultLocPeerParent>(1, "ResultLocPeerParent");
3810 scope_block->peer_parent->base.id = ResultLocIdPeerParent;3815 scope_block->peer_parent->base.id = ResultLocIdPeerParent;
3811 scope_block->peer_parent->base.source_instruction = scope_block->is_comptime;3816 scope_block->peer_parent->base.source_instruction = scope_block->is_comptime;
3812 scope_block->peer_parent->end_bb = scope_block->end_block;3817 scope_block->peer_parent->end_bb = scope_block->end_block;
...@@ -3933,7 +3938,7 @@ static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node)...@@ -3933,7 +3938,7 @@ static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node)
3933 if (lvalue == irb->codegen->invalid_instruction)3938 if (lvalue == irb->codegen->invalid_instruction)
3934 return irb->codegen->invalid_instruction;3939 return irb->codegen->invalid_instruction;
39353940
3936 ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);3941 ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1, "ResultLocInstruction");
3937 result_loc_inst->base.id = ResultLocIdInstruction;3942 result_loc_inst->base.id = ResultLocIdInstruction;
3938 result_loc_inst->base.source_instruction = lvalue;3943 result_loc_inst->base.source_instruction = lvalue;
3939 ir_ref_instruction(lvalue, irb->current_basic_block);3944 ir_ref_instruction(lvalue, irb->current_basic_block);
...@@ -4005,10 +4010,10 @@ static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node...@@ -4005,10 +4010,10 @@ static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node
40054010
4006 ir_set_cursor_at_end_and_append_block(irb, true_block);4011 ir_set_cursor_at_end_and_append_block(irb, true_block);
40074012
4008 IrInstruction **incoming_values = allocate<IrInstruction *>(2);4013 IrInstruction **incoming_values = allocate<IrInstruction *>(2, "IrInstruction *");
4009 incoming_values[0] = val1;4014 incoming_values[0] = val1;
4010 incoming_values[1] = val2;4015 incoming_values[1] = val2;
4011 IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);4016 IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
4012 incoming_blocks[0] = post_val1_block;4017 incoming_blocks[0] = post_val1_block;
4013 incoming_blocks[1] = post_val2_block;4018 incoming_blocks[1] = post_val2_block;
40144019
...@@ -8017,7 +8022,8 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A...@@ -8017,7 +8022,8 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A
8017 err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size;8022 err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size;
8018 err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count);8023 err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count);
80198024
8020 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(irb->codegen->errors_by_index.length + err_count);8025 size_t errors_count = irb->codegen->errors_by_index.length + err_count;
8026 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
80218027
8022 for (uint32_t i = 0; i < err_count; i += 1) {8028 for (uint32_t i = 0; i < err_count; i += 1) {
8023 AstNode *field_node = node->data.err_set_decl.decls.at(i);8029 AstNode *field_node = node->data.err_set_decl.decls.at(i);
...@@ -8048,7 +8054,7 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A...@@ -8048,7 +8054,7 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A
8048 }8054 }
8049 errors[err->value] = err;8055 errors[err->value] = err;
8050 }8056 }
8051 free(errors);8057 deallocate(errors, errors_count, "ErrorTableEntry *");
8052 return ir_build_const_type(irb, parent_scope, node, err_set_type);8058 return ir_build_const_type(irb, parent_scope, node, err_set_type);
8053}8059}
80548060
...@@ -9574,7 +9580,8 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp...@@ -9574,7 +9580,8 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp
9574 if (type_is_global_error_set(set2)) {9580 if (type_is_global_error_set(set2)) {
9575 return set1;9581 return set1;
9576 }9582 }
9577 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);9583 size_t errors_count = ira->codegen->errors_by_index.length;
9584 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
9578 populate_error_set_table(errors, set1);9585 populate_error_set_table(errors, set1);
9579 ZigList<ErrorTableEntry *> intersection_list = {};9586 ZigList<ErrorTableEntry *> intersection_list = {};
95809587
...@@ -9595,7 +9602,7 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp...@@ -9595,7 +9602,7 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp
9595 buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&existing_entry_with_docs->name));9602 buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&existing_entry_with_docs->name));
9596 }9603 }
9597 }9604 }
9598 free(errors);9605 deallocate(errors, errors_count, "ErrorTableEntry *");
95999606
9600 err_set_type->data.error_set.err_count = intersection_list.length;9607 err_set_type->data.error_set.err_count = intersection_list.length;
9601 err_set_type->data.error_set.errors = intersection_list.items;9608 err_set_type->data.error_set.errors = intersection_list.items;
...@@ -9792,7 +9799,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted...@@ -9792,7 +9799,8 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
9792 return result;9799 return result;
9793 }9800 }
97949801
9795 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(g->errors_by_index.length);9802 size_t errors_count = g->errors_by_index.length;
9803 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
9796 for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) {9804 for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) {
9797 ErrorTableEntry *error_entry = container_set->data.error_set.errors[i];9805 ErrorTableEntry *error_entry = container_set->data.error_set.errors[i];
9798 assert(errors[error_entry->value] == nullptr);9806 assert(errors[error_entry->value] == nullptr);
...@@ -9809,7 +9817,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted...@@ -9809,7 +9817,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
9809 result.data.error_set_mismatch->missing_errors.append(contained_error_entry);9817 result.data.error_set_mismatch->missing_errors.append(contained_error_entry);
9810 }9818 }
9811 }9819 }
9812 free(errors);9820 deallocate(errors, errors_count, "ErrorTableEntry *");
9813 return result;9821 return result;
9814 }9822 }
98159823
...@@ -10112,6 +10120,18 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT...@@ -10112,6 +10120,18 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
10112 } else {10120 } else {
10113 err_set_type = cur_type;10121 err_set_type = cur_type;
10114 }10122 }
10123
10124 if (!resolve_inferred_error_set(ira->codegen, err_set_type, cur_inst->source_node)) {
10125 return ira->codegen->builtin_types.entry_invalid;
10126 }
10127
10128 if (type_is_global_error_set(err_set_type)) {
10129 err_set_type = ira->codegen->builtin_types.entry_global_error_set;
10130 continue;
10131 }
10132
10133 update_errors_helper(ira->codegen, &errors, &errors_count);
10134
10115 for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {10135 for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
10116 ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];10136 ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
10117 assert(errors[error_entry->value] == nullptr);10137 assert(errors[error_entry->value] == nullptr);
...@@ -10814,7 +10834,8 @@ static IrInstruction *ira_suspend(IrAnalyze *ira, IrInstruction *old_instruction...@@ -10814,7 +10834,8 @@ static IrInstruction *ira_suspend(IrAnalyze *ira, IrInstruction *old_instruction
10814 IrSuspendPosition *suspend_pos)10834 IrSuspendPosition *suspend_pos)
10815{10835{
10816 if (ira->codegen->verbose_ir) {10836 if (ira->codegen->verbose_ir) {
10817 fprintf(stderr, "suspend %s_%zu %s_%zu #%zu (%zu,%zu)\n", ira->old_irb.current_basic_block->name_hint,10837 fprintf(stderr, "suspend %s_%zu %s_%zu #%" PRIu32 " (%zu,%zu)\n",
10838 ira->old_irb.current_basic_block->name_hint,
10818 ira->old_irb.current_basic_block->debug_id,10839 ira->old_irb.current_basic_block->debug_id,
10819 ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->name_hint,10840 ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->name_hint,
10820 ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->debug_id,10841 ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->debug_id,
...@@ -10852,7 +10873,7 @@ static IrInstruction *ira_resume(IrAnalyze *ira) {...@@ -10852,7 +10873,7 @@ static IrInstruction *ira_resume(IrAnalyze *ira) {
10852 ira->instruction_index = pos.instruction_index;10873 ira->instruction_index = pos.instruction_index;
10853 assert(pos.instruction_index < ira->old_irb.current_basic_block->instruction_list.length);10874 assert(pos.instruction_index < ira->old_irb.current_basic_block->instruction_list.length);
10854 if (ira->codegen->verbose_ir) {10875 if (ira->codegen->verbose_ir) {
10855 fprintf(stderr, "%s_%zu #%zu\n", ira->old_irb.current_basic_block->name_hint,10876 fprintf(stderr, "%s_%zu #%" PRIu32 "\n", ira->old_irb.current_basic_block->name_hint,
10856 ira->old_irb.current_basic_block->debug_id,10877 ira->old_irb.current_basic_block->debug_id,
10857 ira->old_irb.current_basic_block->instruction_list.at(pos.instruction_index)->debug_id);10878 ira->old_irb.current_basic_block->instruction_list.at(pos.instruction_index)->debug_id);
10858 }10879 }
...@@ -14686,14 +14707,15 @@ static IrInstruction *ir_analyze_instruction_merge_err_sets(IrAnalyze *ira,...@@ -14686,14 +14707,15 @@ static IrInstruction *ir_analyze_instruction_merge_err_sets(IrAnalyze *ira,
14686 return ira->codegen->invalid_instruction;14707 return ira->codegen->invalid_instruction;
14687 }14708 }
1468814709
14689 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);14710 size_t errors_count = ira->codegen->errors_by_index.length;
14711 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(errors_count, "ErrorTableEntry *");
14690 for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) {14712 for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) {
14691 ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i];14713 ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i];
14692 assert(errors[error_entry->value] == nullptr);14714 assert(errors[error_entry->value] == nullptr);
14693 errors[error_entry->value] = error_entry;14715 errors[error_entry->value] = error_entry;
14694 }14716 }
14695 ZigType *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type, instruction->type_name);14717 ZigType *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type, instruction->type_name);
14696 free(errors);14718 deallocate(errors, errors_count, "ErrorTableEntry *");
1469714719
14698 return ir_const_type(ira, &instruction->base, result_type);14720 return ir_const_type(ira, &instruction->base, result_type);
14699}14721}
...@@ -19241,7 +19263,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira,...@@ -19241,7 +19263,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira,
1924119263
19242 ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type;19264 ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type;
19243 ConstExprValue *pointee_val = nullptr;19265 ConstExprValue *pointee_val = nullptr;
19244 if (instr_is_comptime(target_value_ptr)) {19266 if (instr_is_comptime(target_value_ptr) && target_value_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) {
19245 pointee_val = const_ptr_pointee(ira, ira->codegen, &target_value_ptr->value, target_value_ptr->source_node);19267 pointee_val = const_ptr_pointee(ira, ira->codegen, &target_value_ptr->value, target_value_ptr->source_node);
19246 if (pointee_val == nullptr)19268 if (pointee_val == nullptr)
19247 return ira->codegen->invalid_instruction;19269 return ira->codegen->invalid_instruction;
...@@ -23074,17 +23096,22 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction...@@ -23074,17 +23096,22 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction
23074 zig_unreachable();23096 zig_unreachable();
23075 }23097 }
2307623098
23077 uint64_t start_scalar = bigint_as_u64(&casted_start->value.data.x_bigint);23099 ConstExprValue *start_val = ir_resolve_const(ira, casted_start, UndefBad);
23100 if (!start_val)
23101 return ira->codegen->invalid_instruction;
23102
23103 uint64_t start_scalar = bigint_as_u64(&start_val->data.x_bigint);
23078 if (!ptr_is_undef && start_scalar > rel_end) {23104 if (!ptr_is_undef && start_scalar > rel_end) {
23079 ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));23105 ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
23080 return ira->codegen->invalid_instruction;23106 return ira->codegen->invalid_instruction;
23081 }23107 }
2308223108
23083 uint64_t end_scalar;23109 uint64_t end_scalar = rel_end;
23084 if (end) {23110 if (end) {
23085 end_scalar = bigint_as_u64(&end->value.data.x_bigint);23111 ConstExprValue *end_val = ir_resolve_const(ira, end, UndefBad);
23086 } else {23112 if (!end_val)
23087 end_scalar = rel_end;23113 return ira->codegen->invalid_instruction;
23114 end_scalar = bigint_as_u64(&end_val->data.x_bigint);
23088 }23115 }
23089 if (!ptr_is_undef) {23116 if (!ptr_is_undef) {
23090 if (end_scalar > rel_end) {23117 if (end_scalar > rel_end) {
...@@ -24034,7 +24061,8 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -24034,7 +24061,8 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
24034 return ira->codegen->invalid_instruction;24061 return ira->codegen->invalid_instruction;
24035 }24062 }
2403624063
24037 AstNode **field_prev_uses = allocate<AstNode *>(ira->codegen->errors_by_index.length);24064 size_t field_prev_uses_count = ira->codegen->errors_by_index.length;
24065 AstNode **field_prev_uses = allocate<AstNode *>(field_prev_uses_count, "AstNode *");
2403824066
24039 for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {24067 for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
24040 IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i];24068 IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i];
...@@ -24091,7 +24119,7 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,...@@ -24091,7 +24119,7 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
24091 }24119 }
24092 }24120 }
2409324121
24094 free(field_prev_uses);24122 deallocate(field_prev_uses, field_prev_uses_count, "AstNode *");
24095 } else if (switch_type->id == ZigTypeIdInt) {24123 } else if (switch_type->id == ZigTypeIdInt) {
24096 RangeSet rs = {0};24124 RangeSet rs = {0};
24097 for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {24125 for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
...@@ -26318,7 +26346,7 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_...@@ -26318,7 +26346,7 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_
26318 }26346 }
2631926347
26320 if (ira->codegen->verbose_ir) {26348 if (ira->codegen->verbose_ir) {
26321 fprintf(stderr, "analyze #%zu\n", old_instruction->debug_id);26349 fprintf(stderr, "analyze #%" PRIu32 "\n", old_instruction->debug_id);
26322 }26350 }
26323 IrInstruction *new_instruction = ir_analyze_instruction_base(ira, old_instruction);26351 IrInstruction *new_instruction = ir_analyze_instruction_base(ira, old_instruction);
26324 if (new_instruction != nullptr) {26352 if (new_instruction != nullptr) {
src/ir_print.cpp+6-6
...@@ -38,8 +38,8 @@ struct IrPrint {...@@ -38,8 +38,8 @@ struct IrPrint {
3838
39static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction);39static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction);
4040
41static const char* ir_instruction_type_str(IrInstruction* instruction) {41const char* ir_instruction_type_str(IrInstructionId id) {
42 switch (instruction->id) {42 switch (id) {
43 case IrInstructionIdInvalid:43 case IrInstructionIdInvalid:
44 return "Invalid";44 return "Invalid";
45 case IrInstructionIdShuffleVector:45 case IrInstructionIdShuffleVector:
...@@ -385,9 +385,9 @@ static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction, bool trail...@@ -385,9 +385,9 @@ static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction, bool trail
385 const char mark = trailing ? ':' : '#';385 const char mark = trailing ? ':' : '#';
386 const char *type_name = instruction->value.type ? buf_ptr(&instruction->value.type->name) : "(unknown)";386 const char *type_name = instruction->value.type ? buf_ptr(&instruction->value.type->name) : "(unknown)";
387 const char *ref_count = ir_has_side_effects(instruction) ?387 const char *ref_count = ir_has_side_effects(instruction) ?
388 "-" : buf_ptr(buf_sprintf("%" ZIG_PRI_usize "", instruction->ref_count));388 "-" : buf_ptr(buf_sprintf("%" PRIu32 "", instruction->ref_count));
389 fprintf(irp->f, "%c%-3zu| %-22s| %-12s| %-2s| ", mark, instruction->debug_id,389 fprintf(irp->f, "%c%-3" PRIu32 "| %-22s| %-12s| %-2s| ", mark, instruction->debug_id,
390 ir_instruction_type_str(instruction), type_name, ref_count);390 ir_instruction_type_str(instruction->id), type_name, ref_count);
391}391}
392392
393static void ir_print_const_value(IrPrint *irp, ConstExprValue *const_val) {393static void ir_print_const_value(IrPrint *irp, ConstExprValue *const_val) {
...@@ -398,7 +398,7 @@ static void ir_print_const_value(IrPrint *irp, ConstExprValue *const_val) {...@@ -398,7 +398,7 @@ static void ir_print_const_value(IrPrint *irp, ConstExprValue *const_val) {
398}398}
399399
400static void ir_print_var_instruction(IrPrint *irp, IrInstruction *instruction) {400static void ir_print_var_instruction(IrPrint *irp, IrInstruction *instruction) {
401 fprintf(irp->f, "#%" ZIG_PRI_usize "", instruction->debug_id);401 fprintf(irp->f, "#%" PRIu32 "", instruction->debug_id);
402 if (irp->pass != IrPassSrc && irp->printed.maybe_get(instruction) == nullptr) {402 if (irp->pass != IrPassSrc && irp->printed.maybe_get(instruction) == nullptr) {
403 irp->printed.put(instruction, 0);403 irp->printed.put(instruction, 0);
404 irp->pending.append(instruction);404 irp->pending.append(instruction);
src/ir_print.hpp+2
...@@ -15,4 +15,6 @@...@@ -15,4 +15,6 @@
15void ir_print(CodeGen *codegen, FILE *f, IrExecutable *executable, int indent_size, IrPass pass);15void ir_print(CodeGen *codegen, FILE *f, IrExecutable *executable, int indent_size, IrPass pass);
16void ir_print_instruction(CodeGen *codegen, FILE *f, IrInstruction *instruction, int indent_size, IrPass pass);16void ir_print_instruction(CodeGen *codegen, FILE *f, IrInstruction *instruction, int indent_size, IrPass pass);
1717
18const char* ir_instruction_type_str(IrInstructionId id);
19
18#endif20#endif
src/main.cpp+52-23
...@@ -64,6 +64,9 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {...@@ -64,6 +64,9 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
64 " -fno-PIC disable Position Independent Code\n"64 " -fno-PIC disable Position Independent Code\n"
65 " -ftime-report print timing diagnostics\n"65 " -ftime-report print timing diagnostics\n"
66 " -fstack-report print stack size diagnostics\n"66 " -fstack-report print stack size diagnostics\n"
67#ifdef ZIG_ENABLE_MEM_PROFILE
68 " -fmem-report print memory usage diagnostics\n"
69#endif
67 " -fdump-analysis write analysis.json file with type information\n"70 " -fdump-analysis write analysis.json file with type information\n"
68 " -femit-docs create a docs/ dir with html documentation\n"71 " -femit-docs create a docs/ dir with html documentation\n"
69 " -fno-emit-bin skip emitting machine code\n"72 " -fno-emit-bin skip emitting machine code\n"
...@@ -306,9 +309,29 @@ static int zig_error_no_build_file(void) {...@@ -306,9 +309,29 @@ static int zig_error_no_build_file(void) {
306309
307extern "C" int ZigClang_main(int argc, char **argv);310extern "C" int ZigClang_main(int argc, char **argv);
308311
312#ifdef ZIG_ENABLE_MEM_PROFILE
313bool mem_report = false;
314#endif
315
316int main_exit(Stage2ProgressNode *root_progress_node, int exit_code) {
317 if (root_progress_node != nullptr) {
318 stage2_progress_end(root_progress_node);
319 }
320#ifdef ZIG_ENABLE_MEM_PROFILE
321 if (mem_report) {
322 memprof_dump_stats(stderr);
323 }
324#endif
325 return exit_code;
326}
327
309int main(int argc, char **argv) {328int main(int argc, char **argv) {
310 stage2_attach_segfault_handler();329 stage2_attach_segfault_handler();
311330
331#ifdef ZIG_ENABLE_MEM_PROFILE
332 memprof_init();
333#endif
334
312 char *arg0 = argv[0];335 char *arg0 = argv[0];
313 Error err;336 Error err;
314337
...@@ -670,6 +693,13 @@ int main(int argc, char **argv) {...@@ -670,6 +693,13 @@ int main(int argc, char **argv) {
670 timing_info = true;693 timing_info = true;
671 } else if (strcmp(arg, "-fstack-report") == 0) {694 } else if (strcmp(arg, "-fstack-report") == 0) {
672 stack_report = true;695 stack_report = true;
696 } else if (strcmp(arg, "-fmem-report") == 0) {
697#ifdef ZIG_ENABLE_MEM_PROFILE
698 mem_report = true;
699#else
700 fprintf(stderr, "-fmem-report requires configuring with -DZIG_ENABLE_MEM_PROFILE=ON\n");
701 return print_error_usage(arg0);
702#endif
673 } else if (strcmp(arg, "-fdump-analysis") == 0) {703 } else if (strcmp(arg, "-fdump-analysis") == 0) {
674 enable_dump_analysis = true;704 enable_dump_analysis = true;
675 } else if (strcmp(arg, "-femit-docs") == 0) {705 } else if (strcmp(arg, "-femit-docs") == 0) {
...@@ -1038,16 +1068,14 @@ int main(int argc, char **argv) {...@@ -1038,16 +1068,14 @@ int main(int argc, char **argv) {
1038 if (in_file) {1068 if (in_file) {
1039 ZigLibCInstallation libc;1069 ZigLibCInstallation libc;
1040 if ((err = zig_libc_parse(&libc, buf_create_from_str(in_file), &target, true)))1070 if ((err = zig_libc_parse(&libc, buf_create_from_str(in_file), &target, true)))
1041 return EXIT_FAILURE;1071 return main_exit(root_progress_node, EXIT_FAILURE);
1042 stage2_progress_end(root_progress_node);1072 return main_exit(root_progress_node, EXIT_SUCCESS);
1043 return EXIT_SUCCESS;
1044 }1073 }
1045 ZigLibCInstallation libc;1074 ZigLibCInstallation libc;
1046 if ((err = zig_libc_find_native(&libc, true)))1075 if ((err = zig_libc_find_native(&libc, true)))
1047 return EXIT_FAILURE;1076 return main_exit(root_progress_node, EXIT_FAILURE);
1048 zig_libc_render(&libc, stdout);1077 zig_libc_render(&libc, stdout);
1049 stage2_progress_end(root_progress_node);1078 return main_exit(root_progress_node, EXIT_SUCCESS);
1050 return EXIT_SUCCESS;
1051 }1079 }
1052 case CmdBuiltin: {1080 case CmdBuiltin: {
1053 CodeGen *g = codegen_create(main_pkg_path, nullptr, &target,1081 CodeGen *g = codegen_create(main_pkg_path, nullptr, &target,
...@@ -1065,10 +1093,9 @@ int main(int argc, char **argv) {...@@ -1065,10 +1093,9 @@ int main(int argc, char **argv) {
1065 Buf *builtin_source = codegen_generate_builtin_source(g);1093 Buf *builtin_source = codegen_generate_builtin_source(g);
1066 if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {1094 if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
1067 fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout)));1095 fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout)));
1068 return EXIT_FAILURE;1096 return main_exit(root_progress_node, EXIT_FAILURE);
1069 }1097 }
1070 stage2_progress_end(root_progress_node);1098 return main_exit(root_progress_node, EXIT_SUCCESS);
1071 return EXIT_SUCCESS;
1072 }1099 }
1073 case CmdRun:1100 case CmdRun:
1074 case CmdBuild:1101 case CmdBuild:
...@@ -1142,7 +1169,7 @@ int main(int argc, char **argv) {...@@ -1142,7 +1169,7 @@ int main(int argc, char **argv) {
1142 libc = allocate<ZigLibCInstallation>(1);1169 libc = allocate<ZigLibCInstallation>(1);
1143 if ((err = zig_libc_parse(libc, buf_create_from_str(libc_txt), &target, true))) {1170 if ((err = zig_libc_parse(libc, buf_create_from_str(libc_txt), &target, true))) {
1144 fprintf(stderr, "Unable to parse --libc text file: %s\n", err_str(err));1171 fprintf(stderr, "Unable to parse --libc text file: %s\n", err_str(err));
1145 return EXIT_FAILURE;1172 return main_exit(root_progress_node, EXIT_FAILURE);
1146 }1173 }
1147 }1174 }
1148 Buf *cache_dir_buf;1175 Buf *cache_dir_buf;
...@@ -1219,7 +1246,7 @@ int main(int argc, char **argv) {...@@ -1219,7 +1246,7 @@ int main(int argc, char **argv) {
1219 codegen_set_rdynamic(g, rdynamic);1246 codegen_set_rdynamic(g, rdynamic);
1220 if (mmacosx_version_min && mios_version_min) {1247 if (mmacosx_version_min && mios_version_min) {
1221 fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n");1248 fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n");
1222 return EXIT_FAILURE;1249 return main_exit(root_progress_node, EXIT_FAILURE);
1223 }1250 }
12241251
1225 if (mmacosx_version_min) {1252 if (mmacosx_version_min) {
...@@ -1259,6 +1286,11 @@ int main(int argc, char **argv) {...@@ -1259,6 +1286,11 @@ int main(int argc, char **argv) {
1259 zig_print_stack_report(g, stdout);1286 zig_print_stack_report(g, stdout);
12601287
1261 if (cmd == CmdRun) {1288 if (cmd == CmdRun) {
1289 stage2_progress_end(root_progress_node);
1290#ifdef ZIG_ENABLE_MEM_PROFILE
1291 memprof_dump_stats(stderr);
1292#endif
1293
1262 const char *exec_path = buf_ptr(&g->output_file_path);1294 const char *exec_path = buf_ptr(&g->output_file_path);
1263 ZigList<const char*> args = {0};1295 ZigList<const char*> args = {0};
12641296
...@@ -1282,10 +1314,9 @@ int main(int argc, char **argv) {...@@ -1282,10 +1314,9 @@ int main(int argc, char **argv) {
1282 buf_replace(&g->output_file_path, '/', '\\');1314 buf_replace(&g->output_file_path, '/', '\\');
1283#endif1315#endif
1284 if (printf("%s\n", buf_ptr(&g->output_file_path)) < 0)1316 if (printf("%s\n", buf_ptr(&g->output_file_path)) < 0)
1285 return EXIT_FAILURE;1317 return main_exit(root_progress_node, EXIT_FAILURE);
1286 }1318 }
1287 stage2_progress_end(root_progress_node);1319 return main_exit(root_progress_node, EXIT_SUCCESS);
1288 return EXIT_SUCCESS;
1289 } else {1320 } else {
1290 zig_unreachable();1321 zig_unreachable();
1291 }1322 }
...@@ -1293,8 +1324,7 @@ int main(int argc, char **argv) {...@@ -1293,8 +1324,7 @@ int main(int argc, char **argv) {
1293 codegen_translate_c(g, in_file_buf, stdout, cmd == CmdTranslateCUserland);1324 codegen_translate_c(g, in_file_buf, stdout, cmd == CmdTranslateCUserland);
1294 if (timing_info)1325 if (timing_info)
1295 codegen_print_timing_report(g, stderr);1326 codegen_print_timing_report(g, stderr);
1296 stage2_progress_end(root_progress_node);1327 return main_exit(root_progress_node, EXIT_SUCCESS);
1297 return EXIT_SUCCESS;
1298 } else if (cmd == CmdTest) {1328 } else if (cmd == CmdTest) {
1299 codegen_set_emit_file_type(g, emit_file_type);1329 codegen_set_emit_file_type(g, emit_file_type);
13001330
...@@ -1314,7 +1344,7 @@ int main(int argc, char **argv) {...@@ -1314,7 +1344,7 @@ int main(int argc, char **argv) {
13141344
1315 if (g->disable_bin_generation) {1345 if (g->disable_bin_generation) {
1316 fprintf(stderr, "Semantic analysis complete. No binary produced due to -fno-emit-bin.\n");1346 fprintf(stderr, "Semantic analysis complete. No binary produced due to -fno-emit-bin.\n");
1317 return 0;1347 return main_exit(root_progress_node, EXIT_SUCCESS);
1318 }1348 }
13191349
1320 Buf *test_exe_path_unresolved = &g->output_file_path;1350 Buf *test_exe_path_unresolved = &g->output_file_path;
...@@ -1324,7 +1354,7 @@ int main(int argc, char **argv) {...@@ -1324,7 +1354,7 @@ int main(int argc, char **argv) {
1324 if (emit_file_type != EmitFileTypeBinary) {1354 if (emit_file_type != EmitFileTypeBinary) {
1325 fprintf(stderr, "Created %s but skipping execution because it is non executable.\n",1355 fprintf(stderr, "Created %s but skipping execution because it is non executable.\n",
1326 buf_ptr(test_exe_path));1356 buf_ptr(test_exe_path));
1327 return 0;1357 return main_exit(root_progress_node, EXIT_SUCCESS);
1328 }1358 }
13291359
1330 for (size_t i = 0; i < test_exec_args.length; i += 1) {1360 for (size_t i = 0; i < test_exec_args.length; i += 1) {
...@@ -1336,7 +1366,7 @@ int main(int argc, char **argv) {...@@ -1336,7 +1366,7 @@ int main(int argc, char **argv) {
1336 if (!target_can_exec(&native, &target) && test_exec_args.length == 0) {1366 if (!target_can_exec(&native, &target) && test_exec_args.length == 0) {
1337 fprintf(stderr, "Created %s but skipping execution because it is non-native.\n",1367 fprintf(stderr, "Created %s but skipping execution because it is non-native.\n",
1338 buf_ptr(test_exe_path));1368 buf_ptr(test_exe_path));
1339 return 0;1369 return main_exit(root_progress_node, EXIT_SUCCESS);
1340 }1370 }
13411371
1342 Termination term;1372 Termination term;
...@@ -1348,21 +1378,20 @@ int main(int argc, char **argv) {...@@ -1348,21 +1378,20 @@ int main(int argc, char **argv) {
1348 fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n");1378 fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n");
1349 fprintf(stderr, "%s\n", buf_ptr(test_exe_path));1379 fprintf(stderr, "%s\n", buf_ptr(test_exe_path));
1350 }1380 }
1351 stage2_progress_end(root_progress_node);1381 return main_exit(root_progress_node, (term.how == TerminationIdClean) ? term.code : -1);
1352 return (term.how == TerminationIdClean) ? term.code : -1;
1353 } else {1382 } else {
1354 zig_unreachable();1383 zig_unreachable();
1355 }1384 }
1356 }1385 }
1357 case CmdVersion:1386 case CmdVersion:
1358 printf("%s\n", ZIG_VERSION_STRING);1387 printf("%s\n", ZIG_VERSION_STRING);
1359 return EXIT_SUCCESS;1388 return main_exit(root_progress_node, EXIT_SUCCESS);
1360 case CmdZen: {1389 case CmdZen: {
1361 const char *ptr;1390 const char *ptr;
1362 size_t len;1391 size_t len;
1363 stage2_zen(&ptr, &len);1392 stage2_zen(&ptr, &len);
1364 fwrite(ptr, len, 1, stdout);1393 fwrite(ptr, len, 1, stdout);
1365 return EXIT_SUCCESS;1394 return main_exit(root_progress_node, EXIT_SUCCESS);
1366 }1395 }
1367 case CmdTargets:1396 case CmdTargets:
1368 return print_target_list(stdout);1397 return print_target_list(stdout);
src/memory_profiling.cpp created+139
...@@ -0,0 +1,139 @@
1#include "memory_profiling.hpp"
2#include "hash_map.hpp"
3#include "list.hpp"
4#include "util.hpp"
5#include <string.h>
6
7#ifdef ZIG_ENABLE_MEM_PROFILE
8
9static bool str_eql_str(const char *a, const char *b) {
10 return strcmp(a, b) == 0;
11}
12
13static uint32_t str_hash(const char *s) {
14 // FNV 32-bit hash
15 uint32_t h = 2166136261;
16 for (; *s; s += 1) {
17 h = h ^ *s;
18 h = h * 16777619;
19 }
20 return h;
21}
22
23struct CountAndSize {
24 size_t item_count;
25 size_t type_size;
26};
27
28ZigList<const char *> unknown_names = {};
29HashMap<const char *, CountAndSize, str_hash, str_eql_str> usage_table = {};
30bool table_active = false;
31
32
33static const char *get_default_name(const char *name_or_null, size_t type_size) {
34 if (name_or_null != nullptr) return name_or_null;
35 if (type_size >= unknown_names.length) {
36 table_active = false;
37 unknown_names.resize(type_size + 1);
38 table_active = true;
39 }
40 if (unknown_names.at(type_size) == nullptr) {
41 char buf[100];
42 sprintf(buf, "Unknown_%zu%c", type_size, 0);
43 unknown_names.at(type_size) = strdup(buf);
44 }
45 return unknown_names.at(type_size);
46}
47
48void memprof_alloc(const char *name, size_t count, size_t type_size) {
49 if (!table_active) return;
50 if (count == 0) return;
51 // temporarily disable during table put
52 table_active = false;
53 name = get_default_name(name, type_size);
54 auto existing_entry = usage_table.put_unique(name, {count, type_size});
55 if (existing_entry != nullptr) {
56 assert(existing_entry->value.type_size == type_size); // allocated name does not match type
57 existing_entry->value.item_count += count;
58 }
59 table_active = true;
60}
61
62void memprof_dealloc(const char *name, size_t count, size_t type_size) {
63 if (!table_active) return;
64 if (count == 0) return;
65 name = get_default_name(name, type_size);
66 auto existing_entry = usage_table.maybe_get(name);
67 if (existing_entry == nullptr) {
68 zig_panic("deallocated more than allocated; compromised memory usage stats");
69 }
70 if (existing_entry->value.type_size != type_size) {
71 zig_panic("deallocated name '%s' does not match expected type size %zu", name, type_size);
72 }
73 existing_entry->value.item_count -= count;
74}
75
76void memprof_init(void) {
77 usage_table.init(1024);
78 table_active = true;
79}
80
81struct MemItem {
82 const char *type_name;
83 CountAndSize count_and_size;
84};
85
86static size_t get_bytes(const MemItem *item) {
87 return item->count_and_size.item_count * item->count_and_size.type_size;
88}
89
90static int compare_bytes_desc(const void *a, const void *b) {
91 size_t size_a = get_bytes((const MemItem *)(a));
92 size_t size_b = get_bytes((const MemItem *)(b));
93 if (size_a > size_b)
94 return -1;
95 if (size_a < size_b)
96 return 1;
97 return 0;
98}
99
100void memprof_dump_stats(FILE *file) {
101 assert(table_active);
102 // disable modifications from this function
103 table_active = false;
104
105 ZigList<MemItem> list = {};
106
107 auto it = usage_table.entry_iterator();
108 for (;;) {
109 auto *entry = it.next();
110 if (!entry)
111 break;
112
113 list.append({entry->key, entry->value});
114 }
115
116 qsort(list.items, list.length, sizeof(MemItem), compare_bytes_desc);
117
118 size_t total_bytes_used = 0;
119
120 for (size_t i = 0; i < list.length; i += 1) {
121 const MemItem *item = &list.at(i);
122 fprintf(file, "%s: %zu items, %zu bytes each, total ", item->type_name,
123 item->count_and_size.item_count, item->count_and_size.type_size);
124 size_t bytes = get_bytes(item);
125 zig_pretty_print_bytes(file, bytes);
126 fprintf(file, "\n");
127
128 total_bytes_used += bytes;
129 }
130
131 fprintf(stderr, "Total bytes used: ");
132 zig_pretty_print_bytes(file, total_bytes_used);
133 fprintf(file, "\n");
134
135 list.deinit();
136 table_active = true;
137}
138
139#endif
src/memory_profiling.hpp created+22
...@@ -0,0 +1,22 @@
1/*
2 * Copyright (c) 2019 Andrew Kelley
3 *
4 * This file is part of zig, which is MIT licensed.
5 * See http://opensource.org/licenses/MIT
6 */
7
8#ifndef ZIG_MEMORY_PROFILING_HPP
9#define ZIG_MEMORY_PROFILING_HPP
10
11#include "config.h"
12
13#include <stddef.h>
14#include <stdio.h>
15
16void memprof_init(void);
17
18void memprof_alloc(const char *name, size_t item_count, size_t type_size);
19void memprof_dealloc(const char *name, size_t item_count, size_t type_size);
20
21void memprof_dump_stats(FILE *file);
22#endif
src/parser.cpp+7-9
...@@ -518,8 +518,8 @@ static Token *ast_parse_doc_comments(ParseContext *pc, Buf *buf) {...@@ -518,8 +518,8 @@ static Token *ast_parse_doc_comments(ParseContext *pc, Buf *buf) {
518// <- TestDecl ContainerMembers518// <- TestDecl ContainerMembers
519// / TopLevelComptime ContainerMembers519// / TopLevelComptime ContainerMembers
520// / KEYWORD_pub? TopLevelDecl ContainerMembers520// / KEYWORD_pub? TopLevelDecl ContainerMembers
521// / KEYWORD_pub? ContainerField COMMA ContainerMembers521// / ContainerField COMMA ContainerMembers
522// / KEYWORD_pub? ContainerField522// / ContainerField
523// /523// /
524static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {524static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
525 AstNodeContainerDecl res = {};525 AstNodeContainerDecl res = {};
...@@ -548,10 +548,13 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {...@@ -548,10 +548,13 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
548 continue;548 continue;
549 }549 }
550550
551 if (visib_token != nullptr) {
552 ast_error(pc, peek_token(pc), "expected function or variable declaration after pub");
553 }
554
551 AstNode *container_field = ast_parse_container_field(pc);555 AstNode *container_field = ast_parse_container_field(pc);
552 if (container_field != nullptr) {556 if (container_field != nullptr) {
553 assert(container_field->type == NodeTypeStructField);557 assert(container_field->type == NodeTypeStructField);
554 container_field->data.struct_field.visib_mod = visib_mod;
555 container_field->data.struct_field.doc_comments = doc_comment_buf;558 container_field->data.struct_field.doc_comments = doc_comment_buf;
556 res.fields.append(container_field);559 res.fields.append(container_field);
557 if (eat_token_if(pc, TokenIdComma) != nullptr) {560 if (eat_token_if(pc, TokenIdComma) != nullptr) {
...@@ -561,12 +564,7 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {...@@ -561,12 +564,7 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
561 }564 }
562 }565 }
563566
564 // We visib_token wasn't eaten, then we haven't consumed the first token in this rule yet.567 break;
565 // It is therefore safe to return and let the caller continue parsing.
566 if (visib_token == nullptr)
567 break;
568
569 ast_invalid_token_error(pc, peek_token(pc));
570 }568 }
571569
572 return res;570 return res;
src/util.cpp+18
...@@ -119,3 +119,21 @@ Slice<uint8_t> SplitIterator_rest(SplitIterator *self) {...@@ -119,3 +119,21 @@ Slice<uint8_t> SplitIterator_rest(SplitIterator *self) {
119SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes) {119SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes) {
120 return SplitIterator{0, buffer, split_bytes};120 return SplitIterator{0, buffer, split_bytes};
121}121}
122
123void zig_pretty_print_bytes(FILE *f, double n) {
124 if (n > 1024.0 * 1024.0 * 1024.0) {
125 fprintf(f, "%.02f GiB", n / 1024.0 / 1024.0 / 1024.0);
126 return;
127 }
128 if (n > 1024.0 * 1024.0) {
129 fprintf(f, "%.02f MiB", n / 1024.0 / 1024.0);
130 return;
131 }
132 if (n > 1024.0) {
133 fprintf(f, "%.02f KiB", n / 1024.0);
134 return;
135 }
136 fprintf(f, "%.02f bytes", n );
137 return;
138}
139
src/util.hpp+31-4
...@@ -8,6 +8,8 @@...@@ -8,6 +8,8 @@
8#ifndef ZIG_UTIL_HPP8#ifndef ZIG_UTIL_HPP
9#define ZIG_UTIL_HPP9#define ZIG_UTIL_HPP
1010
11#include "memory_profiling.hpp"
12
11#include <stdlib.h>13#include <stdlib.h>
12#include <stdint.h>14#include <stdint.h>
13#include <string.h>15#include <string.h>
...@@ -96,7 +98,10 @@ static inline int ctzll(unsigned long long mask) {...@@ -96,7 +98,10 @@ static inline int ctzll(unsigned long long mask) {
9698
9799
98template<typename T>100template<typename T>
99ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) {101ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count, const char *name = nullptr) {
102#ifdef ZIG_ENABLE_MEM_PROFILE
103 memprof_alloc(name, count, sizeof(T));
104#endif
100#ifndef NDEBUG105#ifndef NDEBUG
101 // make behavior when size == 0 portable106 // make behavior when size == 0 portable
102 if (count == 0)107 if (count == 0)
...@@ -109,7 +114,10 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) {...@@ -109,7 +114,10 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) {
109}114}
110115
111template<typename T>116template<typename T>
112ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count) {117ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count, const char *name = nullptr) {
118#ifdef ZIG_ENABLE_MEM_PROFILE
119 memprof_alloc(name, count, sizeof(T));
120#endif
113#ifndef NDEBUG121#ifndef NDEBUG
114 // make behavior when size == 0 portable122 // make behavior when size == 0 portable
115 if (count == 0)123 if (count == 0)
...@@ -122,7 +130,7 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count) {...@@ -122,7 +130,7 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count) {
122}130}
123131
124template<typename T>132template<typename T>
125static inline T *reallocate(T *old, size_t old_count, size_t new_count) {133static inline T *reallocate(T *old, size_t old_count, size_t new_count, const char *name = nullptr) {
126 T *ptr = reallocate_nonzero(old, old_count, new_count);134 T *ptr = reallocate_nonzero(old, old_count, new_count);
127 if (new_count > old_count) {135 if (new_count > old_count) {
128 memset(&ptr[old_count], 0, (new_count - old_count) * sizeof(T));136 memset(&ptr[old_count], 0, (new_count - old_count) * sizeof(T));
...@@ -131,7 +139,11 @@ static inline T *reallocate(T *old, size_t old_count, size_t new_count) {...@@ -131,7 +139,11 @@ static inline T *reallocate(T *old, size_t old_count, size_t new_count) {
131}139}
132140
133template<typename T>141template<typename T>
134static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) {142static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count, const char *name = nullptr) {
143#ifdef ZIG_ENABLE_MEM_PROFILE
144 memprof_dealloc(name, old_count, sizeof(T));
145 memprof_alloc(name, new_count, sizeof(T));
146#endif
135#ifndef NDEBUG147#ifndef NDEBUG
136 // make behavior when size == 0 portable148 // make behavior when size == 0 portable
137 if (new_count == 0 && old == nullptr)149 if (new_count == 0 && old == nullptr)
...@@ -143,6 +155,19 @@ static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count)...@@ -143,6 +155,19 @@ static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count)
143 return ptr;155 return ptr;
144}156}
145157
158template<typename T>
159static inline void deallocate(T *old, size_t count, const char *name = nullptr) {
160#ifdef ZIG_ENABLE_MEM_PROFILE
161 memprof_dealloc(name, count, sizeof(T));
162#endif
163 free(old);
164}
165
166template<typename T>
167static inline void destroy(T *old, const char *name = nullptr) {
168 return deallocate(old, 1);
169}
170
146template <typename T, size_t n>171template <typename T, size_t n>
147constexpr size_t array_length(const T (&)[n]) {172constexpr size_t array_length(const T (&)[n]) {
148 return n;173 return n;
...@@ -225,6 +250,8 @@ static inline double zig_f16_to_double(float16_t x) {...@@ -225,6 +250,8 @@ static inline double zig_f16_to_double(float16_t x) {
225 return z;250 return z;
226}251}
227252
253void zig_pretty_print_bytes(FILE *f, double n);
254
228template<typename T>255template<typename T>
229struct Optional {256struct Optional {
230 T value;257 T value;
test/cli.zig+2-2
...@@ -37,7 +37,7 @@ pub fn main() !void {...@@ -37,7 +37,7 @@ pub fn main() !void {
37 testMissingOutputPath,37 testMissingOutputPath,
38 };38 };
39 for (test_fns) |testFn| {39 for (test_fns) |testFn| {
40 try fs.deleteTree(a, dir_path);40 try fs.deleteTree(dir_path);
41 try fs.makeDir(dir_path);41 try fs.makeDir(dir_path);
42 try testFn(zig_exe, dir_path);42 try testFn(zig_exe, dir_path);
43 }43 }
...@@ -87,7 +87,7 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult {...@@ -87,7 +87,7 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult {
87fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void {87fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void {
88 _ = try exec(dir_path, [_][]const u8{ zig_exe, "init-lib" });88 _ = try exec(dir_path, [_][]const u8{ zig_exe, "init-lib" });
89 const test_result = try exec(dir_path, [_][]const u8{ zig_exe, "build", "test" });89 const test_result = try exec(dir_path, [_][]const u8{ zig_exe, "build", "test" });
90 testing.expect(std.mem.eql(u8, test_result.stderr, ""));90 testing.expect(std.mem.endsWith(u8, test_result.stderr, "All tests passed.\n"));
91}91}
9292
93fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {93fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {
test/compile_errors.zig+10
...@@ -2,6 +2,16 @@ const tests = @import("tests.zig");...@@ -2,6 +2,16 @@ const tests = @import("tests.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.add(
6 "comparison with error union and error value",
7 \\export fn entry() void {
8 \\ var number_or_error: anyerror!i32 = error.SomethingAwful;
9 \\ _ = number_or_error == error.SomethingAwful;
10 \\}
11 ,
12 "tmp.zig:3:25: error: operator not allowed for type 'anyerror!i32'",
13 );
14
5 cases.add(15 cases.add(
6 "switch with overlapping case ranges",16 "switch with overlapping case ranges",
7 \\export fn entry() void {17 \\export fn entry() void {
test/stage1/behavior/switch.zig+18
...@@ -434,3 +434,21 @@ test "switch with disjoint range" {...@@ -434,3 +434,21 @@ test "switch with disjoint range" {
434 126...126 => {},434 126...126 => {},
435 }435 }
436}436}
437
438var state: u32 = 0;
439fn poll() void {
440 switch (state) {
441 0 => {
442 state = 1;
443 },
444 else => {
445 state += 1;
446 },
447 }
448}
449
450test "switch on global mutable var isn't constant-folded" {
451 while (state < 2) {
452 poll();
453 }
454}
test/stage1/behavior/union.zig+14
...@@ -521,3 +521,17 @@ test "extern union doesn't trigger field check at comptime" {...@@ -521,3 +521,17 @@ test "extern union doesn't trigger field check at comptime" {
521 const x = U{ .x = 0x55AAAA55 };521 const x = U{ .x = 0x55AAAA55 };
522 comptime expect(x.y == 0x55);522 comptime expect(x.y == 0x55);
523}523}
524
525const Foo1 = union(enum) {
526 f: struct {
527 x: usize,
528 },
529};
530var glbl: Foo1 = undefined;
531
532test "global union with single field is correctly initialized" {
533 glbl = Foo1{
534 .f = @memberType(Foo1, 0){ .x = 123 },
535 };
536 expect(glbl.f.x == 123);
537}
tools/process_headers.zig+1-1
...@@ -340,7 +340,7 @@ pub fn main() !void {...@@ -340,7 +340,7 @@ pub fn main() !void {
340 try dir_stack.append(target_include_dir);340 try dir_stack.append(target_include_dir);
341341
342 while (dir_stack.popOrNull()) |full_dir_name| {342 while (dir_stack.popOrNull()) |full_dir_name| {
343 var dir = std.fs.Dir.open(allocator, full_dir_name) catch |err| switch (err) {343 var dir = std.fs.Dir.open(full_dir_name) catch |err| switch (err) {
344 error.FileNotFound => continue :search,344 error.FileNotFound => continue :search,
345 error.AccessDenied => continue :search,345 error.AccessDenied => continue :search,
346 else => return err,346 else => return err,