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}")
4646set(ZIG_STATIC off CACHE BOOL "Attempt to build a static zig executable (not compatible with glibc)")
4747set(ZIG_STATIC_LLVM off CACHE BOOL "Prefer linking against static LLVM libraries")
4848set(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
5051if(ZIG_STATIC)
5152 set(ZIG_STATIC_LLVM "on")
......@@ -455,6 +456,7 @@ set(ZIG_SOURCES
455456 "${CMAKE_SOURCE_DIR}/src/ir_print.cpp"
456457 "${CMAKE_SOURCE_DIR}/src/libc_installation.cpp"
457458 "${CMAKE_SOURCE_DIR}/src/link.cpp"
459 "${CMAKE_SOURCE_DIR}/src/memory_profiling.cpp"
458460 "${CMAKE_SOURCE_DIR}/src/os.cpp"
459461 "${CMAKE_SOURCE_DIR}/src/parser.cpp"
460462 "${CMAKE_SOURCE_DIR}/src/range_set.cpp"
......@@ -628,5 +630,8 @@ set_target_properties(zig PROPERTIES
628630 LINK_FLAGS ${EXE_LDFLAGS}
629631)
630632target_link_libraries(zig compiler "${LIBUSERLAND}")
633if(MSVC)
634 target_link_libraries(zig ntdll.lib)
635endif()
631636add_dependencies(zig zig_build_libuserland)
632637install(TARGETS zig DESTINATION bin)
doc/docgen.zig+1-1
......@@ -51,7 +51,7 @@ pub fn main() !void {
5151 var toc = try genToc(allocator, &tokenizer);
5252
5353 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
5656 try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe);
5757 try buffered_out_stream.flush();
doc/langref.html.in+2-2
......@@ -10086,8 +10086,8 @@ ContainerMembers
1008610086 &lt;- TestDecl ContainerMembers
1008710087 / TopLevelComptime ContainerMembers
1008810088 / KEYWORD_pub? TopLevelDecl ContainerMembers
10089 / KEYWORD_pub? ContainerField COMMA ContainerMembers
10090 / KEYWORD_pub? ContainerField
10089 / ContainerField COMMA ContainerMembers
10090 / ContainerField
1009110091 /
1009210092
1009310093TestDecl &lt;- KEYWORD_test STRINGLITERAL Block
lib/std/build.zig+18-2
......@@ -331,7 +331,7 @@ pub const Builder = struct {
331331 if (self.verbose) {
332332 warn("rm {}\n", full_path);
333333 }
334 fs.deleteTree(self.allocator, full_path) catch {};
334 fs.deleteTree(full_path) catch {};
335335 }
336336
337337 // TODO remove empty directories
......@@ -1491,6 +1491,8 @@ pub const LibExeObjStep = struct {
14911491 /// Position Independent Code
14921492 force_pic: ?bool = null,
14931493
1494 subsystem: ?builtin.SubSystem = null,
1495
14941496 const LinkObject = union(enum) {
14951497 StaticPath: []const u8,
14961498 OtherStep: *LibExeObjStep,
......@@ -2325,6 +2327,20 @@ pub const LibExeObjStep = struct {
23252327 }
23262328 }
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
23282344 if (self.kind == Kind.Test) {
23292345 try builder.spawnChild(zig_args.toSliceConst());
23302346 } else {
......@@ -2671,7 +2687,7 @@ pub const RemoveDirStep = struct {
26712687 const self = @fieldParentPtr(RemoveDirStep, "step", step);
26722688
26732689 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| {
26752691 warn("Unable to remove {}: {}\n", full_path, @errorName(err));
26762692 return err;
26772693 };
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
8080pub extern "c" fn munmap(addr: *align(page_size) c_void, len: usize) c_int;
8181pub extern "c" fn mprotect(addr: *align(page_size) c_void, len: usize, prot: c_uint) c_int;
8282pub 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;
8384pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8;
8485pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_uint, options: c_uint) c_int;
8586pub extern "c" fn fork() c_int;
lib/std/child_process.zig+16-16
......@@ -17,35 +17,35 @@ const TailQueue = std.TailQueue;
1717const maxInt = std.math.maxInt;
1818
1919pub const ChildProcess = struct {
20 pub pid: if (os.windows.is_the_target) void else i32,
21 pub 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,
20 pid: if (os.windows.is_the_target) void else i32,
21 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,
27 pub stdout: ?File,
28 pub stderr: ?File,
26 stdin: ?File,
27 stdout: ?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
3434 /// 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,
38 pub stdout_behavior: StdIo,
39 pub stderr_behavior: StdIo,
37 stdin_behavior: StdIo,
38 stdout_behavior: StdIo,
39 stderr_behavior: StdIo,
4040
4141 /// 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
4444 /// 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
4747 /// Set to change the current working directory when spawning the child process.
48 pub cwd: ?[]const u8,
48 cwd: ?[]const u8,
4949
5050 err_pipe: if (os.windows.is_the_target) void else [2]os.fd_t,
5151 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";
13121312//
13131313// // TODO move this into event loop too
13141314// try os.makePath(allocator, test_tmp_dir);
1315// defer os.deleteTree(allocator, test_tmp_dir) catch {};
1315// defer os.deleteTree(test_tmp_dir) catch {};
13161316//
13171317// var loop: Loop = undefined;
13181318// try loop.initMultiThreaded(allocator);
lib/std/fs.zig+653-383
......@@ -335,444 +335,708 @@ pub fn deleteDirW(dir_path: [*]const u16) !void {
335335 return os.rmdirW(dir_path);
336336}
337337
338const DeleteTreeError = error{
339 OutOfMemory,
340 AccessDenied,
341 FileTooBig,
342 IsDir,
343 SymLinkLoop,
344 ProcessFdQuotaExceeded,
345 NameTooLong,
346 SystemFdQuotaExceeded,
347 NoDevice,
348 SystemResources,
349 NoSpaceLeft,
350 PathAlreadyExists,
351 ReadOnlyFileSystem,
352 NotDir,
353 FileNotFound,
354 FileSystem,
355 FileBusy,
356 DirNotEmpty,
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);
338/// Removes a symlink, file, or directory.
339/// If `full_path` is relative, this is equivalent to `Dir.deleteTree` with the
340/// current working directory as the open directory handle.
341/// If `full_path` is absolute, this is equivalent to `Dir.deleteTree` with the
342/// base directory.
343pub fn deleteTree(full_path: []const u8) !void {
344 if (path.isAbsolute(full_path)) {
345 const dirname = path.dirname(full_path) orelse return error{
346 /// Attempt to remove the root file system path.
347 /// This error is unreachable if `full_path` is relative.
348 CannotDeleteRootDirectory,
349 }.CannotDeleteRootDirectory;
350
351 var dir = try Dir.open(dirname);
352 defer dir.close();
353
354 return dir.deleteTree(path.basename(full_path));
355 } else {
356 return Dir.cwd().deleteTree(full_path);
441357 }
442358}
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.
446360pub const Dir = struct {
447 handle: Handle,
448 allocator: *Allocator,
361 fd: os.fd_t,
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) {
451383 .macosx, .ios, .freebsd, .netbsd => struct {
452 fd: i32,
384 dir: Dir,
453385 seek: i64,
454 buf: []u8,
386 buf: [8192]u8, // TODO align(@alignOf(os.dirent)),
455387 index: usize,
456388 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 }
457502 },
458503 .linux => struct {
459 fd: i32,
460 buf: []u8,
504 dir: Dir,
505 buf: [8192]u8, // TODO align(@alignOf(os.dirent64)),
461506 index: usize,
462507 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 }
463558 },
464559 .windows => struct {
465 handle: os.windows.HANDLE,
466 find_file_data: os.windows.WIN32_FIND_DATAW,
560 dir: Dir,
561 buf: [8192]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)),
562 index: usize,
563 end_index: usize,
467564 first: bool,
468565 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 }
469627 },
470628 else => @compileError("unimplemented"),
471629 };
472630
473 pub const Entry = struct {
474 name: []const u8,
475 kind: Kind,
631 pub fn iterate(self: Dir) Iterator {
632 switch (builtin.os) {
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 {
478 BlockDevice,
479 CharacterDevice,
480 Directory,
481 NamedPipe,
482 SymLink,
483 File,
484 UnixDomainSocket,
485 Whiteout,
486 Unknown,
487 };
488 };
658 /// Returns an open handle to the current working directory.
659 /// Closing the returned `Dir` is checked illegal behavior.
660 /// On POSIX targets, this function is comptime-callable.
661 pub fn cwd() Dir {
662 if (os.windows.is_the_target) {
663 return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
664 } else {
665 return Dir{ .fd = os.AT_FDCWD };
666 }
667 }
489668
490669 pub const OpenError = error{
491670 FileNotFound,
492671 NotDir,
493672 AccessDenied,
494 FileTooBig,
495 IsDir,
496673 SymLinkLoop,
497674 ProcessFdQuotaExceeded,
498675 NameTooLong,
499676 SystemFdQuotaExceeded,
500677 NoDevice,
501678 SystemResources,
502 NoSpaceLeft,
503 PathAlreadyExists,
504 OutOfMemory,
505679 InvalidUtf8,
506680 BadPathName,
507681 DeviceBusy,
682 } || os.UnexpectedError;
508683
509 Unexpected,
510 };
511
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 };
684 /// Call `close` to free the directory handle.
685 pub fn open(dir_path: []const u8) OpenError!Dir {
686 return cwd().openDir(dir_path);
545687 }
546688
547 pub fn close(self: *Dir) void {
548 if (os.windows.is_the_target) {
549 return os.windows.FindClose(self.handle.handle);
550 }
551 self.allocator.free(self.handle.buf);
552 os.close(self.handle.fd);
689 /// Same as `open` except the parameter is null-terminated.
690 pub fn openC(dir_path_c: [*]const u8) OpenError!Dir {
691 return cwd().openDirC(dir_path_c);
553692 }
554693
555 /// Memory such as file names referenced in this returned entry becomes invalid
556 /// with subsequent calls to next, as well as when this `Dir` is deinitialized.
557 pub fn next(self: *Dir) !?Entry {
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 }
694 pub fn close(self: *Dir) void {
695 os.close(self.fd);
696 self.* = undefined;
566697 }
567698
568 pub fn openRead(self: Dir, file_path: []const u8) os.OpenError!File {
569 const path_c = try os.toPosixPath(file_path);
699 /// Call `File.close` on the result when done.
700 pub fn openRead(self: Dir, sub_path: []const u8) File.OpenError!File {
701 const path_c = try os.toPosixPath(sub_path);
570702 return self.openReadC(&path_c);
571703 }
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 {
574707 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);
576709 return File.openHandle(fd);
577710 }
578711
579 fn nextDarwin(self: *Dir) !?Entry {
580 start_over: while (true) {
581 if (self.handle.index >= self.handle.end_index) {
582 if (self.handle.buf.len == 0) {
583 self.handle.buf = try self.allocator.alloc(u8, mem.page_size);
584 }
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];
712 /// Call `close` on the result when done.
713 pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir {
714 if (os.windows.is_the_target) {
715 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
716 return self.openDirW(&sub_path_w);
717 }
616718
617 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
618 continue :start_over;
619 }
719 const sub_path_c = try os.toPosixPath(sub_path);
720 return self.openDirC(&sub_path_c);
721 }
620722
621 const entry_kind = switch (darwin_entry.d_type) {
622 os.DT_BLK => Entry.Kind.BlockDevice,
623 os.DT_CHR => Entry.Kind.CharacterDevice,
624 os.DT_DIR => Entry.Kind.Directory,
625 os.DT_FIFO => Entry.Kind.NamedPipe,
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 };
723 /// Same as `openDir` except the parameter is null-terminated.
724 pub fn openDirC(self: Dir, sub_path_c: [*]const u8) OpenError!Dir {
725 if (os.windows.is_the_target) {
726 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
727 return self.openDirW(&sub_path_w);
636728 }
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 };
637739 }
638740
639 fn nextWindows(self: *Dir) !?Entry {
640 while (true) {
641 if (self.handle.first) {
642 self.handle.first = false;
643 } else {
644 if (!try os.windows.FindNextFile(self.handle.handle, &self.handle.find_file_data))
645 return null;
646 }
647 const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr);
648 if (mem.eql(u16, name_utf16le, [_]u16{'.'}) or mem.eql(u16, name_utf16le, [_]u16{ '.', '.' }))
649 continue;
650 // Trust that Windows gives us valid UTF-16LE
651 const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable;
652 const name_utf8 = self.handle.name_data[0..name_utf8_len];
653 const kind = blk: {
654 const attrs = self.handle.find_file_data.dwFileAttributes;
655 if (attrs & os.windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
656 if (attrs & os.windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink;
657 break :blk Entry.Kind.File;
658 };
659 return Entry{
660 .name = name_utf8,
661 .kind = kind,
662 };
741 /// Same as `openDir` except the path parameter is UTF16LE, NT-prefixed.
742 /// This function is Windows-only.
743 pub fn openDirW(self: Dir, sub_path_w: [*]const u16) OpenError!Dir {
744 const w = os.windows;
745
746 var result = Dir{
747 .fd = undefined,
748 };
749
750 const path_len_bytes = @intCast(u16, mem.toSliceConst(u16, sub_path_w).len * 2);
751 var nt_name = w.UNICODE_STRING{
752 .Length = path_len_bytes,
753 .MaximumLength = path_len_bytes,
754 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
755 };
756 var attr = w.OBJECT_ATTRIBUTES{
757 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
758 .RootDirectory = if (path.isAbsoluteW(sub_path_w)) null else self.fd,
759 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
760 .ObjectName = &nt_name,
761 .SecurityDescriptor = null,
762 .SecurityQualityOfService = null,
763 };
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),
663794 }
664795 }
665796
666 fn nextLinux(self: *Dir) !?Entry {
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 }
797 pub const DeleteFileError = os.UnlinkError;
672798
673 while (true) {
674 const rc = os.linux.getdents64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len);
675 switch (os.linux.getErrno(rc)) {
676 0 => {},
677 os.EBADF => unreachable,
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;
799 /// Delete a file name and possibly the file it refers to, based on an open directory handle.
800 pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
801 const sub_path_c = try os.toPosixPath(sub_path);
802 return self.deleteFileC(&sub_path_c);
803 }
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 .. entries
699 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
700 continue :start_over;
701 }
813 pub const DeleteDirError = error{
814 DirNotEmpty,
815 FileNotFound,
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) {
704 os.DT_BLK => Entry.Kind.BlockDevice,
705 os.DT_CHR => Entry.Kind.CharacterDevice,
706 os.DT_DIR => Entry.Kind.Directory,
707 os.DT_FIFO => Entry.Kind.NamedPipe,
708 os.DT_LNK => Entry.Kind.SymLink,
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 };
829 /// Returns `error.DirNotEmpty` if the directory is not empty.
830 /// To delete a directory recursively, see `deleteTree`.
831 pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
832 if (os.windows.is_the_target) {
833 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
834 return self.deleteDirW(&sub_path_w);
717835 }
836 const sub_path_c = try os.toPosixPath(sub_path);
837 return self.deleteDirC(&sub_path_c);
718838 }
719839
720 fn nextBsd(self: *Dir) !?Entry {
721 start_over: while (true) {
722 if (self.handle.index >= self.handle.end_index) {
723 if (self.handle.buf.len == 0) {
724 self.handle.buf = try self.allocator.alloc(u8, mem.page_size);
725 }
840 /// Same as `deleteDir` except the parameter is null-terminated.
841 pub fn deleteDirC(self: Dir, sub_path_c: [*]const u8) DeleteDirError!void {
842 os.unlinkatC(self.fd, sub_path_c, os.AT_REMOVEDIR) catch |err| switch (err) {
843 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
844 else => |e| return e,
845 };
846 }
726847
727 while (true) {
728 const rc = os.system.getdirentries(
729 self.handle.fd,
730 self.handle.buf.ptr,
731 self.handle.buf.len,
732 &self.handle.seek,
733 );
734 switch (os.errno(rc)) {
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;
848 /// Same as `deleteDir` except the parameter is UTF16LE, NT prefixed.
849 /// This function is Windows-only.
850 pub fn deleteDirW(self: Dir, sub_path_w: [*]const u16) DeleteDirError!void {
851 os.unlinkatW(self.fd, sub_path_w, os.AT_REMOVEDIR) catch |err| switch (err) {
852 error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
853 else => |e| return e,
854 };
855 }
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, "..")) {
758 continue :start_over;
864 /// Same as `readLink`, except the `pathname` parameter is null-terminated.
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,
759921 }
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) {
762 os.DT_BLK => Entry.Kind.BlockDevice,
763 os.DT_CHR => Entry.Kind.CharacterDevice,
764 os.DT_DIR => Entry.Kind.Directory,
765 os.DT_FIFO => Entry.Kind.NamedPipe,
766 os.DT_LNK => Entry.Kind.SymLink,
767 os.DT_REG => Entry.Kind.File,
768 os.DT_SOCK => Entry.Kind.UnixDomainSocket,
769 os.DT_WHT => Entry.Kind.Whiteout,
770 else => Entry.Kind.Unknown,
771 };
772 return Entry{
773 .name = name,
774 .kind = entry_kind,
934 error.AccessDenied,
935 error.SymLinkLoop,
936 error.ProcessFdQuotaExceeded,
937 error.NameTooLong,
938 error.SystemFdQuotaExceeded,
939 error.NoDevice,
940 error.SystemResources,
941 error.Unexpected,
942 error.InvalidUtf8,
943 error.BadPathName,
944 error.DeviceBusy,
945 => |e| return e,
775946 };
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 }
7761040 }
7771041 }
7781042};
......@@ -782,13 +1046,18 @@ pub const Walker = struct {
7821046 name_buffer: std.Buffer,
7831047
7841048 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,
7861053 basename: []const u8,
1054
1055 path: []const u8,
7871056 kind: Dir.Entry.Kind,
7881057 };
7891058
7901059 const StackItem = struct {
791 dir_it: Dir,
1060 dir_it: Dir.Iterator,
7921061 dirname_len: usize,
7931062 };
7941063
......@@ -806,23 +1075,26 @@ pub const Walker = struct {
8061075 try self.name_buffer.appendByte(path.sep);
8071076 try self.name_buffer.append(base.name);
8081077 if (base.kind == .Directory) {
809 // TODO https://github.com/ziglang/zig/issues/2888
810 var new_dir = try Dir.open(self.stack.allocator, self.name_buffer.toSliceConst());
1078 var new_dir = top.dir_it.dir.openDir(base.name) catch |err| switch (err) {
1079 error.NameTooLong => unreachable, // no path sep in base.name
1080 else => |e| return e,
1081 };
8111082 {
8121083 errdefer new_dir.close();
8131084 try self.stack.append(StackItem{
814 .dir_it = new_dir,
1085 .dir_it = new_dir.iterate(),
8151086 .dirname_len = self.name_buffer.len(),
8161087 });
8171088 }
8181089 }
8191090 return Entry{
1091 .dir = top.dir_it.dir,
8201092 .basename = self.name_buffer.toSliceConst()[dirname_len + 1 ..],
8211093 .path = self.name_buffer.toSliceConst(),
8221094 .kind = base.kind,
8231095 };
8241096 } else {
825 self.stack.pop().dir_it.close();
1097 self.stack.pop().dir_it.dir.close();
8261098 }
8271099 }
8281100 }
......@@ -837,12 +1109,12 @@ pub const Walker = struct {
8371109/// Recursively iterates over a directory.
8381110/// Must call `Walker.deinit` when done.
8391111/// `dir_path` must not end in a path separator.
840/// TODO: https://github.com/ziglang/zig/issues/2888
1112/// The order of returned file system entries is undefined.
8411113pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
8421114 assert(!mem.endsWith(u8, dir_path, path.sep_str));
8431115
844 var dir_it = try Dir.open(allocator, dir_path);
845 errdefer dir_it.close();
1116 var dir = try Dir.open(dir_path);
1117 errdefer dir.close();
8461118
8471119 var name_buffer = try std.Buffer.init(allocator, dir_path);
8481120 errdefer name_buffer.deinit();
......@@ -853,7 +1125,7 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
8531125 };
8541126
8551127 try walker.stack.append(Walker.StackItem{
856 .dir_it = dir_it,
1128 .dir_it = dir.iterate(),
8571129 .dirname_len = dir_path.len,
8581130 });
8591131
......@@ -862,15 +1134,13 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
8621134
8631135/// Read value of a symbolic link.
8641136/// The return value is a slice of buffer, from index `0`.
865/// TODO https://github.com/ziglang/zig/issues/2888
866pub fn readLink(pathname: []const u8, buffer: *[os.PATH_MAX]u8) ![]u8 {
1137pub fn readLink(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
8671138 return os.readlink(pathname, buffer);
8681139}
8691140
870/// Same as `readLink`, except the `pathname` parameter is null-terminated.
871/// TODO https://github.com/ziglang/zig/issues/2888
872pub fn readLinkC(pathname: [*]const u8, buffer: *[os.PATH_MAX]u8) ![]u8 {
873 return os.readlinkC(pathname, buffer);
1141/// Same as `readLink`, except the parameter is null-terminated.
1142pub fn readLinkC(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
1143 return os.readlinkC(pathname_c, buffer);
8741144}
8751145
8761146pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError;
lib/std/fs/file.zig+1
......@@ -243,6 +243,7 @@ pub const File = struct {
243243 switch (rc) {
244244 windows.STATUS.SUCCESS => {},
245245 windows.STATUS.BUFFER_OVERFLOW => {},
246 windows.STATUS.INVALID_PARAMETER => unreachable,
246247 else => return windows.unexpectedStatus(rc),
247248 }
248249 return Stat{
lib/std/fs/path.zig+19
......@@ -136,6 +136,25 @@ pub fn isAbsolute(path: []const u8) bool {
136136 }
137137}
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
139158pub fn isAbsoluteWindows(path: []const u8) bool {
140159 if (path[0] == '/')
141160 return true;
lib/std/heap.zig+1-1
......@@ -338,7 +338,7 @@ pub const HeapAllocator = switch (builtin.os) {
338338/// This allocator takes an existing allocator, wraps it, and provides an interface
339339/// where you can allocate without freeing, and then free it all together.
340340pub const ArenaAllocator = struct {
341 pub allocator: Allocator,
341 allocator: Allocator,
342342
343343 child_allocator: *Allocator,
344344 buffer_list: std.SinglyLinkedList([]u8),
lib/std/http/headers.zig+3-3
......@@ -28,9 +28,9 @@ fn never_index_default(name: []const u8) bool {
2828
2929const HeaderEntry = struct {
3030 allocator: *Allocator,
31 pub name: []const u8,
32 pub value: []u8,
33 pub never_index: bool,
31 name: []const u8,
32 value: []u8,
33 never_index: bool,
3434
3535 const Self = @This();
3636
lib/std/io.zig+13-10
......@@ -127,6 +127,7 @@ pub fn OutStream(comptime WriteError: type) type {
127127 };
128128}
129129
130/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
130131pub fn writeFile(path: []const u8, data: []const u8) !void {
131132 var file = try File.openWrite(path);
132133 defer file.close();
......@@ -134,11 +135,13 @@ pub fn writeFile(path: []const u8, data: []const u8) !void {
134135}
135136
136137/// On success, caller owns returned buffer.
138/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
137139pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
138140 return readFileAllocAligned(allocator, path, @alignOf(u8));
139141}
140142
141143/// On success, caller owns returned buffer.
144/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
142145pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 {
143146 var file = try File.openRead(path);
144147 defer file.close();
......@@ -161,7 +164,7 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type)
161164 const Self = @This();
162165 const Stream = InStream(Error);
163166
164 pub stream: Stream,
167 stream: Stream,
165168
166169 unbuffered_in_stream: *Stream,
167170
......@@ -273,7 +276,7 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ
273276 pub const Error = InStreamError;
274277 pub const Stream = InStream(Error);
275278
276 pub stream: Stream,
279 stream: Stream,
277280 base: *Stream,
278281
279282 // Right now the look-ahead space is statically allocated, but a version with dynamic allocation
......@@ -336,7 +339,7 @@ pub const SliceInStream = struct {
336339 pub const Error = error{};
337340 pub const Stream = InStream(Error);
338341
339 pub stream: Stream,
342 stream: Stream,
340343
341344 pos: usize,
342345 slice: []const u8,
......@@ -514,9 +517,9 @@ pub const SliceOutStream = struct {
514517 pub const Error = error{OutOfSpace};
515518 pub const Stream = OutStream(Error);
516519
517 pub stream: Stream,
520 stream: Stream,
518521
519 pub pos: usize,
522 pos: usize,
520523 slice: []u8,
521524
522525 pub fn init(slice: []u8) SliceOutStream {
......@@ -571,7 +574,7 @@ pub const NullOutStream = struct {
571574 pub const Error = error{};
572575 pub const Stream = OutStream(Error);
573576
574 pub stream: Stream,
577 stream: Stream,
575578
576579 pub fn init() NullOutStream {
577580 return NullOutStream{
......@@ -595,8 +598,8 @@ pub fn CountingOutStream(comptime OutStreamError: type) type {
595598 pub const Stream = OutStream(Error);
596599 pub const Error = OutStreamError;
597600
598 pub stream: Stream,
599 pub bytes_written: u64,
601 stream: Stream,
602 bytes_written: u64,
600603 child_stream: *Stream,
601604
602605 pub fn init(child_stream: *Stream) Self {
......@@ -635,7 +638,7 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime OutStreamEr
635638 pub const Stream = OutStream(Error);
636639 pub const Error = OutStreamError;
637640
638 pub stream: Stream,
641 stream: Stream,
639642
640643 unbuffered_out_stream: *Stream,
641644
......@@ -1084,7 +1087,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
10841087 // safety. If it is bad, it will be caught anyway.
10851088 const TagInt = @TagType(TagType);
10861089 const tag = try self.deserializeInt(TagInt);
1087
1090
10881091 inline for (info.fields) |field_info| {
10891092 if (field_info.enum_field.?.value == tag) {
10901093 const name = field_info.name;
lib/std/io/seekable_stream.zig+2-2
......@@ -39,8 +39,8 @@ pub const SliceSeekableInStream = struct {
3939 pub const Stream = InStream(Error);
4040 pub const SeekableInStream = SeekableStream(SeekError, GetSeekPosError);
4141
42 pub stream: Stream,
43 pub seekable_stream: SeekableInStream,
42 stream: Stream,
43 seekable_stream: SeekableInStream,
4444
4545 pos: usize,
4646 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
529529
530530pub const OpenError = error{
531531 AccessDenied,
532 FileTooBig,
533 IsDir,
534532 SymLinkLoop,
535533 ProcessFdQuotaExceeded,
536 NameTooLong,
537534 SystemFdQuotaExceeded,
538535 NoDevice,
539536 FileNotFound,
540537
538 /// The path exceeded `MAX_PATH_BYTES` bytes.
539 NameTooLong,
540
541541 /// Insufficient kernel memory was available, or
542542 /// the named file is a FIFO and per-user hard limit on
543543 /// memory allocation for pipes has been reached.
544544 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.
546555 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.
547559 NotDir,
560
561 /// The path already exists and the `O_CREAT` and `O_EXCL` flags were provided.
548562 PathAlreadyExists,
549563 DeviceBusy,
550564} || UnexpectedError;
......@@ -978,6 +992,114 @@ pub fn unlinkC(file_path: [*]const u8) UnlinkError!void {
978992 }
979993}
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
9811103const RenameError = error{
9821104 AccessDenied,
9831105 FileBusy,
......@@ -1237,6 +1359,27 @@ pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {
12371359 }
12381360}
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
12401383pub const SetIdError = error{
12411384 ResourceLimitReached,
12421385 InvalidUserId,
......@@ -1476,18 +1619,46 @@ pub const AcceptError = error{
14761619 BlockedByFirewall,
14771620} || UnexpectedError;
14781621
1479/// Accept a connection on a socket. `fd` must be opened in blocking mode.
1480/// See also `accept4_async`.
1481pub fn accept4(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 {
1622/// Accept a connection on a socket.
1623/// If the application has a global event loop enabled, EAGAIN is handled
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 {
14821650 while (true) {
1483 var sockaddr_size = u32(@sizeOf(sockaddr));
1484 const rc = system.accept4(fd, addr, &sockaddr_size, flags);
1651 const rc = system.accept4(sockfd, addr, addr_size, flags);
14851652 switch (errno(rc)) {
14861653 0 => return @intCast(i32, rc),
14871654 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 },
14911662 EBADF => unreachable, // always a race condition
14921663 ECONNABORTED => return error.ConnectionAborted,
14931664 EFAULT => unreachable,
......@@ -1500,34 +1671,8 @@ pub fn accept4(fd: i32, addr: *sockaddr, flags: u32) AcceptError!i32 {
15001671 EOPNOTSUPP => return error.OperationNotSupported,
15011672 EPROTO => return error.ProtocolFailure,
15021673 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,
15161675 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,
15311676 }
15321677 }
15331678}
lib/std/os/bits/darwin.zig+14
......@@ -1178,3 +1178,17 @@ pub fn S_IWHT(m: u32) bool {
11781178 return m & S_IFMT == S_IFWHT;
11791179}
11801180pub 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 {
939939}
940940
941941pub 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;
158158pub const EDQUOT = 10069;
159159
160160pub 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" {
1919 try fs.makePath(a, "os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");
2020 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");
2121 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");
23 if (fs.Dir.open(a, "os_test_tmp")) |dir| {
22 try fs.deleteTree("os_test_tmp");
23 if (fs.Dir.open("os_test_tmp")) |dir| {
2424 @panic("expected error");
2525 } else |err| {
2626 expect(err == error.FileNotFound);
......@@ -37,7 +37,7 @@ test "access file" {
3737
3838 try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", "");
3939 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");
4141}
4242
4343fn testThreadIdFn(thread_id: *Thread.Id) void {
lib/std/os/windows.zig+24-4
......@@ -20,6 +20,8 @@ pub const shell32 = @import("windows/shell32.zig");
2020
2121pub usingnamespace @import("windows/bits.zig");
2222
23pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));
24
2325/// `builtin` is missing `subsystem` when the subsystem is automatically detected,
2426/// so Zig standard library has the subsystem detection logic here. This should generally be
2527/// used rather than `builtin.subsystem`.
......@@ -42,7 +44,6 @@ pub const subsystem: ?builtin.SubSystem = blk: {
4244 break :blk builtin.SubSystem.Console;
4345 }
4446 },
45 .uefi => break :blk builtin.SubSystem.EfiApplication,
4647 else => break :blk null,
4748 }
4849};
......@@ -792,6 +793,25 @@ pub fn SetFileTime(
792793 }
793794}
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
795815/// A file time is a 64-bit value that represents the number of 100-nanosecond
796816/// intervals that have elapsed since 12:00 A.M. January 1, 1601 Coordinated
797817/// Universal Time (UTC).
......@@ -844,8 +864,8 @@ pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16)
844864 else => {},
845865 }
846866 }
847 const start_index = if (mem.startsWith(u8, s, "\\\\") or !std.fs.path.isAbsolute(s)) 0 else blk: {
848 const prefix = [_]u16{ '\\', '\\', '?', '\\' };
867 const start_index = if (mem.startsWith(u8, s, "\\?") or !std.fs.path.isAbsolute(s)) 0 else blk: {
868 const prefix = [_]u16{ '\\', '?', '?', '\\' };
849869 mem.copy(u16, result[0..], prefix);
850870 break :blk prefix.len;
851871 };
......@@ -879,7 +899,7 @@ pub fn unexpectedError(err: DWORD) std.os.UnexpectedError {
879899/// and you get an unexpected status.
880900pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError {
881901 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);
883903 std.debug.dumpCurrentStackTrace(null);
884904 }
885905 return error.Unexpected;
lib/std/os/windows/bits.zig+145-3
......@@ -300,6 +300,44 @@ pub const FILE_SHARE_DELETE = 0x00000004;
300300pub const FILE_SHARE_READ = 0x00000001;
301301pub 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
303341pub const CREATE_ALWAYS = 2;
304342pub const CREATE_NEW = 1;
305343pub const OPEN_ALWAYS = 4;
......@@ -720,15 +758,119 @@ pub const VECTORED_EXCEPTION_HANDLER = stdcallcc fn (ExceptionInfo: *EXCEPTION_P
720758
721759pub const OBJECT_ATTRIBUTES = extern struct {
722760 Length: ULONG,
723 RootDirectory: HANDLE,
761 RootDirectory: ?HANDLE,
724762 ObjectName: *UNICODE_STRING,
725763 Attributes: ULONG,
726764 SecurityDescriptor: ?*c_void,
727765 SecurityQualityOfService: ?*c_void,
728766};
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
730777pub const UNICODE_STRING = extern struct {
731 Length: USHORT,
732 MaximumLength: USHORT,
778 Length: c_ushort,
779 MaximumLength: c_ushort,
733780 Buffer: [*]WCHAR,
734781};
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_
4747
4848pub 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
5052pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
5153
5254pub 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(
1313 DesiredAccess: ACCESS_MASK,
1414 ObjectAttributes: *OBJECT_ATTRIBUTES,
1515 IoStatusBlock: *IO_STATUS_BLOCK,
16 AllocationSize: *LARGE_INTEGER,
16 AllocationSize: ?*LARGE_INTEGER,
1717 FileAttributes: ULONG,
1818 ShareAccess: ULONG,
1919 CreateDisposition: ULONG,
2020 CreateOptions: ULONG,
21 EaBuffer: *c_void,
21 EaBuffer: ?*c_void,
2222 EaLength: ULONG,
2323) NTSTATUS;
2424pub 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;
33
44/// The data was too large to fit into the specified buffer.
55pub 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 {
155155 }
156156 if (node.estimated_total_items) |total| {
157157 if (need_ellipse) self.bufWrite(&end, " ");
158 self.bufWrite(&end, "[{}/{}] ", node.completed_items, total);
158 self.bufWrite(&end, "[{}/{}] ", node.completed_items + 1, total);
159159 need_ellipse = false;
160160 } else if (node.completed_items != 0) {
161161 if (need_ellipse) self.bufWrite(&end, " ");
162 self.bufWrite(&end, "[{}] ", node.completed_items);
162 self.bufWrite(&end, "[{}] ", node.completed_items + 1);
163163 need_ellipse = false;
164164 }
165165 }
lib/std/special/docs/index.html+394-216
......@@ -5,62 +5,228 @@
55 <title>Documentation - Zig</title>
66 <link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAgklEQVR4AWMYWuD7EllJIM4G4g4g5oIJ/odhOJ8wToOxSTXgNxDHoeiBMfA4+wGShjyYOCkG/IGqWQziEzYAoUAeiF9D5U+DxEg14DRU7jWIT5IBIOdCxf+A+CQZAAoopEB7QJwBCBwHiip8UYmRdrAlDpIMgApwQZNnNii5Dq0MBgCxxycBnwEd+wAAAABJRU5ErkJggg==">
77 <style type="text/css">
8 body {
9 font-family: system-ui, -apple-system, Roboto, "Segoe UI", sans-serif;
10 max-width: 60em;
8 :root {
9 font-size: 1em;
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);
1127 }
28
29 a {
30 text-decoration: none;
31 }
32
33 a:hover {
34 text-decoration: underline;
35 }
36
1237 .hidden {
1338 display: none;
1439 }
15 a {
16 color: #2A6286;
40
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);
1751 }
18 pre{
19 font-family:"Source Code Pro",monospace;
20 font-size:1em;
21 background-color:#F5F5F5;
22 padding:1em;
23 overflow-x: auto;
52
53 .flex-main {
54 display: flex;
55 width: 100%;
56 height: 100%;
57 justify-content: center;
58
59 z-index: 100;
2460 }
25 code {
26 font-family:"Source Code Pro",monospace;
27 font-size:1em;
61
62 .flex-filler {
63 flex-grow: 1;
64 flex-shrink: 1;
2865 }
29 nav {
30 width: 10em;
31 position: fixed;
32 left: 0;
33 top: 0;
34 height: 100vh;
66
67 .flex-left {
68 width: 12rem;
69 max-width: 15vw;
70 min-width: 9.5rem;
3571 overflow: auto;
72 overflow-wrap: break-word;
73 flex-shrink: 0;
74 flex-grow: 0;
75
76 z-index: 300;
3677 }
37 nav h2 {
38 font-size: 1.2em;
39 text-decoration: underline;
40 margin: 0;
41 padding: 0.5em 0;
42 text-align: center;
78
79 .flex-right {
80 display: flex;
81 overflow: auto;
82 flex-grow: 1;
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;
43118 }
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;
45126 margin: 0;
46127 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;
48207 }
49 section {
50 margin-left: 10em;
208
209 .docs code {
210 font-family: var(--mono);
211 font-size: 1em;
51212 }
52 section h1 {
53 border-bottom: 1px dashed;
213
214 .docs h1 {
215 font-size: 1.4em;
216 margin: 0.8em 0;
217 padding: 0;
218 border-bottom: 0.0625rem dashed;
54219 }
55 section h2 {
220
221 .docs h2 {
56222 font-size: 1.3em;
57223 margin: 0.5em 0;
58224 padding: 0;
59 border-bottom: 1px solid;
225 border-bottom: 0.0625rem solid;
60226 }
61227 #listNav {
62228 list-style-type: none;
63 margin: 0.5em 0 0 0;
229 margin: 0;
64230 padding: 0;
65231 overflow: hidden;
66232 background-color: #f1f1f1;
......@@ -84,26 +250,14 @@
84250 color: #000;
85251 }
86252
87 #listPkgs {
88 list-style-type: none;
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;
253 #listSearchResults li.selected {
254 background-color: #93e196;
102255 }
103 #listPkgs li a.active {
104 background-color: #FFBB4D;
105 color: #000;
256
257 #tableFnErrors dt {
258 font-weight: bold;
106259 }
260
107261 #listFnExamples {
108262 list-style-type: none;
109263 margin: 0;
......@@ -114,67 +268,76 @@
114268 white-space: nowrap;
115269 overflow-x: auto;
116270 }
117 #logo {
118 width: 8em;
119 padding: 0.5em 1em;
271
272 .docs td {
273 vertical-align: top;
274 margin: 0;
275 padding: 0.5em;
276 max-width: 27em;
277 text-overflow: ellipsis;
278 overflow-x: hidden;
120279 }
121
122 #search {
280
281 /* help dialog */
282 .help-modal {
283 display: flex;
123284 width: 100%;
124 }
125
126 #helpDialog {
127 width: 21em;
128 height: 19em;
285 height: 100%;
129286 position: fixed;
130287 top: 0;
131288 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;
133300 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);
135304 }
136 #helpDialog h1 {
137 text-align: center;
305
306 .help-modal h1 {
307 margin: 0.75em 2.5em 1em 2.5em;
138308 font-size: 1.5em;
309 text-align: center;
139310 }
140 #helpDialog dt, #helpDialog dd {
311
312 .help-modal dt, .help-modal dd {
141313 display: inline;
142314 margin: 0 0.2em;
143315 }
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;
145329 color: #000;
146330 background-color: #fafbfc;
147331 border-color: #d1d5da;
148332 border-bottom-color: #c6cbd1;
333 border: solid 0.0625em;
334 border-radius: 0.1875em;
149335 box-shadow-color: #c6cbd1;
150 display: inline-block;
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;
336 box-shadow: inset 0 -0.0625em 0;
158337 cursor: default;
159338 }
160
161 #listSearchResults li.selected {
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
339
340 /* tokens */
178341 .tok-kw {
179342 color: #333;
180343 font-weight: bold;
......@@ -203,16 +366,29 @@
203366 color: #458;
204367 font-weight: bold;
205368 }
206
369
370 /* dark mode */
207371 @media (prefers-color-scheme: dark) {
208 body{
209 background-color: #111;
210 color: #bbb;
372
373 :root {
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);
211389 }
212 a {
213 color: #88f;
214 }
215 pre{
390
391 .docs pre {
216392 background-color:#2A2A2A;
217393 }
218394 #listNav {
......@@ -229,20 +405,6 @@
229405 background-color: #FFBB4D;
230406 color: #000;
231407 }
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 }
246408 #listSearchResults li.selected {
247409 background-color: #000;
248410 }
......@@ -273,112 +435,128 @@
273435 .tok-type {
274436 color: #68f;
275437 }
438
276439 }
277440 </style>
278441 </head>
279 <body>
280 <nav>
281 <img alt="ZIG" id="logo" src="data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxNTAgMTAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxnIGZpbGw9IiNmN2E0MWQiPjxwYXRoIGQ9Im0wIDEwdjgwaDE5bDYtMTAgMTItMTBoLTE3di00MGgxNXYtMjB6bTQwIDB2MjBoNjJ2LTIwem05MSAwLTYgMTAtMTIgMTBoMTd2NDBoLTE1djIwaDM1di04MHptLTgzIDYwdjIwaDYydi0yMHoiIHNoYXBlLXJlbmRlcmluZz0iY3Jpc3BFZGdlcyIvPjxwYXRoIGQ9Im0zNyA3MC0xOCAyMHYtMTV6Ii8+PHBhdGggZD0ibTExMyAzMCAxOC0yMHYxNXoiLz48cGF0aCBkPSJtOTYuOTggMTAuNjMgMzYuMjgtMTAuNC04MC4yOSA4OS4xNy0zNi4yOCAxMC40eiIvPjwvZz48L3N2Zz4K"></img>
282 <div id="sectPkgs" class="hidden">
283 <h2>Packages</h2>
284 <ul id="listPkgs">
285 </ul>
286 </div>
287 <div id="sectInfo" class="hidden">
288 <h2>Zig Version</h2>
289 <p id="tdZigVer"></p>
290 <h2>Target</h2>
291 <p id="tdTarget"></p>
442 <body class="canvas">
443 <div class="flex-main">
444 <div class="flex-filler"></div>
445 <div class="flex-left sidebar">
446 <nav>
447 <div class="logo">
448 <svg version="1.1" viewBox="0 0 150 80" xmlns="http://www.w3.org/2000/svg">
449 <g fill="#f7a41d">
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>
451 <path d="m37,59.91101l-18,20l0,-15l18,-5z"></path>
452 <path d="m113,19.91101l18,-20l0,15l-18,5z"></path>
453 <path d="m96.98,0.54101l36.28,-10.4l-80.29,89.17l-36.28,10.4l80.29,-89.17z"></path>
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>
292468 </div>
293 </nav>
294 <section>
295 <input type="search" id="search" autocomplete="off" spellcheck="false" placeholder="`s` to search, `?` to see more options">
296 <p id="status">Loading...</p>
297 <div id="sectNav" class="hidden"><ul id="listNav"></ul></div>
298 <div id="fnProto" class="hidden">
299 <pre id="fnProtoCode"></pre>
300 </div>
301 <h1 id="hdrName" class="hidden"></h1>
302 <div id="fnNoExamples" class="hidden">
303 <p>This function is not tested or referenced.</p>
304 </div>
305 <div id="declNoRef" class="hidden">
306 <p>
307 This declaration is not tested or referenced, and it has therefore not been included in
308 semantic analysis, which means the only documentation available is whatever is in the
309 doc comments.
310 </p>
311 </div>
312 <div id="fnDocs" class="hidden"></div>
313 <div id="sectFnErrors" class="hidden">
314 <h2>Errors</h2>
315 <div id="fnErrorsAnyError">
316 <p><span class="tok-type">anyerror</span> means the error set is known only at runtime.</p>
317 </div>
318 <div id="tableFnErrors"><dl id="listFnErrors"></dl></div>
319 </div>
320 <div id="sectSearchResults" class="hidden">
321 <h2>Search Results</h2>
322 <ul id="listSearchResults"></ul>
323 </div>
324 <div id="sectSearchNoResults" class="hidden">
325 <h2>No Results Found</h2>
326 <p>Press escape to exit search and then '?' to see more options.</p>
327 </div>
328 <div id="sectFields" class="hidden">
329 <h2>Fields</h2>
330 <div id="listFields">
469 <div class="flex-right">
470 <div class="wrap">
471 <section class="docs">
472 <input type="search" class="search" id="search" autocomplete="off" spellcheck="false" placeholder="`s` to search, `?` to see more options">
473 <p id="status">Loading...</p>
474 <div id="sectNav" class="hidden"><ul id="listNav"></ul></div>
475 <div id="fnProto" class="hidden">
476 <pre id="fnProtoCode"></pre>
477 </div>
478 <h1 id="hdrName" class="hidden"></h1>
479 <div id="fnNoExamples" class="hidden">
480 <p>This function is not tested or referenced.</p>
481 </div>
482 <div id="declNoRef" class="hidden">
483 <p>
484 This declaration is not tested or referenced, and it has therefore not been included in
485 semantic analysis, which means the only documentation available is whatever is in the
486 doc comments.
487 </p>
488 </div>
489 <div id="fnDocs" class="hidden"></div>
490 <div id="sectFnErrors" class="hidden">
491 <h2>Errors</h2>
492 <div id="fnErrorsAnyError">
493 <p><span class="tok-type">anyerror</span> means the error set is known only at runtime.</p>
494 </div>
495 <div id="tableFnErrors"><dl id="listFnErrors"></dl></div>
496 </div>
497 <div id="sectSearchResults" class="hidden">
498 <h2>Search Results</h2>
499 <ul id="listSearchResults"></ul>
500 </div>
501 <div id="sectSearchNoResults" class="hidden">
502 <h2>No Results Found</h2>
503 <p>Press escape to exit search and then '?' to see more options.</p>
504 </div>
505 <div id="sectFields" class="hidden">
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>
331546 </div>
332547 </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>
374548 <div id="helpDialog" class="hidden">
375 <h1>Keyboard Shortcuts</h1>
376 <dl><dt><kbd>?</kbd></dt><dd>Show this help dialog</dd></dl>
377 <dl><dt><kbd>Esc</kbd></dt><dd>Clear focus; close this dialog</dd></dl>
378 <dl><dt><kbd>s</kbd></dt><dd>Focus the search field</dd></dl>
379 <dl><dt><kbd>↑</kbd></dt><dd>Move up in search results</dd></dl>
380 <dl><dt><kbd>↓</kbd></dt><dd>Move down in search results</dd></dl>
381 <dl><dt><kbd>⏎</kbd></dt><dd>Go to active search result</dd></dl>
549 <div class="help-modal">
550 <div class="dialog">
551 <h1>Keyboard Shortcuts</h1>
552 <dl><dt><kbd>?</kbd></dt><dd>Show this help dialog</dd></dl>
553 <dl><dt><kbd>Esc</kbd></dt><dd>Clear focus; close this dialog</dd></dl>
554 <dl><dt><kbd>s</kbd></dt><dd>Focus the search field</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>
382560 </div>
383561 <script src="data.js"></script>
384562 <script src="main.js"></script>
lib/std/special/test_runner.zig+12-3
......@@ -15,20 +15,29 @@ pub fn main() anyerror!void {
1515 for (test_fn_list) |test_fn, i| {
1616 var test_node = root_node.start(test_fn.name, null);
1717 test_node.activate();
18 progress.refresh();
19 if (progress.terminal == null) std.debug.warn("{}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
1820 if (test_fn.func()) |_| {
1921 ok_count += 1;
2022 test_node.end();
23 if (progress.terminal == null) std.debug.warn("OK\n");
2124 } else |err| switch (err) {
2225 error.SkipZigTest => {
2326 skip_count += 1;
2427 test_node.end();
2528 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;
2634 },
27 else => return err,
2835 }
2936 }
3037 root_node.end();
31 if (ok_count != test_fn_list.len) {
32 progress.log("{} passed; {} skipped.\n", ok_count, skip_count);
38 if (ok_count == test_fn_list.len) {
39 std.debug.warn("All tests passed.\n");
40 } else {
41 std.debug.warn("{} passed; {} skipped.\n", ok_count, skip_count);
3342 }
3443}
lib/std/zig/ast.zig+1-3
......@@ -290,7 +290,7 @@ pub const Error = union(enum) {
290290 pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{}'");
291291
292292 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");
294294 pub const UnattachedDocComment = SimpleError("Unattached documentation comment");
295295 pub const ExtraAlignQualifier = SimpleError("Extra align qualifier");
296296 pub const ExtraConstQualifier = SimpleError("Extra const qualifier");
......@@ -757,7 +757,6 @@ pub const Node = struct {
757757 pub const ContainerField = struct {
758758 base: Node,
759759 doc_comments: ?*DocComment,
760 visib_token: ?TokenIndex,
761760 name_token: TokenIndex,
762761 type_expr: ?*Node,
763762 value_expr: ?*Node,
......@@ -780,7 +779,6 @@ pub const Node = struct {
780779 }
781780
782781 pub fn firstToken(self: *const ContainerField) TokenIndex {
783 if (self.visib_token) |visib_token| return visib_token;
784782 return self.name_token;
785783 }
786784
lib/std/zig/parse.zig+7-9
......@@ -138,9 +138,15 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No
138138 continue;
139139 }
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
141148 if (try parseContainerField(arena, it, tree)) |node| {
142149 const field = node.cast(Node.ContainerField).?;
143 field.visib_token = visib_token;
144150 field.doc_comments = doc_comments;
145151 try list.push(node);
146152 const comma = eatToken(it, .Comma) orelse break;
......@@ -149,13 +155,6 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No
149155 continue;
150156 }
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
159158 break;
160159 }
161160
......@@ -407,7 +406,6 @@ fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
407406 node.* = Node.ContainerField{
408407 .base = Node{ .id = .ContainerField },
409408 .doc_comments = null,
410 .visib_token = null,
411409 .name_token = name_token,
412410 .type_expr = type_expr,
413411 .value_expr = value_expr,
lib/std/zig/parser_test.zig+3-3
......@@ -1766,7 +1766,7 @@ test "zig fmt: struct declaration" {
17661766 \\const S = struct {
17671767 \\ const Self = @This();
17681768 \\ f1: u8,
1769 \\ pub f3: u8,
1769 \\ f3: u8,
17701770 \\
17711771 \\ fn method(self: *Self) Self {
17721772 \\ return self.*;
......@@ -1777,14 +1777,14 @@ test "zig fmt: struct declaration" {
17771777 \\
17781778 \\const Ps = packed struct {
17791779 \\ a: u8,
1780 \\ pub b: u8,
1780 \\ b: u8,
17811781 \\
17821782 \\ c: u8,
17831783 \\};
17841784 \\
17851785 \\const Es = extern struct {
17861786 \\ a: u8,
1787 \\ pub b: u8,
1787 \\ b: u8,
17881788 \\
17891789 \\ c: u8,
17901790 \\};
lib/std/zig/render.zig+2-6
......@@ -254,10 +254,6 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, i
254254
255255 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
261257 if (field.type_expr == null and field.value_expr == null) {
262258 return renderToken(tree, stream, field.name_token, indent, start_col, Space.Comma); // name,
263259 } else if (field.type_expr != null and field.value_expr == null) {
......@@ -2206,8 +2202,8 @@ const FindByteOutStream = struct {
22062202 pub const Error = error{};
22072203 pub const Stream = std.io.OutStream(Error);
22082204
2209 pub stream: Stream,
2210 pub byte_found: bool,
2205 stream: Stream,
2206 byte_found: bool,
22112207 byte: u8,
22122208
22132209 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
747747 )) catch |err| switch (err) {
748748 error.IsDir, error.AccessDenied => {
749749 // 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);
751751 defer dir.close();
752752
753753 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
283283 const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) {
284284 error.IsDir, error.AccessDenied => {
285285 // 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);
287287 defer dir.close();
288288
289 while (try dir.next()) |entry| {
290 if (entry.kind == fs.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
289 var dir_it = dir.iterate();
290
291 while (try dir_it.next()) |entry| {
292 if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
291293 const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name });
292294 try fmtPath(fmt, full_path, check_mode);
293295 }
src-self-hosted/test.zig+2-2
......@@ -56,11 +56,11 @@ pub const TestContext = struct {
5656 errdefer allocator.free(self.zig_lib_dir);
5757
5858 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 {};
6060 }
6161
6262 fn deinit(self: *TestContext) void {
63 std.fs.deleteTree(allocator, tmp_dir_name) catch {};
63 std.fs.deleteTree(tmp_dir_name) catch {};
6464 allocator.free(self.zig_lib_dir);
6565 self.zig_compiler.deinit();
6666 self.loop.deinit();
src/all_types.hpp+3-5
......@@ -990,8 +990,6 @@ struct AstNodeStructField {
990990 // populated if the "align(A)" is present
991991 AstNode *align_expr;
992992 Buf doc_comments;
993
994 VisibMod visib_mod;
995993};
996994
997995struct AstNodeStringLiteral {
......@@ -2569,12 +2567,12 @@ enum IrInstructionId {
25692567struct IrInstruction {
25702568 Scope *scope;
25712569 AstNode *source_node;
2572 ConstExprValue value;
2573 size_t debug_id;
25742570 LLVMValueRef llvm_value;
2571 ConstExprValue value;
2572 uint32_t debug_id;
25752573 // if ref_count is zero and the instruction has no side effects,
25762574 // the instruction can be omitted in codegen
2577 size_t ref_count;
2575 uint32_t ref_count;
25782576 // When analyzing IR, instructions that point to this instruction in the "old ir"
25792577 // can find the instruction that corresponds to this value in the "new ir"
25802578 // 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_
57835783
57845784
57855785ConstExprValue *create_const_vals(size_t count) {
5786 ConstGlobalRefs *global_refs = allocate<ConstGlobalRefs>(count);
5787 ConstExprValue *vals = allocate<ConstExprValue>(count);
5786 ConstGlobalRefs *global_refs = allocate<ConstGlobalRefs>(count, "ConstGlobalRefs");
5787 ConstExprValue *vals = allocate<ConstExprValue>(count, "ConstExprValue");
57885788 for (size_t i = 0; i < count; i += 1) {
57895789 vals[i].global_refs = &global_refs[i];
57905790 }
src/codegen.cpp+7-2
......@@ -6355,12 +6355,17 @@ static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *un
63556355 ConstParent *parent = &union_const_val->parent;
63566356 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
63586363 ZigType *u32 = g->builtin_types.entry_u32;
63596364 LLVMValueRef indices[] = {
63606365 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 payload
6366 LLVMConstInt(get_llvm_type(g, u32), union_payload_index, false),
63626367 };
6363 return LLVMConstInBoundsGEP(base_ptr, indices, 2);
6368 return LLVMConstInBoundsGEP(base_ptr, indices, (union_payload_index != SIZE_MAX) ? 2 : 1);
63646369}
63656370
63666371static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ConstExprValue *const_val) {
src/config.h.in+2-3
......@@ -13,9 +13,6 @@
1313#define ZIG_VERSION_PATCH @ZIG_VERSION_PATCH@
1414#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
1916// Used for communicating build information to self hosted build.
2017#define ZIG_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@"
2118#define ZIG_CXX_COMPILER "@CMAKE_CXX_COMPILER@"
......@@ -24,4 +21,6 @@
2421#define ZIG_LLVM_CONFIG_EXE "@LLVM_CONFIG_EXE@"
2522#define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@"
2623
24#cmakedefine ZIG_ENABLE_MEM_PROFILE
25
2726#endif
src/dump_analysis.cpp+1-18
......@@ -240,23 +240,6 @@ static void jw_string(JsonWriter *jw, const char *s) {
240240
241241static 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
260243static int compare_type_abi_sizes_desc(const void *a, const void *b) {
261244 uint64_t size_a = (*(ZigType * const*)(a))->abi_size;
262245 uint64_t size_b = (*(ZigType * const*)(b))->abi_size;
......@@ -322,7 +305,7 @@ static void tree_print(FILE *f, ZigType *ty, size_t indent) {
322305
323306 start_peer(f, indent);
324307 fprintf(f, "\"sizef\": \"");
325 pretty_print_bytes(f, ty->abi_size);
308 zig_pretty_print_bytes(f, ty->abi_size);
326309 fprintf(f, "\"");
327310
328311 start_peer(f, indent);
src/ir.cpp+58-30
......@@ -413,7 +413,7 @@ ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) {
413413}
414414
415415static 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");
417417 result->scope = scope;
418418 result->name_hint = name_hint;
419419 result->debug_id = exec_next_debug_id(irb->exec);
......@@ -1085,13 +1085,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSpillEnd *) {
10851085
10861086template<typename T>
10871087static 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);
10891094 special_instruction->base.id = ir_instruction_id(special_instruction);
10901095 special_instruction->base.scope = scope;
10911096 special_instruction->base.source_node = source_node;
10921097 special_instruction->base.debug_id = exec_next_debug_id(irb->exec);
10931098 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");
10951100 return special_instruction;
10961101}
10971102
......@@ -3569,7 +3574,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
35693574 switch (node->data.return_expr.kind) {
35703575 case ReturnKindUnconditional:
35713576 {
3572 ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1);
3577 ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn");
35733578 result_loc_ret->base.id = ResultLocIdReturn;
35743579 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,
36643669 ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val, nullptr));
36653670 IrInstructionSpillBegin *spill_begin = ir_build_spill_begin(irb, scope, node, err_val,
36663671 SpillIdRetErrCode);
3667 ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1);
3672 ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1, "ResultLocReturn");
36683673 result_loc_ret->base.id = ResultLocIdReturn;
36693674 ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
36703675 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
36923697 Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime,
36933698 bool skip_name_check)
36943699{
3695 ZigVar *variable_entry = allocate<ZigVar>(1);
3700 ZigVar *variable_entry = allocate<ZigVar>(1, "ZigVar");
36963701 variable_entry->parent_scope = parent_scope;
36973702 variable_entry->shadowable = is_shadowable;
36983703 variable_entry->mem_slot_index = SIZE_MAX;
......@@ -3767,7 +3772,7 @@ static ZigVar *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *n
37673772}
37683773
37693774static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) {
3770 ResultLocPeer *result = allocate<ResultLocPeer>(1);
3775 ResultLocPeer *result = allocate<ResultLocPeer>(1, "ResultLocPeer");
37713776 result->base.id = ResultLocIdPeer;
37723777 result->base.source_instruction = peer_parent->base.source_instruction;
37733778 result->parent = peer_parent;
......@@ -3806,7 +3811,7 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
38063811 scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node,
38073812 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");
38103815 scope_block->peer_parent->base.id = ResultLocIdPeerParent;
38113816 scope_block->peer_parent->base.source_instruction = scope_block->is_comptime;
38123817 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)
39333938 if (lvalue == irb->codegen->invalid_instruction)
39343939 return irb->codegen->invalid_instruction;
39353940
3936 ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);
3941 ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1, "ResultLocInstruction");
39373942 result_loc_inst->base.id = ResultLocIdInstruction;
39383943 result_loc_inst->base.source_instruction = lvalue;
39393944 ir_ref_instruction(lvalue, irb->current_basic_block);
......@@ -4005,10 +4010,10 @@ static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node
40054010
40064011 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 *");
40094014 incoming_values[0] = val1;
40104015 incoming_values[1] = val2;
4011 IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
4016 IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
40124017 incoming_blocks[0] = post_val1_block;
40134018 incoming_blocks[1] = post_val2_block;
40144019
......@@ -8017,7 +8022,8 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A
80178022 err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size;
80188023 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
80228028 for (uint32_t i = 0; i < err_count; i += 1) {
80238029 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
80488054 }
80498055 errors[err->value] = err;
80508056 }
8051 free(errors);
8057 deallocate(errors, errors_count, "ErrorTableEntry *");
80528058 return ir_build_const_type(irb, parent_scope, node, err_set_type);
80538059}
80548060
......@@ -9574,7 +9580,8 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp
95749580 if (type_is_global_error_set(set2)) {
95759581 return set1;
95769582 }
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 *");
95789585 populate_error_set_table(errors, set1);
95799586 ZigList<ErrorTableEntry *> intersection_list = {};
95809587
......@@ -9595,7 +9602,7 @@ static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigTyp
95959602 buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&existing_entry_with_docs->name));
95969603 }
95979604 }
9598 free(errors);
9605 deallocate(errors, errors_count, "ErrorTableEntry *");
95999606
96009607 err_set_type->data.error_set.err_count = intersection_list.length;
96019608 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
97929799 return result;
97939800 }
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 *");
97969804 for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) {
97979805 ErrorTableEntry *error_entry = container_set->data.error_set.errors[i];
97989806 assert(errors[error_entry->value] == nullptr);
......@@ -9809,7 +9817,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
98099817 result.data.error_set_mismatch->missing_errors.append(contained_error_entry);
98109818 }
98119819 }
9812 free(errors);
9820 deallocate(errors, errors_count, "ErrorTableEntry *");
98139821 return result;
98149822 }
98159823
......@@ -10112,6 +10120,18 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
1011210120 } else {
1011310121 err_set_type = cur_type;
1011410122 }
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
1011510135 for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
1011610136 ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
1011710137 assert(errors[error_entry->value] == nullptr);
......@@ -10814,7 +10834,8 @@ static IrInstruction *ira_suspend(IrAnalyze *ira, IrInstruction *old_instruction
1081410834 IrSuspendPosition *suspend_pos)
1081510835{
1081610836 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,
1081810839 ira->old_irb.current_basic_block->debug_id,
1081910840 ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->name_hint,
1082010841 ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->debug_id,
......@@ -10852,7 +10873,7 @@ static IrInstruction *ira_resume(IrAnalyze *ira) {
1085210873 ira->instruction_index = pos.instruction_index;
1085310874 assert(pos.instruction_index < ira->old_irb.current_basic_block->instruction_list.length);
1085410875 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,
1085610877 ira->old_irb.current_basic_block->debug_id,
1085710878 ira->old_irb.current_basic_block->instruction_list.at(pos.instruction_index)->debug_id);
1085810879 }
......@@ -14686,14 +14707,15 @@ static IrInstruction *ir_analyze_instruction_merge_err_sets(IrAnalyze *ira,
1468614707 return ira->codegen->invalid_instruction;
1468714708 }
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 *");
1469014712 for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) {
1469114713 ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i];
1469214714 assert(errors[error_entry->value] == nullptr);
1469314715 errors[error_entry->value] = error_entry;
1469414716 }
1469514717 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
1469814720 return ir_const_type(ira, &instruction->base, result_type);
1469914721}
......@@ -19241,7 +19263,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira,
1924119263
1924219264 ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type;
1924319265 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) {
1924519267 pointee_val = const_ptr_pointee(ira, ira->codegen, &target_value_ptr->value, target_value_ptr->source_node);
1924619268 if (pointee_val == nullptr)
1924719269 return ira->codegen->invalid_instruction;
......@@ -23074,17 +23096,22 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction
2307423096 zig_unreachable();
2307523097 }
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);
2307823104 if (!ptr_is_undef && start_scalar > rel_end) {
2307923105 ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
2308023106 return ira->codegen->invalid_instruction;
2308123107 }
2308223108
23083 uint64_t end_scalar;
23109 uint64_t end_scalar = rel_end;
2308423110 if (end) {
23085 end_scalar = bigint_as_u64(&end->value.data.x_bigint);
23086 } else {
23087 end_scalar = rel_end;
23111 ConstExprValue *end_val = ir_resolve_const(ira, end, UndefBad);
23112 if (!end_val)
23113 return ira->codegen->invalid_instruction;
23114 end_scalar = bigint_as_u64(&end_val->data.x_bigint);
2308823115 }
2308923116 if (!ptr_is_undef) {
2309023117 if (end_scalar > rel_end) {
......@@ -24034,7 +24061,8 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2403424061 return ira->codegen->invalid_instruction;
2403524062 }
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
2403924067 for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
2404024068 IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i];
......@@ -24091,7 +24119,7 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
2409124119 }
2409224120 }
2409324121
24094 free(field_prev_uses);
24122 deallocate(field_prev_uses, field_prev_uses_count, "AstNode *");
2409524123 } else if (switch_type->id == ZigTypeIdInt) {
2409624124 RangeSet rs = {0};
2409724125 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_
2631826346 }
2631926347
2632026348 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);
2632226350 }
2632326351 IrInstruction *new_instruction = ir_analyze_instruction_base(ira, old_instruction);
2632426352 if (new_instruction != nullptr) {
src/ir_print.cpp+6-6
......@@ -38,8 +38,8 @@ struct IrPrint {
3838
3939static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction);
4040
41static const char* ir_instruction_type_str(IrInstruction* instruction) {
42 switch (instruction->id) {
41const char* ir_instruction_type_str(IrInstructionId id) {
42 switch (id) {
4343 case IrInstructionIdInvalid:
4444 return "Invalid";
4545 case IrInstructionIdShuffleVector:
......@@ -385,9 +385,9 @@ static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction, bool trail
385385 const char mark = trailing ? ':' : '#';
386386 const char *type_name = instruction->value.type ? buf_ptr(&instruction->value.type->name) : "(unknown)";
387387 const char *ref_count = ir_has_side_effects(instruction) ?
388 "-" : buf_ptr(buf_sprintf("%" ZIG_PRI_usize "", instruction->ref_count));
389 fprintf(irp->f, "%c%-3zu| %-22s| %-12s| %-2s| ", mark, instruction->debug_id,
390 ir_instruction_type_str(instruction), type_name, ref_count);
388 "-" : buf_ptr(buf_sprintf("%" PRIu32 "", instruction->ref_count));
389 fprintf(irp->f, "%c%-3" PRIu32 "| %-22s| %-12s| %-2s| ", mark, instruction->debug_id,
390 ir_instruction_type_str(instruction->id), type_name, ref_count);
391391}
392392
393393static 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) {
398398}
399399
400400static 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);
402402 if (irp->pass != IrPassSrc && irp->printed.maybe_get(instruction) == nullptr) {
403403 irp->printed.put(instruction, 0);
404404 irp->pending.append(instruction);
src/ir_print.hpp+2
......@@ -15,4 +15,6 @@
1515void ir_print(CodeGen *codegen, FILE *f, IrExecutable *executable, int indent_size, IrPass pass);
1616void ir_print_instruction(CodeGen *codegen, FILE *f, IrInstruction *instruction, int indent_size, IrPass pass);
1717
18const char* ir_instruction_type_str(IrInstructionId id);
19
1820#endif
src/main.cpp+52-23
......@@ -64,6 +64,9 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
6464 " -fno-PIC disable Position Independent Code\n"
6565 " -ftime-report print timing diagnostics\n"
6666 " -fstack-report print stack size diagnostics\n"
67#ifdef ZIG_ENABLE_MEM_PROFILE
68 " -fmem-report print memory usage diagnostics\n"
69#endif
6770 " -fdump-analysis write analysis.json file with type information\n"
6871 " -femit-docs create a docs/ dir with html documentation\n"
6972 " -fno-emit-bin skip emitting machine code\n"
......@@ -306,9 +309,29 @@ static int zig_error_no_build_file(void) {
306309
307310extern "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
309328int main(int argc, char **argv) {
310329 stage2_attach_segfault_handler();
311330
331#ifdef ZIG_ENABLE_MEM_PROFILE
332 memprof_init();
333#endif
334
312335 char *arg0 = argv[0];
313336 Error err;
314337
......@@ -670,6 +693,13 @@ int main(int argc, char **argv) {
670693 timing_info = true;
671694 } else if (strcmp(arg, "-fstack-report") == 0) {
672695 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
673703 } else if (strcmp(arg, "-fdump-analysis") == 0) {
674704 enable_dump_analysis = true;
675705 } else if (strcmp(arg, "-femit-docs") == 0) {
......@@ -1038,16 +1068,14 @@ int main(int argc, char **argv) {
10381068 if (in_file) {
10391069 ZigLibCInstallation libc;
10401070 if ((err = zig_libc_parse(&libc, buf_create_from_str(in_file), &target, true)))
1041 return EXIT_FAILURE;
1042 stage2_progress_end(root_progress_node);
1043 return EXIT_SUCCESS;
1071 return main_exit(root_progress_node, EXIT_FAILURE);
1072 return main_exit(root_progress_node, EXIT_SUCCESS);
10441073 }
10451074 ZigLibCInstallation libc;
10461075 if ((err = zig_libc_find_native(&libc, true)))
1047 return EXIT_FAILURE;
1076 return main_exit(root_progress_node, EXIT_FAILURE);
10481077 zig_libc_render(&libc, stdout);
1049 stage2_progress_end(root_progress_node);
1050 return EXIT_SUCCESS;
1078 return main_exit(root_progress_node, EXIT_SUCCESS);
10511079 }
10521080 case CmdBuiltin: {
10531081 CodeGen *g = codegen_create(main_pkg_path, nullptr, &target,
......@@ -1065,10 +1093,9 @@ int main(int argc, char **argv) {
10651093 Buf *builtin_source = codegen_generate_builtin_source(g);
10661094 if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
10671095 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);
10691097 }
1070 stage2_progress_end(root_progress_node);
1071 return EXIT_SUCCESS;
1098 return main_exit(root_progress_node, EXIT_SUCCESS);
10721099 }
10731100 case CmdRun:
10741101 case CmdBuild:
......@@ -1142,7 +1169,7 @@ int main(int argc, char **argv) {
11421169 libc = allocate<ZigLibCInstallation>(1);
11431170 if ((err = zig_libc_parse(libc, buf_create_from_str(libc_txt), &target, true))) {
11441171 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);
11461173 }
11471174 }
11481175 Buf *cache_dir_buf;
......@@ -1219,7 +1246,7 @@ int main(int argc, char **argv) {
12191246 codegen_set_rdynamic(g, rdynamic);
12201247 if (mmacosx_version_min && mios_version_min) {
12211248 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);
12231250 }
12241251
12251252 if (mmacosx_version_min) {
......@@ -1259,6 +1286,11 @@ int main(int argc, char **argv) {
12591286 zig_print_stack_report(g, stdout);
12601287
12611288 if (cmd == CmdRun) {
1289 stage2_progress_end(root_progress_node);
1290#ifdef ZIG_ENABLE_MEM_PROFILE
1291 memprof_dump_stats(stderr);
1292#endif
1293
12621294 const char *exec_path = buf_ptr(&g->output_file_path);
12631295 ZigList<const char*> args = {0};
12641296
......@@ -1282,10 +1314,9 @@ int main(int argc, char **argv) {
12821314 buf_replace(&g->output_file_path, '/', '\\');
12831315#endif
12841316 if (printf("%s\n", buf_ptr(&g->output_file_path)) < 0)
1285 return EXIT_FAILURE;
1317 return main_exit(root_progress_node, EXIT_FAILURE);
12861318 }
1287 stage2_progress_end(root_progress_node);
1288 return EXIT_SUCCESS;
1319 return main_exit(root_progress_node, EXIT_SUCCESS);
12891320 } else {
12901321 zig_unreachable();
12911322 }
......@@ -1293,8 +1324,7 @@ int main(int argc, char **argv) {
12931324 codegen_translate_c(g, in_file_buf, stdout, cmd == CmdTranslateCUserland);
12941325 if (timing_info)
12951326 codegen_print_timing_report(g, stderr);
1296 stage2_progress_end(root_progress_node);
1297 return EXIT_SUCCESS;
1327 return main_exit(root_progress_node, EXIT_SUCCESS);
12981328 } else if (cmd == CmdTest) {
12991329 codegen_set_emit_file_type(g, emit_file_type);
13001330
......@@ -1314,7 +1344,7 @@ int main(int argc, char **argv) {
13141344
13151345 if (g->disable_bin_generation) {
13161346 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);
13181348 }
13191349
13201350 Buf *test_exe_path_unresolved = &g->output_file_path;
......@@ -1324,7 +1354,7 @@ int main(int argc, char **argv) {
13241354 if (emit_file_type != EmitFileTypeBinary) {
13251355 fprintf(stderr, "Created %s but skipping execution because it is non executable.\n",
13261356 buf_ptr(test_exe_path));
1327 return 0;
1357 return main_exit(root_progress_node, EXIT_SUCCESS);
13281358 }
13291359
13301360 for (size_t i = 0; i < test_exec_args.length; i += 1) {
......@@ -1336,7 +1366,7 @@ int main(int argc, char **argv) {
13361366 if (!target_can_exec(&native, &target) && test_exec_args.length == 0) {
13371367 fprintf(stderr, "Created %s but skipping execution because it is non-native.\n",
13381368 buf_ptr(test_exe_path));
1339 return 0;
1369 return main_exit(root_progress_node, EXIT_SUCCESS);
13401370 }
13411371
13421372 Termination term;
......@@ -1348,21 +1378,20 @@ int main(int argc, char **argv) {
13481378 fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n");
13491379 fprintf(stderr, "%s\n", buf_ptr(test_exe_path));
13501380 }
1351 stage2_progress_end(root_progress_node);
1352 return (term.how == TerminationIdClean) ? term.code : -1;
1381 return main_exit(root_progress_node, (term.how == TerminationIdClean) ? term.code : -1);
13531382 } else {
13541383 zig_unreachable();
13551384 }
13561385 }
13571386 case CmdVersion:
13581387 printf("%s\n", ZIG_VERSION_STRING);
1359 return EXIT_SUCCESS;
1388 return main_exit(root_progress_node, EXIT_SUCCESS);
13601389 case CmdZen: {
13611390 const char *ptr;
13621391 size_t len;
13631392 stage2_zen(&ptr, &len);
13641393 fwrite(ptr, len, 1, stdout);
1365 return EXIT_SUCCESS;
1394 return main_exit(root_progress_node, EXIT_SUCCESS);
13661395 }
13671396 case CmdTargets:
13681397 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) {
518518// <- TestDecl ContainerMembers
519519// / TopLevelComptime ContainerMembers
520520// / KEYWORD_pub? TopLevelDecl ContainerMembers
521// / KEYWORD_pub? ContainerField COMMA ContainerMembers
522// / KEYWORD_pub? ContainerField
521// / ContainerField COMMA ContainerMembers
522// / ContainerField
523523// /
524524static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
525525 AstNodeContainerDecl res = {};
......@@ -548,10 +548,13 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
548548 continue;
549549 }
550550
551 if (visib_token != nullptr) {
552 ast_error(pc, peek_token(pc), "expected function or variable declaration after pub");
553 }
554
551555 AstNode *container_field = ast_parse_container_field(pc);
552556 if (container_field != nullptr) {
553557 assert(container_field->type == NodeTypeStructField);
554 container_field->data.struct_field.visib_mod = visib_mod;
555558 container_field->data.struct_field.doc_comments = doc_comment_buf;
556559 res.fields.append(container_field);
557560 if (eat_token_if(pc, TokenIdComma) != nullptr) {
......@@ -561,12 +564,7 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
561564 }
562565 }
563566
564 // We visib_token wasn't eaten, then we haven't consumed the first token in this rule yet.
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));
567 break;
570568 }
571569
572570 return res;
src/util.cpp+18
......@@ -119,3 +119,21 @@ Slice<uint8_t> SplitIterator_rest(SplitIterator *self) {
119119SplitIterator memSplit(Slice<uint8_t> buffer, Slice<uint8_t> split_bytes) {
120120 return SplitIterator{0, buffer, split_bytes};
121121}
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 @@
88#ifndef ZIG_UTIL_HPP
99#define ZIG_UTIL_HPP
1010
11#include "memory_profiling.hpp"
12
1113#include <stdlib.h>
1214#include <stdint.h>
1315#include <string.h>
......@@ -96,7 +98,10 @@ static inline int ctzll(unsigned long long mask) {
9698
9799
98100template<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
100105#ifndef NDEBUG
101106 // make behavior when size == 0 portable
102107 if (count == 0)
......@@ -109,7 +114,10 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate_nonzero(size_t count) {
109114}
110115
111116template<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
113121#ifndef NDEBUG
114122 // make behavior when size == 0 portable
115123 if (count == 0)
......@@ -122,7 +130,7 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count) {
122130}
123131
124132template<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) {
126134 T *ptr = reallocate_nonzero(old, old_count, new_count);
127135 if (new_count > old_count) {
128136 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) {
131139}
132140
133141template<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
135147#ifndef NDEBUG
136148 // make behavior when size == 0 portable
137149 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)
143155 return ptr;
144156}
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
146171template <typename T, size_t n>
147172constexpr size_t array_length(const T (&)[n]) {
148173 return n;
......@@ -225,6 +250,8 @@ static inline double zig_f16_to_double(float16_t x) {
225250 return z;
226251}
227252
253void zig_pretty_print_bytes(FILE *f, double n);
254
228255template<typename T>
229256struct Optional {
230257 T value;
test/cli.zig+2-2
......@@ -37,7 +37,7 @@ pub fn main() !void {
3737 testMissingOutputPath,
3838 };
3939 for (test_fns) |testFn| {
40 try fs.deleteTree(a, dir_path);
40 try fs.deleteTree(dir_path);
4141 try fs.makeDir(dir_path);
4242 try testFn(zig_exe, dir_path);
4343 }
......@@ -87,7 +87,7 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult {
8787fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void {
8888 _ = try exec(dir_path, [_][]const u8{ zig_exe, "init-lib" });
8989 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"));
9191}
9292
9393fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {
test/compile_errors.zig+10
......@@ -2,6 +2,16 @@ const tests = @import("tests.zig");
22const builtin = @import("builtin");
33
44pub 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
515 cases.add(
616 "switch with overlapping case ranges",
717 \\export fn entry() void {
test/stage1/behavior/switch.zig+18
......@@ -434,3 +434,21 @@ test "switch with disjoint range" {
434434 126...126 => {},
435435 }
436436}
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" {
521521 const x = U{ .x = 0x55AAAA55 };
522522 comptime expect(x.y == 0x55);
523523}
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 {
340340 try dir_stack.append(target_include_dir);
341341
342342 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) {
344344 error.FileNotFound => continue :search,
345345 error.AccessDenied => continue :search,
346346 else => return err,