diff --git a/.gitignore b/.gitignore index f85fc969b129..9fa6f71cc793 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ # -andrewrk zig-cache/ +/release/ +/debug/ /build/ /build-*/ /docgen_tmp/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 291a2f7839b8..2ed25b93d215 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,7 @@ set(ZIG_TARGET_MCPU "baseline" CACHE STRING "-mcpu parameter to output binaries set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary") set(ZIG_PREFER_LLVM_CONFIG off CACHE BOOL "(when cross compiling) use llvm-config to find target llvm dependencies if needed") set(ZIG_SINGLE_THREADED off CACHE BOOL "limit the zig compiler to use only 1 thread") +set(ZIG_OMIT_STAGE2 off CACHE BOOL "omit the stage2 backend from stage1") find_package(llvm) find_package(clang) @@ -587,6 +588,12 @@ if(MSVC) endif() endif() +if(ZIG_OMIT_STAGE2) + set(ZIG_OMIT_STAGE2_BOOL "true") +else() + set(ZIG_OMIT_STAGE2_BOOL "false") +endif() + configure_file ( "${CMAKE_SOURCE_DIR}/src/stage1/config.h.in" "${ZIG_CONFIG_H_OUT}" diff --git a/build.zig b/build.zig index fe588ea2cb35..77d1b573fdf6 100644 --- a/build.zig +++ b/build.zig @@ -91,6 +91,7 @@ pub fn build(b: *Builder) !void { exe.addBuildOption(bool, "have_llvm", enable_llvm); if (enable_llvm) { const cmake_cfg = if (static_llvm) null else findAndParseConfigH(b, config_h_path_option); + if (is_stage1) { exe.addIncludeDir("src"); exe.addIncludeDir("deps/SoftFloat-3e/source/include"); @@ -109,28 +110,8 @@ pub fn build(b: *Builder) !void { softfloat.addCSourceFiles(&softfloat_sources, &[_][]const u8{ "-std=c99", "-O3" }); exe.linkLibrary(softfloat); - const exe_cflags = [_][]const u8{ - "-std=c++14", - "-D__STDC_CONSTANT_MACROS", - "-D__STDC_FORMAT_MACROS", - "-D__STDC_LIMIT_MACROS", - "-D_GNU_SOURCE", - "-fvisibility-inlines-hidden", - "-fno-exceptions", - "-fno-rtti", - "-Werror=type-limits", - "-Wno-missing-braces", - "-Wno-comment", - }; exe.addCSourceFiles(&stage1_sources, &exe_cflags); exe.addCSourceFiles(&optimized_c_sources, &[_][]const u8{ "-std=c99", "-O3" }); - if (cmake_cfg == null) { - // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling - // in a dependency on llvm::cfg::Update::dump() which is - // unavailable when LLVM is compiled in Release mode. - const zig_cpp_cflags = exe_cflags ++ [_][]const u8{"-DNDEBUG=1"}; - exe.addCSourceFiles(&zig_cpp_sources, &zig_cpp_cflags); - } } if (cmake_cfg) |cfg| { // Inside this code path, we have to coordinate with system packaged LLVM, Clang, and LLD. @@ -139,79 +120,13 @@ pub fn build(b: *Builder) !void { if (cfg.cmake_prefix_path.len > 0) { b.addSearchPrefix(cfg.cmake_prefix_path); } - exe.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{ - cfg.cmake_binary_dir, - "zigcpp", - b.fmt("{s}{s}{s}", .{ exe.target.libPrefix(), "zigcpp", exe.target.staticLibSuffix() }), - }) catch unreachable); - assert(cfg.lld_include_dir.len != 0); - exe.addIncludeDir(cfg.lld_include_dir); - addCMakeLibraryList(exe, cfg.clang_libraries); - addCMakeLibraryList(exe, cfg.lld_libraries); - addCMakeLibraryList(exe, cfg.llvm_libraries); - - const need_cpp_includes = tracy != null; - - // System -lc++ must be used because in this code path we are attempting to link - // against system-provided LLVM, Clang, LLD. - if (exe.target.getOsTag() == .linux) { - // First we try to static link against gcc libstdc++. If that doesn't work, - // we fall back to -lc++ and cross our fingers. - addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) { - error.RequiredLibraryNotFound => { - exe.linkSystemLibrary("c++"); - }, - else => |e| return e, - }; - - exe.linkSystemLibrary("pthread"); - } else if (exe.target.isFreeBSD()) { - try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes); - exe.linkSystemLibrary("pthread"); - } else if (exe.target.getOsTag() == .openbsd) { - try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes); - try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes); - } else if (exe.target.isDarwin()) { - if (addCxxKnownPath(b, cfg, exe, "libgcc_eh.a", "", need_cpp_includes)) { - // Compiler is GCC. - try addCxxKnownPath(b, cfg, exe, "libstdc++.a", null, need_cpp_includes); - exe.linkSystemLibrary("pthread"); - // TODO LLD cannot perform this link. - // Set ZIG_SYSTEM_LINKER_HACK env var to use system linker ld instead. - // See https://github.com/ziglang/zig/issues/1535 - } else |err| switch (err) { - error.RequiredLibraryNotFound => { - // System compiler, not gcc. - exe.linkSystemLibrary("c++"); - }, - else => |e| return e, - } - } - if (cfg.dia_guids_lib.len != 0) { - exe.addObjectFile(cfg.dia_guids_lib); - } + try addCmakeCfgOptionsToExe(b, cfg, tracy, exe); + try addCmakeCfgOptionsToExe(b, cfg, tracy, test_stage2); } else { // Here we are -Denable-llvm but no cmake integration. - for (clang_libs) |lib_name| { - exe.linkSystemLibrary(lib_name); - } - - for (lld_libs) |lib_name| { - exe.linkSystemLibrary(lib_name); - } - - for (llvm_libs) |lib_name| { - exe.linkSystemLibrary(lib_name); - } - - // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries. - exe.linkSystemLibrary("c++"); - - if (target.getOs().tag == .windows) { - exe.linkSystemLibrary("version"); - exe.linkSystemLibrary("uuid"); - } + try addStaticLlvmOptionsToExe(exe); + try addStaticLlvmOptionsToExe(test_stage2); } } if (link_libc) { @@ -220,11 +135,10 @@ pub fn build(b: *Builder) !void { } const log_scopes = b.option([]const []const u8, "log", "Which log scopes to enable") orelse &[0][]const u8{}; - const zir_dumps = b.option([]const []const u8, "dump-zir", "Which functions to dump ZIR for before codegen") orelse &[0][]const u8{}; const opt_version_string = b.option([]const u8, "version-string", "Override Zig version string. Default is to find out with git."); const version = if (opt_version_string) |version| version else v: { - const version_string = b.fmt("{}.{}.{}", .{ zig_version.major, zig_version.minor, zig_version.patch }); + const version_string = b.fmt("{d}.{d}.{d}", .{ zig_version.major, zig_version.minor, zig_version.patch }); var code: u8 = undefined; const git_describe_untrimmed = b.execAllowFail(&[_][]const u8{ @@ -238,7 +152,7 @@ pub fn build(b: *Builder) !void { 0 => { // Tagged release version (e.g. 0.7.0). if (!mem.eql(u8, git_describe, version_string)) { - std.debug.print("Zig version '{}' does not match Git tag '{}'\n", .{ version_string, git_describe }); + std.debug.print("Zig version '{s}' does not match Git tag '{s}'\n", .{ version_string, git_describe }); std.process.exit(1); } break :v version_string; @@ -258,15 +172,15 @@ pub fn build(b: *Builder) !void { // Check that the commit hash is prefixed with a 'g' (a Git convention). if (commit_id.len < 1 or commit_id[0] != 'g') { - std.debug.print("Unexpected `git describe` output: {}\n", .{git_describe}); + std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe}); break :v version_string; } // The version is reformatted in accordance with the https://semver.org specification. - break :v b.fmt("{}-dev.{}+{}", .{ version_string, commit_height, commit_id[1..] }); + break :v b.fmt("{s}-dev.{s}+{s}", .{ version_string, commit_height, commit_id[1..] }); }, else => { - std.debug.print("Unexpected `git describe` output: {}\n", .{git_describe}); + std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe}); break :v version_string; }, } @@ -277,9 +191,9 @@ pub fn build(b: *Builder) !void { exe.addBuildOption(std.SemanticVersion, "semver", semver); exe.addBuildOption([]const []const u8, "log_scopes", log_scopes); - exe.addBuildOption([]const []const u8, "zir_dumps", zir_dumps); exe.addBuildOption(bool, "enable_tracy", tracy != null); exe.addBuildOption(bool, "is_stage1", is_stage1); + exe.addBuildOption(bool, "omit_stage2", false); if (tracy) |tracy_path| { const client_cpp = fs.path.join( b.allocator, @@ -302,6 +216,7 @@ pub fn build(b: *Builder) !void { test_stage2.addBuildOption(bool, "skip_non_native", skip_non_native); test_stage2.addBuildOption(bool, "is_stage1", is_stage1); + test_stage2.addBuildOption(bool, "omit_stage2", false); test_stage2.addBuildOption(bool, "have_llvm", enable_llvm); test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled); test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled); @@ -359,6 +274,112 @@ pub fn build(b: *Builder) !void { test_step.dependOn(docs_step); } +const exe_cflags = [_][]const u8{ + "-std=c++14", + "-D__STDC_CONSTANT_MACROS", + "-D__STDC_FORMAT_MACROS", + "-D__STDC_LIMIT_MACROS", + "-D_GNU_SOURCE", + "-fvisibility-inlines-hidden", + "-fno-exceptions", + "-fno-rtti", + "-Werror=type-limits", + "-Wno-missing-braces", + "-Wno-comment", +}; + +fn addCmakeCfgOptionsToExe( + b: *Builder, + cfg: CMakeConfig, + tracy: ?[]const u8, + exe: *std.build.LibExeObjStep, +) !void { + exe.addObjectFile(fs.path.join(b.allocator, &[_][]const u8{ + cfg.cmake_binary_dir, + "zigcpp", + b.fmt("{s}{s}{s}", .{ exe.target.libPrefix(), "zigcpp", exe.target.staticLibSuffix() }), + }) catch unreachable); + assert(cfg.lld_include_dir.len != 0); + exe.addIncludeDir(cfg.lld_include_dir); + addCMakeLibraryList(exe, cfg.clang_libraries); + addCMakeLibraryList(exe, cfg.lld_libraries); + addCMakeLibraryList(exe, cfg.llvm_libraries); + + const need_cpp_includes = tracy != null; + + // System -lc++ must be used because in this code path we are attempting to link + // against system-provided LLVM, Clang, LLD. + if (exe.target.getOsTag() == .linux) { + // First we try to static link against gcc libstdc++. If that doesn't work, + // we fall back to -lc++ and cross our fingers. + addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) { + error.RequiredLibraryNotFound => { + exe.linkSystemLibrary("c++"); + }, + else => |e| return e, + }; + + exe.linkSystemLibrary("pthread"); + } else if (exe.target.isFreeBSD()) { + try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes); + exe.linkSystemLibrary("pthread"); + } else if (exe.target.getOsTag() == .openbsd) { + try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes); + try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes); + } else if (exe.target.isDarwin()) { + if (addCxxKnownPath(b, cfg, exe, "libgcc_eh.a", "", need_cpp_includes)) { + // Compiler is GCC. + try addCxxKnownPath(b, cfg, exe, "libstdc++.a", null, need_cpp_includes); + exe.linkSystemLibrary("pthread"); + // TODO LLD cannot perform this link. + // Set ZIG_SYSTEM_LINKER_HACK env var to use system linker ld instead. + // See https://github.com/ziglang/zig/issues/1535 + } else |err| switch (err) { + error.RequiredLibraryNotFound => { + // System compiler, not gcc. + exe.linkSystemLibrary("c++"); + }, + else => |e| return e, + } + } + + if (cfg.dia_guids_lib.len != 0) { + exe.addObjectFile(cfg.dia_guids_lib); + } +} + +fn addStaticLlvmOptionsToExe( + exe: *std.build.LibExeObjStep, +) !void { + // Adds the Zig C++ sources which both stage1 and stage2 need. + // + // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling + // in a dependency on llvm::cfg::Update::dump() which is + // unavailable when LLVM is compiled in Release mode. + const zig_cpp_cflags = exe_cflags ++ [_][]const u8{"-DNDEBUG=1"}; + exe.addCSourceFiles(&zig_cpp_sources, &zig_cpp_cflags); + + for (clang_libs) |lib_name| { + exe.linkSystemLibrary(lib_name); + } + + for (lld_libs) |lib_name| { + exe.linkSystemLibrary(lib_name); + } + + for (llvm_libs) |lib_name| { + exe.linkSystemLibrary(lib_name); + } + + // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries. + exe.linkSystemLibrary("c++"); + + if (exe.target.getOs().tag == .windows) { + exe.linkSystemLibrary("version"); + exe.linkSystemLibrary("uuid"); + } +} + fn addCxxKnownPath( b: *Builder, ctx: CMakeConfig, @@ -369,14 +390,14 @@ fn addCxxKnownPath( ) !void { const path_padded = try b.exec(&[_][]const u8{ ctx.cxx_compiler, - b.fmt("-print-file-name={}", .{objname}), + b.fmt("-print-file-name={s}", .{objname}), }); const path_unpadded = mem.tokenize(path_padded, "\r\n").next().?; if (mem.eql(u8, path_unpadded, objname)) { if (errtxt) |msg| { - warn("{}", .{msg}); + warn("{s}", .{msg}); } else { - warn("Unable to determine path to {}\n", .{objname}); + warn("Unable to determine path to {s}\n", .{objname}); } return error.RequiredLibraryNotFound; } diff --git a/ci/azure/windows_msvc_script.bat b/ci/azure/windows_msvc_script.bat index cd5e3d5bca39..9d28eccd0bba 100644 --- a/ci/azure/windows_msvc_script.bat +++ b/ci/azure/windows_msvc_script.bat @@ -23,11 +23,12 @@ git.exe fetch --tags mkdir %ZIGBUILDDIR% cd %ZIGBUILDDIR% -cmake.exe .. -Thost=x64 -G"Visual Studio 16 2019" -A x64 "-DCMAKE_INSTALL_PREFIX=%ZIGINSTALLDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release || exit /b +cmake.exe .. -Thost=x64 -G"Visual Studio 16 2019" -A x64 "-DCMAKE_INSTALL_PREFIX=%ZIGINSTALLDIR%" "-DCMAKE_PREFIX_PATH=%ZIGPREFIXPATH%" -DCMAKE_BUILD_TYPE=Release -DZIG_OMIT_STAGE2=ON || exit /b msbuild /maxcpucount /p:Configuration=Release INSTALL.vcxproj || exit /b "%ZIGINSTALLDIR%\bin\zig.exe" build test-behavior -Dskip-non-native || exit /b -"%ZIGINSTALLDIR%\bin\zig.exe" build test-stage2 -Dskip-non-native || exit /b +REM Disabled to prevent OOM +REM "%ZIGINSTALLDIR%\bin\zig.exe" build test-stage2 -Dskip-non-native || exit /b "%ZIGINSTALLDIR%\bin\zig.exe" build test-fmt -Dskip-non-native || exit /b "%ZIGINSTALLDIR%\bin\zig.exe" build test-std -Dskip-non-native || exit /b "%ZIGINSTALLDIR%\bin\zig.exe" build test-compiler-rt -Dskip-non-native || exit /b diff --git a/ci/drone/linux_script b/ci/drone/linux_script index 422949e60752..dbe13f6f19a3 100755 --- a/ci/drone/linux_script +++ b/ci/drone/linux_script @@ -22,7 +22,7 @@ cd build cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STATIC=ON -DCMAKE_PREFIX_PATH=/deps/local -GNinja samu install -./zig build test -Dskip-release -Dskip-non-native +./zig build test -Dskip-release -Dskip-non-native -Dskip-compile-errors if [ -z "$DRONE_PULL_REQUEST" ]; then mv ../LICENSE "$DISTDIR/" diff --git a/doc/docgen.zig b/doc/docgen.zig index e995e2f3241c..7e0bedbb6c61 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -215,9 +215,9 @@ const Tokenizer = struct { fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, args: anytype) anyerror { const loc = tokenizer.getTokenLocation(token); const args_prefix = .{ tokenizer.source_file_name, loc.line + 1, loc.column + 1 }; - print("{}:{}:{}: error: " ++ fmt ++ "\n", args_prefix ++ args); + print("{s}:{d}:{d}: error: " ++ fmt ++ "\n", args_prefix ++ args); if (loc.line_start <= loc.line_end) { - print("{}\n", .{tokenizer.buffer[loc.line_start..loc.line_end]}); + print("{s}\n", .{tokenizer.buffer[loc.line_start..loc.line_end]}); { var i: usize = 0; while (i < loc.column) : (i += 1) { @@ -238,7 +238,7 @@ fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, arg fn assertToken(tokenizer: *Tokenizer, token: Token, id: Token.Id) !void { if (token.id != id) { - return parseError(tokenizer, token, "expected {}, found {}", .{ @tagName(id), @tagName(token.id) }); + return parseError(tokenizer, token, "expected {s}, found {s}", .{ @tagName(id), @tagName(token.id) }); } } @@ -374,7 +374,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { return parseError( tokenizer, bracket_tok, - "unrecognized header_open param: {}", + "unrecognized header_open param: {s}", .{param}, ); } @@ -394,7 +394,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { }, }); if (try urls.fetchPut(urlized, tag_token)) |entry| { - parseError(tokenizer, tag_token, "duplicate header url: #{}", .{urlized}) catch {}; + parseError(tokenizer, tag_token, "duplicate header url: #{s}", .{urlized}) catch {}; parseError(tokenizer, entry.value, "other tag here", .{}) catch {}; return error.ParseError; } @@ -411,7 +411,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { } last_columns = columns; try toc.writeByteNTimes(' ', 4 + header_stack_size * 4); - try toc.print("
  • {}", .{ urlized, urlized, content }); + try toc.print("
  • {s}", .{ urlized, urlized, content }); } else if (mem.eql(u8, tag_name, "header_close")) { if (header_stack_size == 0) { return parseError(tokenizer, tag_token, "unbalanced close header", .{}); @@ -515,7 +515,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { code_kind_id = Code.Id{ .Obj = null }; is_inline = true; } else { - return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {}", .{code_kind_str}); + return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {s}", .{code_kind_str}); } var mode: builtin.Mode = .Debug; @@ -559,7 +559,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { return parseError( tokenizer, end_code_tag, - "invalid token inside code_begin: {}", + "invalid token inside code_begin: {s}", .{end_tag_name}, ); } @@ -590,14 +590,14 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { return parseError( tokenizer, end_syntax_tag, - "invalid token inside syntax: {}", + "invalid token inside syntax: {s}", .{end_tag_name}, ); } _ = try eatToken(tokenizer, Token.Id.BracketClose); try nodes.append(Node{ .Syntax = content_tok }); } else { - return parseError(tokenizer, tag_token, "unrecognized tag name: {}", .{tag_name}); + return parseError(tokenizer, tag_token, "unrecognized tag name: {s}", .{tag_name}); } }, else => return parseError(tokenizer, token, "invalid token", .{}), @@ -744,7 +744,7 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 { try out.writeAll(""); } if (first_number != 0 or second_number != 0) { - try out.print("", .{ first_number, second_number }); + try out.print("", .{ first_number, second_number }); open_span_count += 1; } }, @@ -1004,9 +1004,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any }, .Link => |info| { if (!toc.urls.contains(info.url)) { - return parseError(tokenizer, info.token, "url not found: {}", .{info.url}); + return parseError(tokenizer, info.token, "url not found: {s}", .{info.url}); } - try out.print("{}", .{ info.url, info.name }); + try out.print("{s}", .{ info.url, info.name }); }, .Nav => { try out.writeAll(toc.toc); @@ -1018,7 +1018,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any }, .HeaderOpen => |info| { try out.print( - "{} §\n", + "{s} §\n", .{ info.n, info.url, info.url, info.name, info.url, info.n }, ); }, @@ -1027,9 +1027,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any for (items) |item| { const url = try urlize(allocator, item.name); if (!toc.urls.contains(url)) { - return parseError(tokenizer, item.token, "url not found: {}", .{url}); + return parseError(tokenizer, item.token, "url not found: {s}", .{url}); } - try out.print("
  • {}
  • \n", .{ url, item.name }); + try out.print("
  • {s}
  • \n", .{ url, item.name }); } try out.writeAll("\n"); }, @@ -1043,12 +1043,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any const raw_source = tokenizer.buffer[code.source_token.start..code.source_token.end]; const trimmed_raw_source = mem.trim(u8, raw_source, " \n"); if (!code.is_inline) { - try out.print("

    {}.zig

    ", .{code.name}); + try out.print("

    {s}.zig

    ", .{code.name}); } try out.writeAll("
    ");
                     try tokenizeAndPrint(tokenizer, out, code.source_token);
                     try out.writeAll("
    "); - const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", .{code.name}); + const name_plus_ext = try std.fmt.allocPrint(allocator, "{s}.zig", .{code.name}); const tmp_source_file_name = try fs.path.join( allocator, &[_][]const u8{ tmp_dir_name, name_plus_ext }, @@ -1057,7 +1057,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any switch (code.id) { Code.Id.Exe => |expected_outcome| code_block: { - const name_plus_bin_ext = try std.fmt.allocPrint(allocator, "{}{}", .{ code.name, exe_ext }); + const name_plus_bin_ext = try std.fmt.allocPrint(allocator, "{s}{s}", .{ code.name, exe_ext }); var build_args = std.ArrayList([]const u8).init(allocator); defer build_args.deinit(); try build_args.appendSlice(&[_][]const u8{ @@ -1066,7 +1066,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any "--color", "on", "--enable-cache", tmp_source_file_name, }); - try out.print("
    $ zig build-exe {}.zig", .{code.name});
    +                        try out.print("
    $ zig build-exe {s}.zig", .{code.name});
                             switch (code.mode) {
                                 .Debug => {},
                                 else => {
    @@ -1075,7 +1075,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                                 },
                             }
                             for (code.link_objects) |link_object| {
    -                            const name_with_ext = try std.fmt.allocPrint(allocator, "{}{}", .{ link_object, obj_ext });
    +                            const name_with_ext = try std.fmt.allocPrint(allocator, "{s}{s}", .{ link_object, obj_ext });
                                 const full_path_object = try fs.path.join(
                                     allocator,
                                     &[_][]const u8{ tmp_dir_name, name_with_ext },
    @@ -1093,7 +1093,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                             if (code.target_str) |triple| {
                                 try build_args.appendSlice(&[_][]const u8{ "-target", triple });
                                 if (!code.is_inline) {
    -                                try out.print(" -target {}", .{triple});
    +                                try out.print(" -target {s}", .{triple});
                                 }
                             }
                             if (expected_outcome == .BuildFail) {
    @@ -1106,20 +1106,20 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                                 switch (result.term) {
                                     .Exited => |exit_code| {
                                         if (exit_code == 0) {
    -                                        print("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
    +                                        print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
                                             dumpArgs(build_args.items);
                                             return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
                                         }
                                     },
                                     else => {
    -                                    print("{}\nThe following command crashed:\n", .{result.stderr});
    +                                    print("{s}\nThe following command crashed:\n", .{result.stderr});
                                         dumpArgs(build_args.items);
                                         return parseError(tokenizer, code.source_token, "example compile crashed", .{});
                                     },
                                 }
                                 const escaped_stderr = try escapeHtml(allocator, result.stderr);
                                 const colored_stderr = try termColor(allocator, escaped_stderr);
    -                            try out.print("\n{}
    \n", .{colored_stderr}); + try out.print("\n{s}
    \n", .{colored_stderr}); break :code_block; } const exec_result = exec(allocator, &env_map, build_args.items) catch @@ -1138,7 +1138,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any } const path_to_exe_dir = mem.trim(u8, exec_result.stdout, " \r\n"); - const path_to_exe_basename = try std.fmt.allocPrint(allocator, "{}{}", .{ + const path_to_exe_basename = try std.fmt.allocPrint(allocator, "{s}{s}", .{ code.name, target.exeFileExt(), }); @@ -1160,7 +1160,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any switch (result.term) { .Exited => |exit_code| { if (exit_code == 0) { - print("{}\nThe following command incorrectly succeeded:\n", .{result.stderr}); + print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr}); dumpArgs(run_args); return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{}); } @@ -1179,7 +1179,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any const colored_stderr = try termColor(allocator, escaped_stderr); const colored_stdout = try termColor(allocator, escaped_stdout); - try out.print("\n$ ./{}\n{}{}", .{ code.name, colored_stdout, colored_stderr }); + try out.print("\n$ ./{s}\n{s}{s}", .{ code.name, colored_stdout, colored_stderr }); if (exited_with_signal) { try out.print("(process terminated by signal)", .{}); } @@ -1190,7 +1190,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any defer test_args.deinit(); try test_args.appendSlice(&[_][]const u8{ zig_exe, "test", tmp_source_file_name }); - try out.print("
    $ zig test {}.zig", .{code.name});
    +                        try out.print("
    $ zig test {s}.zig", .{code.name});
                             switch (code.mode) {
                                 .Debug => {},
                                 else => {
    @@ -1204,12 +1204,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                             }
                             if (code.target_str) |triple| {
                                 try test_args.appendSlice(&[_][]const u8{ "-target", triple });
    -                            try out.print(" -target {}", .{triple});
    +                            try out.print(" -target {s}", .{triple});
                             }
                             const result = exec(allocator, &env_map, test_args.items) catch return parseError(tokenizer, code.source_token, "test failed", .{});
                             const escaped_stderr = try escapeHtml(allocator, result.stderr);
                             const escaped_stdout = try escapeHtml(allocator, result.stdout);
    -                        try out.print("\n{}{}
    \n", .{ escaped_stderr, escaped_stdout }); + try out.print("\n{s}{s}
    \n", .{ escaped_stderr, escaped_stdout }); }, Code.Id.TestError => |error_match| { var test_args = std.ArrayList([]const u8).init(allocator); @@ -1222,7 +1222,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any "on", tmp_source_file_name, }); - try out.print("
    $ zig test {}.zig", .{code.name});
    +                        try out.print("
    $ zig test {s}.zig", .{code.name});
                             switch (code.mode) {
                                 .Debug => {},
                                 else => {
    @@ -1239,24 +1239,24 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                             switch (result.term) {
                                 .Exited => |exit_code| {
                                     if (exit_code == 0) {
    -                                    print("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
    +                                    print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
                                         dumpArgs(test_args.items);
                                         return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
                                     }
                                 },
                                 else => {
    -                                print("{}\nThe following command crashed:\n", .{result.stderr});
    +                                print("{s}\nThe following command crashed:\n", .{result.stderr});
                                     dumpArgs(test_args.items);
                                     return parseError(tokenizer, code.source_token, "example compile crashed", .{});
                                 },
                             }
                             if (mem.indexOf(u8, result.stderr, error_match) == null) {
    -                            print("{}\nExpected to find '{}' in stderr\n", .{ result.stderr, error_match });
    +                            print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });
                                 return parseError(tokenizer, code.source_token, "example did not have expected compile error", .{});
                             }
                             const escaped_stderr = try escapeHtml(allocator, result.stderr);
                             const colored_stderr = try termColor(allocator, escaped_stderr);
    -                        try out.print("\n{}
    \n", .{colored_stderr}); + try out.print("\n{s}
    \n", .{colored_stderr}); }, Code.Id.TestSafety => |error_match| { @@ -1294,31 +1294,31 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any switch (result.term) { .Exited => |exit_code| { if (exit_code == 0) { - print("{}\nThe following command incorrectly succeeded:\n", .{result.stderr}); + print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr}); dumpArgs(test_args.items); return parseError(tokenizer, code.source_token, "example test incorrectly succeeded", .{}); } }, else => { - print("{}\nThe following command crashed:\n", .{result.stderr}); + print("{s}\nThe following command crashed:\n", .{result.stderr}); dumpArgs(test_args.items); return parseError(tokenizer, code.source_token, "example compile crashed", .{}); }, } if (mem.indexOf(u8, result.stderr, error_match) == null) { - print("{}\nExpected to find '{}' in stderr\n", .{ result.stderr, error_match }); + print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match }); return parseError(tokenizer, code.source_token, "example did not have expected runtime safety error message", .{}); } const escaped_stderr = try escapeHtml(allocator, result.stderr); const colored_stderr = try termColor(allocator, escaped_stderr); - try out.print("
    $ zig test {}.zig{}\n{}
    \n", .{ + try out.print("
    $ zig test {s}.zig{s}\n{s}
    \n", .{ code.name, mode_arg, colored_stderr, }); }, Code.Id.Obj => |maybe_error_match| { - const name_plus_obj_ext = try std.fmt.allocPrint(allocator, "{}{}", .{ code.name, obj_ext }); + const name_plus_obj_ext = try std.fmt.allocPrint(allocator, "{s}{s}", .{ code.name, obj_ext }); const tmp_obj_file_name = try fs.path.join( allocator, &[_][]const u8{ tmp_dir_name, name_plus_obj_ext }, @@ -1326,7 +1326,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any var build_args = std.ArrayList([]const u8).init(allocator); defer build_args.deinit(); - const name_plus_h_ext = try std.fmt.allocPrint(allocator, "{}.h", .{code.name}); + const name_plus_h_ext = try std.fmt.allocPrint(allocator, "{s}.h", .{code.name}); const output_h_file_name = try fs.path.join( allocator, &[_][]const u8{ tmp_dir_name, name_plus_h_ext }, @@ -1345,7 +1345,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any }), }); if (!code.is_inline) { - try out.print("
    $ zig build-obj {}.zig", .{code.name});
    +                            try out.print("
    $ zig build-obj {s}.zig", .{code.name});
                             }
     
                             switch (code.mode) {
    @@ -1360,7 +1360,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
     
                             if (code.target_str) |triple| {
                                 try build_args.appendSlice(&[_][]const u8{ "-target", triple });
    -                            try out.print(" -target {}", .{triple});
    +                            try out.print(" -target {s}", .{triple});
                             }
     
                             if (maybe_error_match) |error_match| {
    @@ -1373,24 +1373,24 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                                 switch (result.term) {
                                     .Exited => |exit_code| {
                                         if (exit_code == 0) {
    -                                        print("{}\nThe following command incorrectly succeeded:\n", .{result.stderr});
    +                                        print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
                                             dumpArgs(build_args.items);
                                             return parseError(tokenizer, code.source_token, "example build incorrectly succeeded", .{});
                                         }
                                     },
                                     else => {
    -                                    print("{}\nThe following command crashed:\n", .{result.stderr});
    +                                    print("{s}\nThe following command crashed:\n", .{result.stderr});
                                         dumpArgs(build_args.items);
                                         return parseError(tokenizer, code.source_token, "example compile crashed", .{});
                                     },
                                 }
                                 if (mem.indexOf(u8, result.stderr, error_match) == null) {
    -                                print("{}\nExpected to find '{}' in stderr\n", .{ result.stderr, error_match });
    +                                print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });
                                     return parseError(tokenizer, code.source_token, "example did not have expected compile error message", .{});
                                 }
                                 const escaped_stderr = try escapeHtml(allocator, result.stderr);
                                 const colored_stderr = try termColor(allocator, escaped_stderr);
    -                            try out.print("\n{}", .{colored_stderr});
    +                            try out.print("\n{s}", .{colored_stderr});
                             } else {
                                 _ = exec(allocator, &env_map, build_args.items) catch return parseError(tokenizer, code.source_token, "example failed to compile", .{});
                             }
    @@ -1416,7 +1416,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                                     tmp_dir_name, fs.path.sep_str, bin_basename,
                                 }),
                             });
    -                        try out.print("
    $ zig build-lib {}.zig", .{code.name});
    +                        try out.print("
    $ zig build-lib {s}.zig", .{code.name});
                             switch (code.mode) {
                                 .Debug => {},
                                 else => {
    @@ -1426,12 +1426,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any
                             }
                             if (code.target_str) |triple| {
                                 try test_args.appendSlice(&[_][]const u8{ "-target", triple });
    -                            try out.print(" -target {}", .{triple});
    +                            try out.print(" -target {s}", .{triple});
                             }
                             const result = exec(allocator, &env_map, test_args.items) catch return parseError(tokenizer, code.source_token, "test failed", .{});
                             const escaped_stderr = try escapeHtml(allocator, result.stderr);
                             const escaped_stdout = try escapeHtml(allocator, result.stdout);
    -                        try out.print("\n{}{}
    \n", .{ escaped_stderr, escaped_stdout }); + try out.print("\n{s}{s}
    \n", .{ escaped_stderr, escaped_stdout }); }, } print("OK\n", .{}); @@ -1450,13 +1450,13 @@ fn exec(allocator: *mem.Allocator, env_map: *std.BufMap, args: []const []const u switch (result.term) { .Exited => |exit_code| { if (exit_code != 0) { - print("{}\nThe following command exited with code {}:\n", .{ result.stderr, exit_code }); + print("{s}\nThe following command exited with code {}:\n", .{ result.stderr, exit_code }); dumpArgs(args); return error.ChildExitError; } }, else => { - print("{}\nThe following command crashed:\n", .{result.stderr}); + print("{s}\nThe following command crashed:\n", .{result.stderr}); dumpArgs(args); return error.ChildCrashed; }, @@ -1471,7 +1471,7 @@ fn getBuiltinCode(allocator: *mem.Allocator, env_map: *std.BufMap, zig_exe: []co fn dumpArgs(args: []const []const u8) void { for (args) |arg| - print("{} ", .{arg}) + print("{s} ", .{arg}) else print("\n", .{}); } diff --git a/doc/langref.html.in b/doc/langref.html.in index 7c8d1c6662a3..9d89e894ad61 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -236,7 +236,7 @@ const std = @import("std"); pub fn main() !void { const stdout = std.io.getStdOut().writer(); - try stdout.print("Hello, {}!\n", .{"world"}); + try stdout.print("Hello, {s}!\n", .{"world"}); } {#code_end#}

    @@ -308,7 +308,7 @@ pub fn main() !void { multiple arguments passed to a function, they are separated by commas ,.

    - The two arguments passed to the stdout.print() function, "Hello, {}!\n" + The two arguments passed to the stdout.print() function, "Hello, {s}!\n" and .{"world"}, are evaluated at {#link|compile-time|comptime#}. The code sample is purposely written to show how to perform {#link|string|String Literals and Character Literals#} substitution in the print function. The curly-braces inside of the first argument @@ -435,7 +435,7 @@ pub fn main() void { var optional_value: ?[]const u8 = null; assert(optional_value == null); - print("\noptional 1\ntype: {}\nvalue: {}\n", .{ + print("\noptional 1\ntype: {s}\nvalue: {s}\n", .{ @typeName(@TypeOf(optional_value)), optional_value, }); @@ -443,7 +443,7 @@ pub fn main() void { optional_value = "hi"; assert(optional_value != null); - print("\noptional 2\ntype: {}\nvalue: {}\n", .{ + print("\noptional 2\ntype: {s}\nvalue: {s}\n", .{ @typeName(@TypeOf(optional_value)), optional_value, }); @@ -451,14 +451,14 @@ pub fn main() void { // error union var number_or_error: anyerror!i32 = error.ArgNotFound; - print("\nerror union 1\ntype: {}\nvalue: {}\n", .{ + print("\nerror union 1\ntype: {s}\nvalue: {}\n", .{ @typeName(@TypeOf(number_or_error)), number_or_error, }); number_or_error = 1234; - print("\nerror union 2\ntype: {}\nvalue: {}\n", .{ + print("\nerror union 2\ntype: {s}\nvalue: {}\n", .{ @typeName(@TypeOf(number_or_error)), number_or_error, }); @@ -2339,7 +2339,7 @@ test "using slices for strings" { // You can use slice syntax on an array to convert an array into a slice. const all_together_slice = all_together[0..]; // String concatenation example. - const hello_world = try fmt.bufPrint(all_together_slice, "{} {}", .{ hello, world }); + const hello_world = try fmt.bufPrint(all_together_slice, "{s} {s}", .{ hello, world }); // Generally, you can use UTF-8 and not worry about whether something is a // string. If you don't need to deal with individual characters, no need @@ -2772,9 +2772,9 @@ const std = @import("std"); pub fn main() void { const Foo = struct {}; - std.debug.print("variable: {}\n", .{@typeName(Foo)}); - std.debug.print("anonymous: {}\n", .{@typeName(struct {})}); - std.debug.print("function: {}\n", .{@typeName(List(i32))}); + std.debug.print("variable: {s}\n", .{@typeName(Foo)}); + std.debug.print("anonymous: {s}\n", .{@typeName(struct {})}); + std.debug.print("function: {s}\n", .{@typeName(List(i32))}); } fn List(comptime T: type) type { @@ -6110,7 +6110,7 @@ const a_number: i32 = 1234; const a_string = "foobar"; pub fn main() void { - print("here is a string: '{}' here is a number: {}\n", .{a_string, a_number}); + print("here is a string: '{s}' here is a number: {}\n", .{a_string, a_number}); } {#code_end#} @@ -6230,7 +6230,7 @@ const a_number: i32 = 1234; const a_string = "foobar"; test "printf too many arguments" { - print("here is a string: '{}' here is a number: {}\n", .{ + print("here is a string: '{s}' here is a number: {}\n", .{ a_string, a_number, a_number, @@ -6249,7 +6249,7 @@ const print = @import("std").debug.print; const a_number: i32 = 1234; const a_string = "foobar"; -const fmt = "here is a string: '{}' here is a number: {}\n"; +const fmt = "here is a string: '{s}' here is a number: {}\n"; pub fn main() void { print(fmt, .{a_string, a_number}); @@ -6720,8 +6720,8 @@ fn amain() !void { const download_text = try await download_frame; defer allocator.free(download_text); - std.debug.print("download_text: {}\n", .{download_text}); - std.debug.print("file_text: {}\n", .{file_text}); + std.debug.print("download_text: {s}\n", .{download_text}); + std.debug.print("file_text: {s}\n", .{file_text}); } var global_download_frame: anyframe = undefined; @@ -6790,8 +6790,8 @@ fn amain() !void { const download_text = try await download_frame; defer allocator.free(download_text); - std.debug.print("download_text: {}\n", .{download_text}); - std.debug.print("file_text: {}\n", .{file_text}); + std.debug.print("download_text: {s}\n", .{download_text}); + std.debug.print("file_text: {s}\n", .{file_text}); } fn fetchUrl(allocator: *Allocator, url: []const u8) ![]u8 { @@ -8848,7 +8848,7 @@ pub fn main() !void { var byte: u8 = 255; byte = if (math.add(u8, byte, 1)) |result| result else |err| { - print("unable to add one: {}\n", .{@errorName(err)}); + print("unable to add one: {s}\n", .{@errorName(err)}); return err; }; @@ -9078,7 +9078,7 @@ pub fn main() void { if (result) |number| { print("got number: {}\n", .{number}); } else |err| { - print("got error: {}\n", .{@errorName(err)}); + print("got error: {s}\n", .{@errorName(err)}); } } @@ -9135,7 +9135,7 @@ const Foo = enum { pub fn main() void { var a: u2 = 3; var b = @intToEnum(Foo, a); - std.debug.print("value: {}\n", .{@tagName(b)}); + std.debug.print("value: {s}\n", .{@tagName(b)}); } {#code_end#} {#header_close#} @@ -10025,7 +10025,7 @@ pub fn main() !void { defer std.process.argsFree(gpa, args); for (args) |arg, i| { - std.debug.print("{}: {}\n", .{ i, arg }); + std.debug.print("{}: {s}\n", .{ i, arg }); } } {#code_end#} diff --git a/lib/libc/include/any-macos-any/libkern/OSAtomic.h b/lib/libc/include/aarch64-macos-gnu/libkern/OSAtomic.h similarity index 100% rename from lib/libc/include/any-macos-any/libkern/OSAtomic.h rename to lib/libc/include/aarch64-macos-gnu/libkern/OSAtomic.h diff --git a/lib/libc/include/any-macos-any/libkern/OSSpinLockDeprecated.h b/lib/libc/include/aarch64-macos-gnu/libkern/OSSpinLockDeprecated.h similarity index 100% rename from lib/libc/include/any-macos-any/libkern/OSSpinLockDeprecated.h rename to lib/libc/include/aarch64-macos-gnu/libkern/OSSpinLockDeprecated.h diff --git a/lib/libc/include/x86_64-macos-gnu/libkern/OSAtomic.h b/lib/libc/include/x86_64-macos-gnu/libkern/OSAtomic.h new file mode 100644 index 000000000000..37ef16ce4454 --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/libkern/OSAtomic.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2004-2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _OSATOMIC_H_ +#define _OSATOMIC_H_ + +/*! @header + * These are deprecated legacy interfaces for atomic and synchronization + * operations. + * + * Define OSATOMIC_USE_INLINED=1 to get inline implementations of the + * OSAtomic interfaces in terms of the primitives. + * + * Define OSSPINLOCK_USE_INLINED=1 to get inline implementations of the + * OSSpinLock interfaces in terms of the primitives. + * + * These are intended as a transition convenience, direct use of those + * primitives should be preferred. + */ + +#include + +#include "OSAtomicDeprecated.h" +#include "OSSpinLockDeprecated.h" +#include "OSAtomicQueue.h" + +#endif /* _OSATOMIC_H_ */ \ No newline at end of file diff --git a/lib/libc/include/x86_64-macos-gnu/libkern/OSSpinLockDeprecated.h b/lib/libc/include/x86_64-macos-gnu/libkern/OSSpinLockDeprecated.h new file mode 100644 index 000000000000..a654a7bbc2d8 --- /dev/null +++ b/lib/libc/include/x86_64-macos-gnu/libkern/OSSpinLockDeprecated.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2004-2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _OSSPINLOCK_DEPRECATED_H_ +#define _OSSPINLOCK_DEPRECATED_H_ + +/*! @header + * These are deprecated legacy interfaces for userspace spinlocks. + * + * These interfaces should no longer be used, particularily in situations where + * threads of differing priorities may contend on the same spinlock. + * + * The interfaces in should be used instead in cases where a very + * low-level lock primitive is required. In general however, using higher level + * synchronization primitives such as those provided by the pthread or dispatch + * subsystems should be preferred. + * + * Define OSSPINLOCK_USE_INLINED=1 to get inline implementations of these + * interfaces in terms of the primitives. This is intended as a + * transition convenience, direct use of those primitives is preferred. + */ + +#ifndef OSSPINLOCK_DEPRECATED +#define OSSPINLOCK_DEPRECATED 1 +#define OSSPINLOCK_DEPRECATED_MSG(_r) "Use " #_r "() from instead" +#define OSSPINLOCK_DEPRECATED_REPLACE_WITH(_r) \ + __OS_AVAILABILITY_MSG(macosx, deprecated=10.12, OSSPINLOCK_DEPRECATED_MSG(_r)) \ + __OS_AVAILABILITY_MSG(ios, deprecated=10.0, OSSPINLOCK_DEPRECATED_MSG(_r)) \ + __OS_AVAILABILITY_MSG(tvos, deprecated=10.0, OSSPINLOCK_DEPRECATED_MSG(_r)) \ + __OS_AVAILABILITY_MSG(watchos, deprecated=3.0, OSSPINLOCK_DEPRECATED_MSG(_r)) +#else +#undef OSSPINLOCK_DEPRECATED +#define OSSPINLOCK_DEPRECATED 0 +#define OSSPINLOCK_DEPRECATED_REPLACE_WITH(_r) +#endif + +#if !(defined(OSSPINLOCK_USE_INLINED) && OSSPINLOCK_USE_INLINED) + +#include +#include +#include +#include +#include + +__BEGIN_DECLS + +/*! @abstract The default value for an OSSpinLock. + @discussion + The convention is that unlocked is zero, locked is nonzero. + */ +#define OS_SPINLOCK_INIT 0 + + +/*! @abstract Data type for a spinlock. + @discussion + You should always initialize a spinlock to {@link OS_SPINLOCK_INIT} before + using it. + */ +typedef int32_t OSSpinLock OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock); + + +/*! @abstract Locks a spinlock if it would not block + @result + Returns false if the lock was already held by another thread, + true if it took the lock successfully. + */ +OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_trylock) +__OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0) +bool OSSpinLockTry( volatile OSSpinLock *__lock ); + + +/*! @abstract Locks a spinlock + @discussion + Although the lock operation spins, it employs various strategies to back + off if the lock is held. + */ +OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_lock) +__OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0) +void OSSpinLockLock( volatile OSSpinLock *__lock ); + + +/*! @abstract Unlocks a spinlock */ +OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_unlock) +__OSX_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0) +void OSSpinLockUnlock( volatile OSSpinLock *__lock ); + +__END_DECLS + +#else /* OSSPINLOCK_USE_INLINED */ + +/* + * Inline implementations of the legacy OSSpinLock interfaces in terms of the + * of the primitives. Direct use of those primitives is preferred. + * + * NOTE: the locked value of os_unfair_lock is implementation defined and + * subject to change, code that relies on the specific locked value used by the + * legacy OSSpinLock interface WILL break when using these inline + * implementations in terms of os_unfair_lock. + */ + +#if !OSSPINLOCK_USE_INLINED_TRANSPARENT + +#include + +__BEGIN_DECLS + +#if __has_attribute(always_inline) +#define OSSPINLOCK_INLINE static __inline +#else +#define OSSPINLOCK_INLINE static __inline __attribute__((__always_inline__)) +#endif + +#define OS_SPINLOCK_INIT 0 +typedef int32_t OSSpinLock; + +#if __has_extension(c_static_assert) +_Static_assert(sizeof(OSSpinLock) == sizeof(os_unfair_lock), + "Incompatible os_unfair_lock type"); +#endif + +OSSPINLOCK_INLINE +void +OSSpinLockLock(volatile OSSpinLock *__lock) +{ + os_unfair_lock_t lock = (os_unfair_lock_t)__lock; + return os_unfair_lock_lock(lock); +} + +OSSPINLOCK_INLINE +bool +OSSpinLockTry(volatile OSSpinLock *__lock) +{ + os_unfair_lock_t lock = (os_unfair_lock_t)__lock; + return os_unfair_lock_trylock(lock); +} + +OSSPINLOCK_INLINE +void +OSSpinLockUnlock(volatile OSSpinLock *__lock) +{ + os_unfair_lock_t lock = (os_unfair_lock_t)__lock; + return os_unfair_lock_unlock(lock); +} + +#undef OSSPINLOCK_INLINE + +__END_DECLS + +#else /* OSSPINLOCK_USE_INLINED_TRANSPARENT */ + +#include +#include +#include +#include +#include + +#define OS_NOSPIN_LOCK_AVAILABILITY \ + __OSX_AVAILABLE(10.12) __IOS_AVAILABLE(10.0) \ + __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0) + +__BEGIN_DECLS + +#define OS_SPINLOCK_INIT 0 +typedef int32_t OSSpinLock OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock); +typedef volatile OSSpinLock *_os_nospin_lock_t + OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_t); + +OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_lock) +OS_NOSPIN_LOCK_AVAILABILITY +void _os_nospin_lock_lock(_os_nospin_lock_t lock); +#undef OSSpinLockLock +#define OSSpinLockLock(lock) _os_nospin_lock_lock(lock) + +OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_trylock) +OS_NOSPIN_LOCK_AVAILABILITY +bool _os_nospin_lock_trylock(_os_nospin_lock_t lock); +#undef OSSpinLockTry +#define OSSpinLockTry(lock) _os_nospin_lock_trylock(lock) + +OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock_unlock) +OS_NOSPIN_LOCK_AVAILABILITY +void _os_nospin_lock_unlock(_os_nospin_lock_t lock); +#undef OSSpinLockUnlock +#define OSSpinLockUnlock(lock) _os_nospin_lock_unlock(lock) + +__END_DECLS + +#endif /* OSSPINLOCK_USE_INLINED_TRANSPARENT */ + +#endif /* OSSPINLOCK_USE_INLINED */ + +#endif /* _OSSPINLOCK_DEPRECATED_H_ */ \ No newline at end of file diff --git a/lib/std/SemanticVersion.zig b/lib/std/SemanticVersion.zig index 74e515f9d158..dc9e6d85728a 100644 --- a/lib/std/SemanticVersion.zig +++ b/lib/std/SemanticVersion.zig @@ -163,9 +163,9 @@ pub fn format( out_stream: anytype, ) !void { if (fmt.len != 0) @compileError("Unknown format string: '" ++ fmt ++ "'"); - try std.fmt.format(out_stream, "{}.{}.{}", .{ self.major, self.minor, self.patch }); - if (self.pre) |pre| try std.fmt.format(out_stream, "-{}", .{pre}); - if (self.build) |build| try std.fmt.format(out_stream, "+{}", .{build}); + try std.fmt.format(out_stream, "{d}.{d}.{d}", .{ self.major, self.minor, self.patch }); + if (self.pre) |pre| try std.fmt.format(out_stream, "-{s}", .{pre}); + if (self.build) |build| try std.fmt.format(out_stream, "+{s}", .{build}); } const expect = std.testing.expect; @@ -287,9 +287,9 @@ fn testFmt(expected: []const u8, comptime template: []const u8, args: anytype) ! if (std.mem.eql(u8, result, expected)) return; std.debug.warn("\n====== expected this output: =========\n", .{}); - std.debug.warn("{}", .{expected}); + std.debug.warn("{s}", .{expected}); std.debug.warn("\n======== instead found this: =========\n", .{}); - std.debug.warn("{}", .{result}); + std.debug.warn("{s}", .{result}); std.debug.warn("\n======================================\n", .{}); return error.TestFailed; } diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index ccdf487f48de..3114d1b74477 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -12,10 +12,20 @@ const Allocator = mem.Allocator; /// A contiguous, growable list of items in memory. /// This is a wrapper around an array of T values. Initialize with `init`. +/// +/// This struct internally stores a `std.mem.Allocator` for memory management. +/// To manually specify an allocator with each method call see `ArrayListUnmanaged`. pub fn ArrayList(comptime T: type) type { return ArrayListAligned(T, null); } +/// A contiguous, growable list of arbitrarily aligned items in memory. +/// This is a wrapper around an array of T values aligned to `alignment`-byte +/// addresses. If the specified alignment is `null`, then `@alignOf(T)` is used. +/// Initialize with `init`. +/// +/// This struct internally stores a `std.mem.Allocator` for memory management. +/// To manually specify an allocator with each method call see `ArrayListAlignedUnmanaged`. pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { if (alignment) |a| { if (a == @alignOf(T)) { @@ -24,9 +34,18 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } return struct { const Self = @This(); - - /// Content of the ArrayList + /// Contents of the list. Pointers to elements in this slice are + /// **invalid after resizing operations** on the ArrayList, unless the + /// operation explicitly either: (1) states otherwise or (2) lists the + /// invalidated pointers. + /// + /// The allocator used determines how element pointers are + /// invalidated, so the behavior may vary between lists. To avoid + /// illegal behavior, take into account the above paragraph plus the + /// explicit statements given in each method. items: Slice, + /// How many T values this list can hold without allocating + /// additional memory. capacity: usize, allocator: *Allocator, @@ -42,7 +61,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { }; } - /// Initialize with capacity to hold at least num elements. + /// Initialize with capacity to hold at least `num` elements. /// Deinitialize with `deinit` or use `toOwnedSlice`. pub fn initCapacity(allocator: *Allocator, num: usize) !Self { var self = Self.init(allocator); @@ -79,11 +98,13 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { }; } + /// Initializes an ArrayListUnmanaged with the `items` and `capacity` fields + /// of this ArrayList. This ArrayList retains ownership of underlying memory. pub fn toUnmanaged(self: Self) ArrayListAlignedUnmanaged(T, alignment) { return .{ .items = self.items, .capacity = self.capacity }; } - /// The caller owns the returned memory. ArrayList becomes empty. + /// The caller owns the returned memory. Empties this ArrayList. pub fn toOwnedSlice(self: *Self) Slice { const allocator = self.allocator; const result = allocator.shrink(self.allocatedSlice(), self.items.len); @@ -91,7 +112,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { return result; } - /// The caller owns the returned memory. ArrayList becomes empty. + /// The caller owns the returned memory. Empties this ArrayList. pub fn toOwnedSliceSentinel(self: *Self, comptime sentinel: T) ![:sentinel]T { try self.append(sentinel); const result = self.toOwnedSlice(); @@ -118,9 +139,10 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { mem.copy(T, self.items[i .. i + items.len], items); } - /// Replace range of elements `list[start..start+len]` with `new_items` - /// grows list if `len < new_items.len`. may allocate - /// shrinks list if `len > new_items.len` + /// Replace range of elements `list[start..start+len]` with `new_items`. + /// Grows list if `len < new_items.len`. + /// Shrinks list if `len > new_items.len`. + /// Invalidates pointers if this ArrayList is resized. pub fn replaceRange(self: *Self, start: usize, len: usize, new_items: SliceConst) !void { const after_range = start + len; const range = self.items[start..after_range]; @@ -151,15 +173,18 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { new_item_ptr.* = item; } - /// Extend the list by 1 element, but asserting `self.capacity` - /// is sufficient to hold an additional item. + /// Extend the list by 1 element, but assert `self.capacity` + /// is sufficient to hold an additional item. **Does not** + /// invalidate pointers. pub fn appendAssumeCapacity(self: *Self, item: T) void { const new_item_ptr = self.addOneAssumeCapacity(); new_item_ptr.* = item; } - /// Remove the element at index `i` from the list and return its value. + /// Remove the element at index `i`, shift elements after index + /// `i` forward, and return the removed element. /// Asserts the array has at least one item. + /// Invalidates pointers to end of list. /// This operation is O(N). pub fn orderedRemove(self: *Self, i: usize) T { const newlen = self.items.len - 1; @@ -191,7 +216,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } /// Append the slice of items to the list, asserting the capacity is already - /// enough to store the new items. + /// enough to store the new items. **Does not** invalidate pointers. pub fn appendSliceAssumeCapacity(self: *Self, items: SliceConst) void { const oldlen = self.items.len; const newlen = self.items.len + items.len; @@ -227,7 +252,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } /// Append a value to the list `n` times. - /// Asserts the capacity is enough. + /// Asserts the capacity is enough. **Does not** invalidate pointers. pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void { const new_len = self.items.len + n; assert(new_len <= self.capacity); @@ -243,7 +268,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } /// Reduce allocated capacity to `new_len`. - /// Invalidates element pointers. + /// May invalidate element pointers. pub fn shrink(self: *Self, new_len: usize) void { assert(new_len <= self.items.len); @@ -257,13 +282,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } /// Reduce length to `new_len`. - /// Invalidates element pointers. - /// Keeps capacity the same. + /// Invalidates pointers for the elements `items[new_len..]`. pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void { assert(new_len <= self.items.len); self.items.len = new_len; } + /// Modify the array so that it can hold at least `new_capacity` items. + /// Invalidates pointers if additional memory is needed. pub fn ensureCapacity(self: *Self, new_capacity: usize) !void { var better_capacity = self.capacity; if (better_capacity >= new_capacity) return; @@ -280,14 +306,13 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } /// Increases the array's length to match the full capacity that is already allocated. - /// The new elements have `undefined` values. This operation does not invalidate any - /// element pointers. + /// The new elements have `undefined` values. **Does not** invalidate pointers. pub fn expandToCapacity(self: *Self) void { self.items.len = self.capacity; } /// Increase length by 1, returning pointer to the new item. - /// The returned pointer becomes invalid when the list is resized. + /// The returned pointer becomes invalid when the list resized. pub fn addOne(self: *Self) !*T { const newlen = self.items.len + 1; try self.ensureCapacity(newlen); @@ -297,6 +322,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { /// Increase length by 1, returning pointer to the new item. /// Asserts that there is already space for the new item without allocating more. /// The returned pointer becomes invalid when the list is resized. + /// **Does not** invalidate element pointers. pub fn addOneAssumeCapacity(self: *Self) *T { assert(self.items.len < self.capacity); @@ -306,6 +332,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { /// Resize the array, adding `n` new elements, which have `undefined` values. /// The return value is an array pointing to the newly allocated elements. + /// The returned pointer becomes invalid when the list is resized. + /// Resizes list if `self.capacity` is not large enough. pub fn addManyAsArray(self: *Self, comptime n: usize) !*[n]T { const prev_len = self.items.len; try self.resize(self.items.len + n); @@ -315,6 +343,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { /// Resize the array, adding `n` new elements, which have `undefined` values. /// The return value is an array pointing to the newly allocated elements. /// Asserts that there is already space for the new item without allocating more. + /// **Does not** invalidate element pointers. + /// The returned pointer becomes invalid when the list is resized. pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T { assert(self.items.len + n <= self.capacity); const prev_len = self.items.len; @@ -324,21 +354,23 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { /// Remove and return the last element from the list. /// Asserts the list has at least one item. + /// Invalidates pointers to the removed element. pub fn pop(self: *Self) T { const val = self.items[self.items.len - 1]; self.items.len -= 1; return val; } - /// Remove and return the last element from the list. - /// If the list is empty, returns `null`. + /// Remove and return the last element from the list, or + /// return `null` if list is empty. + /// Invalidates pointers to the removed element, if any. pub fn popOrNull(self: *Self) ?T { if (self.items.len == 0) return null; return self.pop(); } /// Returns a slice of all the items plus the extra capacity, whose memory - /// contents are undefined. + /// contents are `undefined`. pub fn allocatedSlice(self: Self) Slice { // For a nicer API, `items.len` is the length, not the capacity. // This requires "unsafe" slicing. @@ -346,7 +378,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { } /// Returns a slice of only the extra capacity after items. - /// This can be useful for writing directly into an `ArrayList`. + /// This can be useful for writing directly into an ArrayList. /// Note that such an operation must be followed up with a direct /// modification of `self.items.len`. pub fn unusedCapacitySlice(self: Self) Slice { @@ -355,12 +387,18 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { }; } -/// Bring-your-own allocator with every function call. -/// Initialize directly and deinitialize with `deinit` or use `toOwnedSlice`. +/// An ArrayList, but the allocator is passed as a parameter to the relevant functions +/// rather than stored in the struct itself. The same allocator **must** be used throughout +/// the entire lifetime of an ArrayListUnmanaged. Initialize directly or with +/// `initCapacity`, and deinitialize with `deinit` or use `toOwnedSlice`. pub fn ArrayListUnmanaged(comptime T: type) type { return ArrayListAlignedUnmanaged(T, null); } +/// An ArrayListAligned, but the allocator is passed as a parameter to the relevant +/// functions rather than stored in the struct itself. The same allocator **must** +/// be used throughout the entire lifetime of an ArrayListAlignedUnmanaged. +/// Initialize directly or with `initCapacity`, and deinitialize with `deinit` or use `toOwnedSlice`. pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) type { if (alignment) |a| { if (a == @alignOf(T)) { @@ -369,9 +407,18 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } return struct { const Self = @This(); - - /// Content of the ArrayList. + /// Contents of the list. Pointers to elements in this slice are + /// **invalid after resizing operations** on the ArrayList, unless the + /// operation explicitly either: (1) states otherwise or (2) lists the + /// invalidated pointers. + /// + /// The allocator used determines how element pointers are + /// invalidated, so the behavior may vary between lists. To avoid + /// illegal behavior, take into account the above paragraph plus the + /// explicit statements given in each method. items: Slice = &[_]T{}, + /// How many T values this list can hold without allocating + /// additional memory. capacity: usize = 0, pub const Slice = if (alignment) |a| ([]align(a) T) else []T; @@ -395,6 +442,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ self.* = undefined; } + /// Convert this list into an analogous memory-managed one. + /// The returned list has ownership of the underlying memory. pub fn toManaged(self: *Self, allocator: *Allocator) ArrayListAligned(T, alignment) { return .{ .items = self.items, .capacity = self.capacity, .allocator = allocator }; } @@ -414,7 +463,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } /// Insert `item` at index `n`. Moves `list[n .. list.len]` - /// to make room. + /// to higher indices to make room. + /// This operation is O(N). pub fn insert(self: *Self, allocator: *Allocator, n: usize, item: T) !void { try self.ensureCapacity(allocator, self.items.len + 1); self.items.len += 1; @@ -423,8 +473,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ self.items[n] = item; } - /// Insert slice `items` at index `i`. Moves - /// `list[i .. list.len]` to make room. + /// Insert slice `items` at index `i`. Moves `list[i .. list.len]` to + /// higher indicices make room. /// This operation is O(N). pub fn insertSlice(self: *Self, allocator: *Allocator, i: usize, items: SliceConst) !void { try self.ensureCapacity(allocator, self.items.len + items.len); @@ -435,8 +485,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } /// Replace range of elements `list[start..start+len]` with `new_items` - /// grows list if `len < new_items.len`. may allocate - /// shrinks list if `len > new_items.len` + /// Grows list if `len < new_items.len`. + /// Shrinks list if `len > new_items.len` + /// Invalidates pointers if this ArrayList is resized. pub fn replaceRange(self: *Self, allocator: *Allocator, start: usize, len: usize, new_items: SliceConst) !void { var managed = self.toManaged(allocator); try managed.replaceRange(start, len, new_items); @@ -457,7 +508,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } /// Remove the element at index `i` from the list and return its value. - /// Asserts the array has at least one item. + /// Asserts the array has at least one item. Invalidates pointers to + /// last element. /// This operation is O(N). pub fn orderedRemove(self: *Self, i: usize) T { const newlen = self.items.len - 1; @@ -472,6 +524,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Removes the element at the specified index and returns it. /// The empty slot is filled from the end of the list. + /// Invalidates pointers to last element. /// This operation is O(1). pub fn swapRemove(self: *Self, i: usize) T { if (self.items.len - 1 == i) return self.pop(); @@ -515,6 +568,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } /// Append a value to the list `n` times. + /// **Does not** invalidate pointers. /// Asserts the capacity is enough. pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void { const new_len = self.items.len + n; @@ -524,14 +578,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } /// Adjust the list's length to `new_len`. - /// Does not initialize added items if any. + /// Does not initialize added items, if any. pub fn resize(self: *Self, allocator: *Allocator, new_len: usize) !void { try self.ensureCapacity(allocator, new_len); self.items.len = new_len; } /// Reduce allocated capacity to `new_len`. - /// Invalidates element pointers. pub fn shrink(self: *Self, allocator: *Allocator, new_len: usize) void { assert(new_len <= self.items.len); @@ -545,13 +598,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ } /// Reduce length to `new_len`. - /// Invalidates element pointers. + /// Invalidates pointers to elements `items[new_len..]`. /// Keeps capacity the same. pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void { assert(new_len <= self.items.len); self.items.len = new_len; } + /// Modify the array so that it can hold at least `new_capacity` items. + /// Invalidates pointers if additional memory is needed. pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_capacity: usize) !void { var better_capacity = self.capacity; if (better_capacity >= new_capacity) return; @@ -568,13 +623,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Increases the array's length to match the full capacity that is already allocated. /// The new elements have `undefined` values. - /// This operation does not invalidate any element pointers. + /// **Does not** invalidate pointers. pub fn expandToCapacity(self: *Self) void { self.items.len = self.capacity; } /// Increase length by 1, returning pointer to the new item. - /// The returned pointer becomes invalid when the list is resized. + /// The returned pointer becomes invalid when the list resized. pub fn addOne(self: *Self, allocator: *Allocator) !*T { const newlen = self.items.len + 1; try self.ensureCapacity(allocator, newlen); @@ -583,8 +638,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Increase length by 1, returning pointer to the new item. /// Asserts that there is already space for the new item without allocating more. - /// The returned pointer becomes invalid when the list is resized. - /// This operation does not invalidate any element pointers. + /// **Does not** invalidate pointers. + /// The returned pointer becomes invalid when the list resized. pub fn addOneAssumeCapacity(self: *Self) *T { assert(self.items.len < self.capacity); @@ -594,6 +649,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Resize the array, adding `n` new elements, which have `undefined` values. /// The return value is an array pointing to the newly allocated elements. + /// The returned pointer becomes invalid when the list is resized. pub fn addManyAsArray(self: *Self, allocator: *Allocator, comptime n: usize) !*[n]T { const prev_len = self.items.len; try self.resize(allocator, self.items.len + n); @@ -603,6 +659,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Resize the array, adding `n` new elements, which have `undefined` values. /// The return value is an array pointing to the newly allocated elements. /// Asserts that there is already space for the new item without allocating more. + /// **Does not** invalidate pointers. + /// The returned pointer becomes invalid when the list is resized. pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T { assert(self.items.len + n <= self.capacity); const prev_len = self.items.len; @@ -612,7 +670,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Remove and return the last element from the list. /// Asserts the list has at least one item. - /// This operation does not invalidate any element pointers. + /// Invalidates pointers to last element. pub fn pop(self: *Self) T { const val = self.items[self.items.len - 1]; self.items.len -= 1; @@ -621,7 +679,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ /// Remove and return the last element from the list. /// If the list is empty, returns `null`. - /// This operation does not invalidate any element pointers. + /// Invalidates pointers to last element. pub fn popOrNull(self: *Self) ?T { if (self.items.len == 0) return null; return self.pop(); diff --git a/lib/std/build.zig b/lib/std/build.zig index 3b6f8815bc86..bd097c941492 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -294,7 +294,7 @@ pub const Builder = struct { /// To run an executable built with zig build, see `LibExeObjStep.run`. pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep { assert(argv.len >= 1); - const run_step = RunStep.create(self, self.fmt("run {}", .{argv[0]})); + const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]})); run_step.addArgs(argv); return run_step; } @@ -409,7 +409,7 @@ pub const Builder = struct { for (self.installed_files.items) |installed_file| { const full_path = self.getInstallPath(installed_file.dir, installed_file.path); if (self.verbose) { - warn("rm {}\n", .{full_path}); + warn("rm {s}\n", .{full_path}); } fs.cwd().deleteTree(full_path) catch {}; } @@ -419,7 +419,7 @@ pub const Builder = struct { fn makeOneStep(self: *Builder, s: *Step) anyerror!void { if (s.loop_flag) { - warn("Dependency loop detected:\n {}\n", .{s.name}); + warn("Dependency loop detected:\n {s}\n", .{s.name}); return error.DependencyLoopDetected; } s.loop_flag = true; @@ -427,7 +427,7 @@ pub const Builder = struct { for (s.dependencies.items) |dep| { self.makeOneStep(dep) catch |err| { if (err == error.DependencyLoopDetected) { - warn(" {}\n", .{s.name}); + warn(" {s}\n", .{s.name}); } return err; }; @@ -444,7 +444,7 @@ pub const Builder = struct { return &top_level_step.step; } } - warn("Cannot run step '{}' because it does not exist\n", .{name}); + warn("Cannot run step '{s}' because it does not exist\n", .{name}); return error.InvalidStepName; } @@ -456,7 +456,7 @@ pub const Builder = struct { .description = description, }; if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) { - panic("Option '{}' declared twice", .{name}); + panic("Option '{s}' declared twice", .{name}); } self.available_options_list.append(available_option) catch unreachable; @@ -471,32 +471,32 @@ pub const Builder = struct { } else if (mem.eql(u8, s, "false")) { return false; } else { - warn("Expected -D{} to be a boolean, but received '{}'\n\n", .{ name, s }); + warn("Expected -D{s} to be a boolean, but received '{s}'\n\n", .{ name, s }); self.markInvalidUserInput(); return null; } }, .List => { - warn("Expected -D{} to be a boolean, but received a list.\n\n", .{name}); + warn("Expected -D{s} to be a boolean, but received a list.\n\n", .{name}); self.markInvalidUserInput(); return null; }, }, .Int => switch (entry.value.value) { .Flag => { - warn("Expected -D{} to be an integer, but received a boolean.\n\n", .{name}); + warn("Expected -D{s} to be an integer, but received a boolean.\n\n", .{name}); self.markInvalidUserInput(); return null; }, .Scalar => |s| { const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) { error.Overflow => { - warn("-D{} value {} cannot fit into type {}.\n\n", .{ name, s, @typeName(T) }); + warn("-D{s} value {} cannot fit into type {s}.\n\n", .{ name, s, @typeName(T) }); self.markInvalidUserInput(); return null; }, else => { - warn("Expected -D{} to be an integer of type {}.\n\n", .{ name, @typeName(T) }); + warn("Expected -D{s} to be an integer of type {s}.\n\n", .{ name, @typeName(T) }); self.markInvalidUserInput(); return null; }, @@ -504,34 +504,34 @@ pub const Builder = struct { return n; }, .List => { - warn("Expected -D{} to be an integer, but received a list.\n\n", .{name}); + warn("Expected -D{s} to be an integer, but received a list.\n\n", .{name}); self.markInvalidUserInput(); return null; }, }, .Float => switch (entry.value.value) { .Flag => { - warn("Expected -D{} to be a float, but received a boolean.\n\n", .{name}); + warn("Expected -D{s} to be a float, but received a boolean.\n\n", .{name}); self.markInvalidUserInput(); return null; }, .Scalar => |s| { const n = std.fmt.parseFloat(T, s) catch |err| { - warn("Expected -D{} to be a float of type {}.\n\n", .{ name, @typeName(T) }); + warn("Expected -D{s} to be a float of type {s}.\n\n", .{ name, @typeName(T) }); self.markInvalidUserInput(); return null; }; return n; }, .List => { - warn("Expected -D{} to be a float, but received a list.\n\n", .{name}); + warn("Expected -D{s} to be a float, but received a list.\n\n", .{name}); self.markInvalidUserInput(); return null; }, }, .Enum => switch (entry.value.value) { .Flag => { - warn("Expected -D{} to be a string, but received a boolean.\n\n", .{name}); + warn("Expected -D{s} to be a string, but received a boolean.\n\n", .{name}); self.markInvalidUserInput(); return null; }, @@ -539,25 +539,25 @@ pub const Builder = struct { if (std.meta.stringToEnum(T, s)) |enum_lit| { return enum_lit; } else { - warn("Expected -D{} to be of type {}.\n\n", .{ name, @typeName(T) }); + warn("Expected -D{s} to be of type {s}.\n\n", .{ name, @typeName(T) }); self.markInvalidUserInput(); return null; } }, .List => { - warn("Expected -D{} to be a string, but received a list.\n\n", .{name}); + warn("Expected -D{s} to be a string, but received a list.\n\n", .{name}); self.markInvalidUserInput(); return null; }, }, .String => switch (entry.value.value) { .Flag => { - warn("Expected -D{} to be a string, but received a boolean.\n\n", .{name}); + warn("Expected -D{s} to be a string, but received a boolean.\n\n", .{name}); self.markInvalidUserInput(); return null; }, .List => { - warn("Expected -D{} to be a string, but received a list.\n\n", .{name}); + warn("Expected -D{s} to be a string, but received a list.\n\n", .{name}); self.markInvalidUserInput(); return null; }, @@ -565,7 +565,7 @@ pub const Builder = struct { }, .List => switch (entry.value.value) { .Flag => { - warn("Expected -D{} to be a list, but received a boolean.\n\n", .{name}); + warn("Expected -D{s} to be a list, but received a boolean.\n\n", .{name}); self.markInvalidUserInput(); return null; }, @@ -592,7 +592,7 @@ pub const Builder = struct { if (self.release_mode != null) { @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice"); } - const description = self.fmt("Create a release build ({})", .{@tagName(mode)}); + const description = self.fmt("Create a release build ({s})", .{@tagName(mode)}); self.is_release = self.option(bool, "release", description) orelse false; self.release_mode = if (self.is_release) mode else builtin.Mode.Debug; } @@ -646,12 +646,12 @@ pub const Builder = struct { .diagnostics = &diags, }) catch |err| switch (err) { error.UnknownCpuModel => { - warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{ + warn("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':\n", .{ diags.cpu_name.?, @tagName(diags.arch.?), }); for (diags.arch.?.allCpuModels()) |cpu| { - warn(" {}\n", .{cpu.name}); + warn(" {s}\n", .{cpu.name}); } warn("\n", .{}); self.markInvalidUserInput(); @@ -659,15 +659,15 @@ pub const Builder = struct { }, error.UnknownCpuFeature => { warn( - \\Unknown CPU feature: '{}' - \\Available CPU features for architecture '{}': + \\Unknown CPU feature: '{s}' + \\Available CPU features for architecture '{s}': \\ , .{ diags.unknown_feature_name, @tagName(diags.arch.?), }); for (diags.arch.?.allFeaturesList()) |feature| { - warn(" {}: {}\n", .{ feature.name, feature.description }); + warn(" {s}: {s}\n", .{ feature.name, feature.description }); } warn("\n", .{}); self.markInvalidUserInput(); @@ -675,19 +675,19 @@ pub const Builder = struct { }, error.UnknownOperatingSystem => { warn( - \\Unknown OS: '{}' + \\Unknown OS: '{s}' \\Available operating systems: \\ , .{diags.os_name}); inline for (std.meta.fields(std.Target.Os.Tag)) |field| { - warn(" {}\n", .{field.name}); + warn(" {s}\n", .{field.name}); } warn("\n", .{}); self.markInvalidUserInput(); return args.default_target; }, else => |e| { - warn("Unable to parse target '{}': {}\n\n", .{ triple, @errorName(e) }); + warn("Unable to parse target '{}': {s}\n\n", .{ triple, @errorName(e) }); self.markInvalidUserInput(); return args.default_target; }, @@ -703,12 +703,12 @@ pub const Builder = struct { break :whitelist_check; } } - warn("Chosen target '{}' does not match one of the supported targets:\n", .{ + warn("Chosen target '{s}' does not match one of the supported targets:\n", .{ selected_canonicalized_triple, }); for (list) |t| { const t_triple = t.zigTriple(self.allocator) catch unreachable; - warn(" {}\n", .{t_triple}); + warn(" {s}\n", .{t_triple}); } warn("\n", .{}); self.markInvalidUserInput(); @@ -752,7 +752,7 @@ pub const Builder = struct { }) catch unreachable; }, UserValue.Flag => { - warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", .{ name, value, name }); + warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.\n", .{ name, value, name }); return true; }, } @@ -773,11 +773,11 @@ pub const Builder = struct { // option already exists switch (gop.entry.value.value) { UserValue.Scalar => |s| { - warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", .{ name, name, s }); + warn("Flag '-D{s}' conflicts with option '-D{s}={s}'.\n", .{ name, name, s }); return true; }, UserValue.List => { - warn("Flag '-D{}' conflicts with multiple options of the same name.\n", .{name}); + warn("Flag '-D{s}' conflicts with multiple options of the same name.\n", .{name}); return true; }, UserValue.Flag => {}, @@ -820,7 +820,7 @@ pub const Builder = struct { while (true) { const entry = it.next() orelse break; if (!entry.value.used) { - warn("Invalid option: -D{}\n\n", .{entry.key}); + warn("Invalid option: -D{s}\n\n", .{entry.key}); self.markInvalidUserInput(); } } @@ -833,9 +833,9 @@ pub const Builder = struct { } fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { - if (cwd) |yes_cwd| warn("cd {} && ", .{yes_cwd}); + if (cwd) |yes_cwd| warn("cd {s} && ", .{yes_cwd}); for (argv) |arg| { - warn("{} ", .{arg}); + warn("{s} ", .{arg}); } warn("\n", .{}); } @@ -852,7 +852,7 @@ pub const Builder = struct { child.env_map = env_map; const term = child.spawnAndWait() catch |err| { - warn("Unable to spawn {}: {}\n", .{ argv[0], @errorName(err) }); + warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); return err; }; @@ -875,7 +875,7 @@ pub const Builder = struct { pub fn makePath(self: *Builder, path: []const u8) !void { fs.cwd().makePath(self.pathFromRoot(path)) catch |err| { - warn("Unable to create path {}: {}\n", .{ path, @errorName(err) }); + warn("Unable to create path {s}: {s}\n", .{ path, @errorName(err) }); return err; }; } @@ -959,7 +959,7 @@ pub const Builder = struct { pub fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void { if (self.verbose) { - warn("cp {} {} ", .{ source_path, dest_path }); + warn("cp {s} {s} ", .{ source_path, dest_path }); } const cwd = fs.cwd(); const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{}); @@ -988,7 +988,7 @@ pub const Builder = struct { const full_path = try fs.path.join(self.allocator, &[_][]const u8{ search_prefix, "bin", - self.fmt("{}{}", .{ name, exe_extension }), + self.fmt("{s}{s}", .{ name, exe_extension }), }); return fs.realpathAlloc(self.allocator, full_path) catch continue; } @@ -1002,7 +1002,7 @@ pub const Builder = struct { while (it.next()) |path| { const full_path = try fs.path.join(self.allocator, &[_][]const u8{ path, - self.fmt("{}{}", .{ name, exe_extension }), + self.fmt("{s}{s}", .{ name, exe_extension }), }); return fs.realpathAlloc(self.allocator, full_path) catch continue; } @@ -1015,7 +1015,7 @@ pub const Builder = struct { for (paths) |path| { const full_path = try fs.path.join(self.allocator, &[_][]const u8{ path, - self.fmt("{}{}", .{ name, exe_extension }), + self.fmt("{s}{s}", .{ name, exe_extension }), }); return fs.realpathAlloc(self.allocator, full_path) catch continue; } @@ -1070,19 +1070,19 @@ pub const Builder = struct { var code: u8 = undefined; return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) { error.FileNotFound => { - if (src_step) |s| warn("{}...", .{s.name}); + if (src_step) |s| warn("{s}...", .{s.name}); warn("Unable to spawn the following command: file not found\n", .{}); printCmd(null, argv); std.os.exit(@truncate(u8, code)); }, error.ExitCodeFailure => { - if (src_step) |s| warn("{}...", .{s.name}); - warn("The following command exited with error code {}:\n", .{code}); + if (src_step) |s| warn("{s}...", .{s.name}); + warn("The following command exited with error code {d}:\n", .{code}); printCmd(null, argv); std.os.exit(@truncate(u8, code)); }, error.ProcessTerminated => { - if (src_step) |s| warn("{}...", .{s.name}); + if (src_step) |s| warn("{s}...", .{s.name}); warn("The following command terminated unexpectedly:\n", .{}); printCmd(null, argv); std.os.exit(@truncate(u8, code)); @@ -1405,7 +1405,7 @@ pub const LibExeObjStep = struct { ver: ?Version, ) LibExeObjStep { if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { - panic("invalid name: '{}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); + panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); } var self = LibExeObjStep{ .strip = false, @@ -1421,9 +1421,9 @@ pub const LibExeObjStep = struct { .step = Step.init(.LibExeObj, name, builder.allocator, make), .version = ver, .out_filename = undefined, - .out_h_filename = builder.fmt("{}.h", .{name}), + .out_h_filename = builder.fmt("{s}.h", .{name}), .out_lib_filename = undefined, - .out_pdb_filename = builder.fmt("{}.pdb", .{name}), + .out_pdb_filename = builder.fmt("{s}.pdb", .{name}), .major_only_filename = undefined, .name_only_filename = undefined, .packages = ArrayList(Pkg).init(builder.allocator), @@ -1529,7 +1529,7 @@ pub const LibExeObjStep = struct { // It doesn't have to be native. We catch that if you actually try to run it. // Consider that this is declarative; the run step may not be run unless a user // option is supplied. - const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {}", .{exe.step.name})); + const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name})); run_step.addArtifactArg(exe); if (exe.vcpkg_bin_path) |path| { @@ -1680,7 +1680,7 @@ pub const LibExeObjStep = struct { } else if (mem.eql(u8, tok, "-pthread")) { self.linkLibC(); } else if (self.builder.verbose) { - warn("Ignoring pkg-config flag '{}'\n", .{tok}); + warn("Ignoring pkg-config flag '{s}'\n", .{tok}); } } } @@ -1926,7 +1926,7 @@ pub const LibExeObjStep = struct { }, else => {}, } - out.print("pub const {z}: {} = {};\n", .{ name, @typeName(T), value }) catch unreachable; + out.print("pub const {z}: {s} = {};\n", .{ name, @typeName(T), value }) catch unreachable; } /// The value is the path in the cache dir. @@ -2048,7 +2048,7 @@ pub const LibExeObjStep = struct { const builder = self.builder; if (self.root_src == null and self.link_objects.items.len == 0) { - warn("{}: linker needs 1 or more objects to link\n", .{self.step.name}); + warn("{s}: linker needs 1 or more objects to link\n", .{self.step.name}); return error.NeedAnObject; } @@ -2156,12 +2156,12 @@ pub const LibExeObjStep = struct { // Render build artifact options at the last minute, now that the path is known. for (self.build_options_artifact_args.items) |item| { const out = self.build_options_contents.writer(); - out.print("pub const {}: []const u8 = \"{Z}\";\n", .{ item.name, item.artifact.getOutputPath() }) catch unreachable; + out.print("pub const {s}: []const u8 = \"{Z}\";\n", .{ item.name, item.artifact.getOutputPath() }) catch unreachable; } const build_options_file = try fs.path.join( builder.allocator, - &[_][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", .{self.name}) }, + &[_][]const u8{ builder.cache_root, builder.fmt("{s}_build_options.zig", .{self.name}) }, ); const path_from_root = builder.pathFromRoot(build_options_file); try fs.cwd().writeFile(path_from_root, self.build_options_contents.items); @@ -2294,16 +2294,16 @@ pub const LibExeObjStep = struct { } else { var mcpu_buffer = std.ArrayList(u8).init(builder.allocator); - try mcpu_buffer.outStream().print("-mcpu={}", .{cross.cpu.model.name}); + try mcpu_buffer.outStream().print("-mcpu={s}", .{cross.cpu.model.name}); for (all_features) |feature, i_usize| { const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); const in_cpu_set = populated_cpu_features.isEnabled(i); const in_actual_set = cross.cpu.features.isEnabled(i); if (in_cpu_set and !in_actual_set) { - try mcpu_buffer.outStream().print("-{}", .{feature.name}); + try mcpu_buffer.outStream().print("-{s}", .{feature.name}); } else if (!in_cpu_set and in_actual_set) { - try mcpu_buffer.outStream().print("+{}", .{feature.name}); + try mcpu_buffer.outStream().print("+{s}", .{feature.name}); } } @@ -2536,7 +2536,7 @@ pub const InstallArtifactStep = struct { const self = builder.allocator.create(Self) catch unreachable; self.* = Self{ .builder = builder, - .step = Step.init(.InstallArtifact, builder.fmt("install {}", .{artifact.step.name}), builder.allocator, make), + .step = Step.init(.InstallArtifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make), .artifact = artifact, .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) { .Obj => unreachable, @@ -2612,7 +2612,7 @@ pub const InstallFileStep = struct { builder.pushInstalledFile(dir, dest_rel_path); return InstallFileStep{ .builder = builder, - .step = Step.init(.InstallFile, builder.fmt("install {}", .{src_path}), builder.allocator, make), + .step = Step.init(.InstallFile, builder.fmt("install {s}", .{src_path}), builder.allocator, make), .src_path = src_path, .dir = dir, .dest_rel_path = dest_rel_path, @@ -2646,7 +2646,7 @@ pub const InstallDirStep = struct { builder.pushInstalledFile(options.install_dir, options.install_subdir); return InstallDirStep{ .builder = builder, - .step = Step.init(.InstallDir, builder.fmt("install {}/", .{options.source_dir}), builder.allocator, make), + .step = Step.init(.InstallDir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make), .options = options, }; } @@ -2682,14 +2682,14 @@ pub const LogStep = struct { pub fn init(builder: *Builder, data: []const u8) LogStep { return LogStep{ .builder = builder, - .step = Step.init(.Log, builder.fmt("log {}", .{data}), builder.allocator, make), + .step = Step.init(.Log, builder.fmt("log {s}", .{data}), builder.allocator, make), .data = data, }; } fn make(step: *Step) anyerror!void { const self = @fieldParentPtr(LogStep, "step", step); - warn("{}", .{self.data}); + warn("{s}", .{self.data}); } }; @@ -2701,7 +2701,7 @@ pub const RemoveDirStep = struct { pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep { return RemoveDirStep{ .builder = builder, - .step = Step.init(.RemoveDir, builder.fmt("RemoveDir {}", .{dir_path}), builder.allocator, make), + .step = Step.init(.RemoveDir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make), .dir_path = dir_path, }; } @@ -2711,7 +2711,7 @@ pub const RemoveDirStep = struct { const full_path = self.builder.pathFromRoot(self.dir_path); fs.cwd().deleteTree(full_path) catch |err| { - warn("Unable to remove {}: {}\n", .{ full_path, @errorName(err) }); + warn("Unable to remove {s}: {s}\n", .{ full_path, @errorName(err) }); return err; }; } @@ -2799,7 +2799,7 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj &[_][]const u8{ out_dir, filename_major_only }, ) catch unreachable; fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { - warn("Unable to symlink {} -> {}\n", .{ major_only_path, out_basename }); + warn("Unable to symlink {s} -> {s}\n", .{ major_only_path, out_basename }); return err; }; // sym link for libfoo.so to libfoo.so.1 @@ -2808,7 +2808,7 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj &[_][]const u8{ out_dir, filename_name_only }, ) catch unreachable; fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { - warn("Unable to symlink {} -> {}\n", .{ name_only_path, filename_major_only }); + warn("Unable to symlink {s} -> {s}\n", .{ name_only_path, filename_major_only }); return err; }; } diff --git a/lib/std/build/check_file.zig b/lib/std/build/check_file.zig index 565ff0692a72..31966fad521a 100644 --- a/lib/std/build/check_file.zig +++ b/lib/std/build/check_file.zig @@ -45,9 +45,9 @@ pub const CheckFileStep = struct { warn( \\ \\========= Expected to find: =================== - \\{} + \\{s} \\========= But file does not contain it: ======= - \\{} + \\{s} \\ , .{ expected_match, contents }); return error.TestFailed; diff --git a/lib/std/build/emit_raw.zig b/lib/std/build/emit_raw.zig index 66d44fc59fae..3d2c6124c166 100644 --- a/lib/std/build/emit_raw.zig +++ b/lib/std/build/emit_raw.zig @@ -189,7 +189,7 @@ pub const InstallRawStep = struct { pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) *Self { const self = builder.allocator.create(Self) catch unreachable; self.* = Self{ - .step = Step.init(.InstallRaw, builder.fmt("install raw binary {}", .{artifact.step.name}), builder.allocator, make), + .step = Step.init(.InstallRaw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make), .builder = builder, .artifact = artifact, .dest_dir = switch (artifact.kind) { diff --git a/lib/std/build/run.zig b/lib/std/build/run.zig index 18e05c2e0149..36a4a1a843b9 100644 --- a/lib/std/build/run.zig +++ b/lib/std/build/run.zig @@ -116,7 +116,7 @@ pub const RunStep = struct { } if (prev_path) |pp| { - const new_path = self.builder.fmt("{}" ++ [1]u8{fs.path.delimiter} ++ "{}", .{ pp, search_path }); + const new_path = self.builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path }); env_map.set(key, new_path) catch unreachable; } else { env_map.set(key, search_path) catch unreachable; @@ -189,7 +189,7 @@ pub const RunStep = struct { child.stderr_behavior = stdIoActionToBehavior(self.stderr_action); child.spawn() catch |err| { - warn("Unable to spawn {}: {}\n", .{ argv[0], @errorName(err) }); + warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); return err; }; @@ -216,7 +216,7 @@ pub const RunStep = struct { } const term = child.wait() catch |err| { - warn("Unable to spawn {}: {}\n", .{ argv[0], @errorName(err) }); + warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); return err; }; @@ -245,9 +245,9 @@ pub const RunStep = struct { warn( \\ \\========= Expected this stderr: ========= - \\{} + \\{s} \\========= But found: ==================== - \\{} + \\{s} \\ , .{ expected_bytes, stderr.? }); printCmd(cwd, argv); @@ -259,9 +259,9 @@ pub const RunStep = struct { warn( \\ \\========= Expected to find in stderr: ========= - \\{} + \\{s} \\========= But stderr does not contain it: ===== - \\{} + \\{s} \\ , .{ match, stderr.? }); printCmd(cwd, argv); @@ -277,9 +277,9 @@ pub const RunStep = struct { warn( \\ \\========= Expected this stdout: ========= - \\{} + \\{s} \\========= But found: ==================== - \\{} + \\{s} \\ , .{ expected_bytes, stdout.? }); printCmd(cwd, argv); @@ -291,9 +291,9 @@ pub const RunStep = struct { warn( \\ \\========= Expected to find in stdout: ========= - \\{} + \\{s} \\========= But stdout does not contain it: ===== - \\{} + \\{s} \\ , .{ match, stdout.? }); printCmd(cwd, argv); @@ -304,9 +304,9 @@ pub const RunStep = struct { } fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { - if (cwd) |yes_cwd| warn("cd {} && ", .{yes_cwd}); + if (cwd) |yes_cwd| warn("cd {s} && ", .{yes_cwd}); for (argv) |arg| { - warn("{} ", .{arg}); + warn("{s} ", .{arg}); } warn("\n", .{}); } diff --git a/lib/std/build/write_file.zig b/lib/std/build/write_file.zig index 04b7268e81e1..bbe6ec508629 100644 --- a/lib/std/build/write_file.zig +++ b/lib/std/build/write_file.zig @@ -80,14 +80,14 @@ pub const WriteFileStep = struct { }); // TODO replace with something like fs.makePathAndOpenDir fs.cwd().makePath(self.output_dir) catch |err| { - warn("unable to make path {}: {}\n", .{ self.output_dir, @errorName(err) }); + warn("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) }); return err; }; var dir = try fs.cwd().openDir(self.output_dir, .{}); defer dir.close(); for (self.files.items) |file| { dir.writeFile(file.basename, file.bytes) catch |err| { - warn("unable to write {} into {}: {}\n", .{ + warn("unable to write {s} into {s}: {s}\n", .{ file.basename, self.output_dir, @errorName(err), diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index fbc4a7cef599..c883e03ba909 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -67,12 +67,12 @@ pub const StackTrace = struct { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const debug_info = std.debug.getSelfDebugInfo() catch |err| { - return writer.print("\nUnable to print stack trace: Unable to open debug info: {}\n", .{@errorName(err)}); + return writer.print("\nUnable to print stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}); }; const tty_config = std.debug.detectTTYConfig(); try writer.writeAll("\n"); std.debug.writeStackTrace(self, writer, &arena.allocator, debug_info, tty_config) catch |err| { - try writer.print("Unable to print stack trace: {}\n", .{@errorName(err)}); + try writer.print("Unable to print stack trace: {s}\n", .{@errorName(err)}); }; try writer.writeAll("\n"); } @@ -529,12 +529,12 @@ pub const Version = struct { if (fmt.len == 0) { if (self.patch == 0) { if (self.minor == 0) { - return std.fmt.format(out_stream, "{}", .{self.major}); + return std.fmt.format(out_stream, "{d}", .{self.major}); } else { - return std.fmt.format(out_stream, "{}.{}", .{ self.major, self.minor }); + return std.fmt.format(out_stream, "{d}.{d}", .{ self.major, self.minor }); } } else { - return std.fmt.format(out_stream, "{}.{}.{}", .{ self.major, self.minor, self.patch }); + return std.fmt.format(out_stream, "{d}.{d}.{d}", .{ self.major, self.minor, self.patch }); } } else { @compileError("Unknown format string: '" ++ fmt ++ "'"); @@ -683,7 +683,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn } }, .wasi => { - std.debug.warn("{}", .{msg}); + std.debug.warn("{s}", .{msg}); std.os.abort(); }, .uefi => { @@ -692,7 +692,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn }, else => { const first_trace_addr = @returnAddress(); - std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", .{msg}); + std.debug.panicExtra(error_return_trace, first_trace_addr, "{s}", .{msg}); }, } } diff --git a/lib/std/c.zig b/lib/std/c.zig index 7a1f47e6ac57..9b29d9a2c475 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -73,10 +73,10 @@ pub fn versionCheck(glibc_version: builtin.Version) type { pub extern "c" var environ: [*:null]?[*:0]u8; -pub extern "c" fn fopen(filename: [*:0]const u8, modes: [*:0]const u8) ?*FILE; +pub extern "c" fn fopen(noalias filename: [*:0]const u8, noalias modes: [*:0]const u8) ?*FILE; pub extern "c" fn fclose(stream: *FILE) c_int; -pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize; -pub extern "c" fn fread(ptr: [*]u8, size_of_type: usize, item_count: usize, stream: *FILE) usize; +pub extern "c" fn fwrite(noalias ptr: [*]const u8, size_of_type: usize, item_count: usize, noalias stream: *FILE) usize; +pub extern "c" fn fread(noalias ptr: [*]u8, size_of_type: usize, item_count: usize, noalias stream: *FILE) usize; pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; pub extern "c" fn abort() noreturn; @@ -153,9 +153,9 @@ pub extern "c" fn socketpair(domain: c_uint, sock_type: c_uint, protocol: c_uint pub extern "c" fn listen(sockfd: fd_t, backlog: c_uint) c_int; pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias addrlen: *socklen_t) c_int; pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int; -pub extern "c" fn accept(sockfd: fd_t, addr: ?*sockaddr, addrlen: ?*socklen_t) c_int; -pub extern "c" fn accept4(sockfd: fd_t, addr: ?*sockaddr, addrlen: ?*socklen_t, flags: c_uint) c_int; -pub extern "c" fn getsockopt(sockfd: fd_t, level: u32, optname: u32, optval: ?*c_void, optlen: *socklen_t) c_int; +pub extern "c" fn accept(sockfd: fd_t, noalias addr: ?*sockaddr, noalias addrlen: ?*socklen_t) c_int; +pub extern "c" fn accept4(sockfd: fd_t, noalias addr: ?*sockaddr, noalias addrlen: ?*socklen_t, flags: c_uint) c_int; +pub extern "c" fn getsockopt(sockfd: fd_t, level: u32, optname: u32, noalias optval: ?*c_void, noalias optlen: *socklen_t) c_int; pub extern "c" fn setsockopt(sockfd: fd_t, level: u32, optname: u32, optval: ?*const c_void, optlen: socklen_t) c_int; pub extern "c" fn send(sockfd: fd_t, buf: *const c_void, len: usize, flags: u32) isize; pub extern "c" fn sendto( diff --git a/lib/std/c/ast.zig b/lib/std/c/ast.zig index c1e20f799de1..207fe8eac8cd 100644 --- a/lib/std/c/ast.zig +++ b/lib/std/c/ast.zig @@ -115,10 +115,10 @@ pub const Error = union(enum) { pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void { const found_token = tree.tokens.at(self.token); if (found_token.id == .Invalid) { - return stream.print("expected '{}', found invalid bytes", .{self.expected_id.symbol()}); + return stream.print("expected '{s}', found invalid bytes", .{self.expected_id.symbol()}); } else { const token_name = found_token.id.symbol(); - return stream.print("expected '{}', found '{}'", .{ self.expected_id.symbol(), token_name }); + return stream.print("expected '{s}', found '{s}'", .{ self.expected_id.symbol(), token_name }); } } }; @@ -131,7 +131,7 @@ pub const Error = union(enum) { try stream.write("invalid type specifier '"); try type_spec.spec.print(tree, stream); const token_name = tree.tokens.at(self.token).id.symbol(); - return stream.print("{}'", .{token_name}); + return stream.print("{s}'", .{token_name}); } }; @@ -140,7 +140,7 @@ pub const Error = union(enum) { name: TokenIndex, pub fn render(self: *const ExpectedToken, tree: *Tree, stream: anytype) !void { - return stream.print("must use '{}' tag to refer to type '{}'", .{ tree.slice(kw), tree.slice(name) }); + return stream.print("must use '{s}' tag to refer to type '{s}'", .{ tree.slice(kw), tree.slice(name) }); } }; diff --git a/lib/std/c/tokenizer.zig b/lib/std/c/tokenizer.zig index 3fa3d1d6f82f..ea5774fe4c7f 100644 --- a/lib/std/c/tokenizer.zig +++ b/lib/std/c/tokenizer.zig @@ -446,7 +446,7 @@ pub const Tokenizer = struct { 'L' => { state = .L; }, - 'a'...'t', 'v'...'z', 'A'...'K', 'M'...'T', 'V'...'Z', '_' => { + 'a'...'t', 'v'...'z', 'A'...'K', 'M'...'T', 'V'...'Z', '_', '$' => { state = .Identifier; }, '=' => { @@ -776,7 +776,7 @@ pub const Tokenizer = struct { }, }, .Identifier => switch (c) { - 'a'...'z', 'A'...'Z', '_', '0'...'9' => {}, + 'a'...'z', 'A'...'Z', '_', '0'...'9', '$' => {}, else => { result.id = Token.getKeyword(self.buffer[result.start..self.index], self.prev_tok_id == .Hash and !self.pp_directive) orelse .Identifier; if (self.prev_tok_id == .Hash) @@ -1552,7 +1552,7 @@ fn expectTokens(source: []const u8, expected_tokens: []const Token.Id) void { for (expected_tokens) |expected_token_id| { const token = tokenizer.next(); if (!std.meta.eql(token.id, expected_token_id)) { - std.debug.panic("expected {}, found {}\n", .{ @tagName(expected_token_id), @tagName(token.id) }); + std.debug.panic("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.id) }); } } const last_token = tokenizer.next(); diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index f97db98b0bd3..da6ec2edf0d1 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -140,6 +140,9 @@ pub const random = &@import("crypto/tlcsprng.zig").interface; const std = @import("std.zig"); test "crypto" { + const please_windows_dont_oom = std.Target.current.os.tag == .windows; + if (please_windows_dont_oom) return error.SkipZigTest; + inline for (std.meta.declarations(@This())) |decl| { switch (decl.data) { .Type => |t| { diff --git a/lib/std/crypto/bcrypt.zig b/lib/std/crypto/bcrypt.zig index 554ba50c6824..6d333c45cb98 100644 --- a/lib/std/crypto/bcrypt.zig +++ b/lib/std/crypto/bcrypt.zig @@ -247,7 +247,7 @@ fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) Codec.encode(ct_str[0..], ct[0 .. ct.len - 1]); var s_buf: [hash_length]u8 = undefined; - const s = fmt.bufPrint(s_buf[0..], "$2b${}{}${}{}", .{ rounds_log / 10, rounds_log % 10, salt_str, ct_str }) catch unreachable; + const s = fmt.bufPrint(s_buf[0..], "$2b${d}{d}${s}{s}", .{ rounds_log / 10, rounds_log % 10, salt_str, ct_str }) catch unreachable; debug.assert(s.len == s_buf.len); return s_buf; } diff --git a/lib/std/crypto/blake2.zig b/lib/std/crypto/blake2.zig index bcb925c12d21..4203a994596f 100644 --- a/lib/std/crypto/blake2.zig +++ b/lib/std/crypto/blake2.zig @@ -33,6 +33,7 @@ fn roundParam(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) RoundP // Blake2s pub const Blake2s128 = Blake2s(128); +pub const Blake2s160 = Blake2s(160); pub const Blake2s224 = Blake2s(224); pub const Blake2s256 = Blake2s(256); @@ -137,12 +138,8 @@ pub fn Blake2s(comptime out_bits: usize) type { mem.set(u8, d.buf[d.buf_len..], 0); d.t += d.buf_len; d.round(d.buf[0..], true); - - const rr = d.h[0 .. digest_length / 4]; - - for (rr) |s, j| { - mem.writeIntSliceLittle(u32, out[4 * j ..], s); - } + for (d.h) |*x| x.* = mem.nativeToLittle(u32, x.*); + mem.copy(u8, out[0..], @ptrCast(*[digest_length]u8, &d.h)); } fn round(d: *Self, b: *const [64]u8, last: bool) void { @@ -195,6 +192,89 @@ pub fn Blake2s(comptime out_bits: usize) type { }; } +test "blake2s160 single" { + const h1 = "354c9c33f735962418bdacb9479873429c34916f"; + htest.assertEqualHash(Blake2s160, h1, ""); + + const h2 = "5ae3b99be29b01834c3b508521ede60438f8de17"; + htest.assertEqualHash(Blake2s160, h2, "abc"); + + const h3 = "5a604fec9713c369e84b0ed68daed7d7504ef240"; + htest.assertEqualHash(Blake2s160, h3, "The quick brown fox jumps over the lazy dog"); + + const h4 = "b60c4dc60e2681e58fbc24e77f07e02c69e72ed0"; + htest.assertEqualHash(Blake2s160, h4, "a" ** 32 ++ "b" ** 32); +} + +test "blake2s160 streaming" { + var h = Blake2s160.init(.{}); + var out: [20]u8 = undefined; + + const h1 = "354c9c33f735962418bdacb9479873429c34916f"; + + h.final(out[0..]); + htest.assertEqual(h1, out[0..]); + + const h2 = "5ae3b99be29b01834c3b508521ede60438f8de17"; + + h = Blake2s160.init(.{}); + h.update("abc"); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); + + h = Blake2s160.init(.{}); + h.update("a"); + h.update("b"); + h.update("c"); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); + + const h3 = "b60c4dc60e2681e58fbc24e77f07e02c69e72ed0"; + + h = Blake2s160.init(.{}); + h.update("a" ** 32); + h.update("b" ** 32); + h.final(out[0..]); + htest.assertEqual(h3, out[0..]); + + h = Blake2s160.init(.{}); + h.update("a" ** 32 ++ "b" ** 32); + h.final(out[0..]); + htest.assertEqual(h3, out[0..]); + + const h4 = "4667fd60791a7fe41f939bca646b4529e296bd68"; + + h = Blake2s160.init(.{ .context = [_]u8{0x69} ** 8, .salt = [_]u8{0x42} ** 8 }); + h.update("a" ** 32); + h.update("b" ** 32); + h.final(out[0..]); + htest.assertEqual(h4, out[0..]); + + h = Blake2s160.init(.{ .context = [_]u8{0x69} ** 8, .salt = [_]u8{0x42} ** 8 }); + h.update("a" ** 32 ++ "b" ** 32); + h.final(out[0..]); + htest.assertEqual(h4, out[0..]); +} + +test "comptime blake2s160" { + //comptime + { + @setEvalBranchQuota(10000); + var block = [_]u8{0} ** Blake2s160.block_length; + var out: [Blake2s160.digest_length]u8 = undefined; + + const h1 = "2c56ad9d0b2c8b474aafa93ab307db2f0940105f"; + + htest.assertEqualHash(Blake2s160, h1, block[0..]); + + var h = Blake2s160.init(.{}); + h.update(&block); + h.final(out[0..]); + + htest.assertEqual(h1, out[0..]); + } +} + test "blake2s224 single" { const h1 = "1fa1291e65248b37b3433475b2a0dd63d54a11ecc4e3e034e7bc1ef4"; htest.assertEqualHash(Blake2s224, h1, ""); @@ -373,6 +453,7 @@ test "comptime blake2s256" { // Blake2b pub const Blake2b128 = Blake2b(128); +pub const Blake2b160 = Blake2b(160); pub const Blake2b256 = Blake2b(256); pub const Blake2b384 = Blake2b(384); pub const Blake2b512 = Blake2b(512); @@ -480,12 +561,8 @@ pub fn Blake2b(comptime out_bits: usize) type { mem.set(u8, d.buf[d.buf_len..], 0); d.t += d.buf_len; d.round(d.buf[0..], true); - - const rr = d.h[0 .. digest_length / 8]; - - for (rr) |s, j| { - mem.writeIntSliceLittle(u64, out[8 * j ..], s); - } + for (d.h) |*x| x.* = mem.nativeToLittle(u64, x.*); + mem.copy(u8, out[0..], @ptrCast(*[digest_length]u8, &d.h)); } fn round(d: *Self, b: *const [128]u8, last: bool) void { @@ -538,6 +615,95 @@ pub fn Blake2b(comptime out_bits: usize) type { }; } +test "blake2b160 single" { + const h1 = "3345524abf6bbe1809449224b5972c41790b6cf2"; + htest.assertEqualHash(Blake2b160, h1, ""); + + const h2 = "384264f676f39536840523f284921cdc68b6846b"; + htest.assertEqualHash(Blake2b160, h2, "abc"); + + const h3 = "3c523ed102ab45a37d54f5610d5a983162fde84f"; + htest.assertEqualHash(Blake2b160, h3, "The quick brown fox jumps over the lazy dog"); + + const h4 = "43758f5de1740f651f1ae39de92260fe8bd5a11f"; + htest.assertEqualHash(Blake2b160, h4, "a" ** 64 ++ "b" ** 64); +} + +test "blake2b160 streaming" { + var h = Blake2b160.init(.{}); + var out: [20]u8 = undefined; + + const h1 = "3345524abf6bbe1809449224b5972c41790b6cf2"; + + h.final(out[0..]); + htest.assertEqual(h1, out[0..]); + + const h2 = "384264f676f39536840523f284921cdc68b6846b"; + + h = Blake2b160.init(.{}); + h.update("abc"); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); + + h = Blake2b160.init(.{}); + h.update("a"); + h.update("b"); + h.update("c"); + h.final(out[0..]); + htest.assertEqual(h2, out[0..]); + + const h3 = "43758f5de1740f651f1ae39de92260fe8bd5a11f"; + + h = Blake2b160.init(.{}); + h.update("a" ** 64 ++ "b" ** 64); + h.final(out[0..]); + htest.assertEqual(h3, out[0..]); + + h = Blake2b160.init(.{}); + h.update("a" ** 64); + h.update("b" ** 64); + h.final(out[0..]); + htest.assertEqual(h3, out[0..]); + + h = Blake2b160.init(.{}); + h.update("a" ** 64); + h.update("b" ** 64); + h.final(out[0..]); + htest.assertEqual(h3, out[0..]); + + const h4 = "72328f8a8200663752fc302d372b5dd9b49dd8dc"; + + h = Blake2b160.init(.{ .context = [_]u8{0x69} ** 16, .salt = [_]u8{0x42} ** 16 }); + h.update("a" ** 64); + h.update("b" ** 64); + h.final(out[0..]); + htest.assertEqual(h4, out[0..]); + + h = Blake2b160.init(.{ .context = [_]u8{0x69} ** 16, .salt = [_]u8{0x42} ** 16 }); + h.update("a" ** 64); + h.update("b" ** 64); + h.final(out[0..]); + htest.assertEqual(h4, out[0..]); +} + +test "comptime blake2b160" { + comptime { + @setEvalBranchQuota(10000); + var block = [_]u8{0} ** Blake2b160.block_length; + var out: [Blake2b160.digest_length]u8 = undefined; + + const h1 = "8d26f158f564e3293b42f5e3d34263cb173aa9c9"; + + htest.assertEqualHash(Blake2b160, h1, block[0..]); + + var h = Blake2b160.init(.{}); + h.update(&block); + h.final(out[0..]); + + htest.assertEqual(h1, out[0..]); + } +} + test "blake2b384 single" { const h1 = "b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100"; htest.assertEqualHash(Blake2b384, h1, ""); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 57a0e20d2e87..073f68da5d98 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -108,11 +108,11 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { return; } const debug_info = getSelfDebugInfo() catch |err| { - stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; + stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return; return; }; writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(), start_addr) catch |err| { - stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return; + stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return; return; }; } @@ -129,7 +129,7 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void { return; } const debug_info = getSelfDebugInfo() catch |err| { - stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; + stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return; return; }; const tty_config = detectTTYConfig(); @@ -199,11 +199,11 @@ pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { return; } const debug_info = getSelfDebugInfo() catch |err| { - stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", .{@errorName(err)}) catch return; + stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return; return; }; writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, detectTTYConfig()) catch |err| { - stderr.print("Unable to dump stack trace: {}\n", .{@errorName(err)}) catch return; + stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return; return; }; } @@ -611,7 +611,7 @@ fn printLineInfo( tty_config.setColor(out_stream, .White); if (line_info) |*li| { - try out_stream.print("{}:{}:{}", .{ li.file_name, li.line, li.column }); + try out_stream.print("{s}:{d}:{d}", .{ li.file_name, li.line, li.column }); } else { try out_stream.writeAll("???:?:?"); } @@ -619,7 +619,7 @@ fn printLineInfo( tty_config.setColor(out_stream, .Reset); try out_stream.writeAll(": "); tty_config.setColor(out_stream, .Dim); - try out_stream.print("0x{x} in {} ({})", .{ address, symbol_name, compile_unit_name }); + try out_stream.print("0x{x} in {s} ({s})", .{ address, symbol_name, compile_unit_name }); tty_config.setColor(out_stream, .Reset); try out_stream.writeAll("\n"); diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index b79dea04cbad..2e732cbc7546 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -413,10 +413,7 @@ pub const DwarfInfo = struct { var this_unit_offset: u64 = 0; while (this_unit_offset < try seekable.getEndPos()) { - seekable.seekTo(this_unit_offset) catch |err| switch (err) { - error.EndOfStream => unreachable, - else => return err, - }; + try seekable.seekTo(this_unit_offset); var is_64: bool = undefined; const unit_length = try readUnitLength(in, di.endian, &is_64); @@ -520,10 +517,7 @@ pub const DwarfInfo = struct { var this_unit_offset: u64 = 0; while (this_unit_offset < try seekable.getEndPos()) { - seekable.seekTo(this_unit_offset) catch |err| switch (err) { - error.EndOfStream => unreachable, - else => return err, - }; + try seekable.seekTo(this_unit_offset); var is_64: bool = undefined; const unit_length = try readUnitLength(in, di.endian, &is_64); diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig index e9112c917834..788f8f19334b 100644 --- a/lib/std/fifo.zig +++ b/lib/std/fifo.zig @@ -466,7 +466,7 @@ test "LinearFifo(u8, .Dynamic)" { fifo.shrink(0); { - try fifo.writer().print("{}, {}!", .{ "Hello", "World" }); + try fifo.writer().print("{s}, {s}!", .{ "Hello", "World" }); var result: [30]u8 = undefined; testing.expectEqualSlices(u8, "Hello, World!", result[0..fifo.read(&result)]); testing.expectEqual(@as(usize, 0), fifo.readableLength()); diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 803df7da9460..bc9ac922831e 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -367,6 +367,36 @@ pub fn format( } } +pub fn formatAddress(value: anytype, options: FormatOptions, writer: anytype) @TypeOf(writer).Error!void { + const T = @TypeOf(value); + + switch (@typeInfo(T)) { + .Pointer => |info| { + try writer.writeAll(@typeName(info.child) ++ "@"); + if (info.size == .Slice) + try formatInt(@ptrToInt(value.ptr), 16, false, FormatOptions{}, writer) + else + try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer); + return; + }, + .Optional => |info| { + if (@typeInfo(info.child) == .Pointer) { + try writer.writeAll(@typeName(info.child) ++ "@"); + try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer); + return; + } + }, + .Array => |info| { + try writer.writeAll(@typeName(info.child) ++ "@"); + try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer); + return; + }, + else => {}, + } + + @compileError("Cannot format non-pointer type " ++ @typeName(T) ++ " with * specifier"); +} + pub fn formatType( value: anytype, comptime fmt: []const u8, @@ -375,10 +405,7 @@ pub fn formatType( max_depth: usize, ) @TypeOf(writer).Error!void { if (comptime std.mem.eql(u8, fmt, "*")) { - try writer.writeAll(@typeName(std.meta.Child(@TypeOf(value)))); - try writer.writeAll("@"); - try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer); - return; + return formatAddress(value, options, writer); } const T = @TypeOf(value); @@ -436,12 +463,11 @@ pub fn formatType( try formatType(@enumToInt(value), fmt, options, writer, max_depth); try writer.writeAll(")"); }, - .Union => { + .Union => |info| { try writer.writeAll(@typeName(T)); if (max_depth == 0) { return writer.writeAll("{ ... }"); } - const info = @typeInfo(T).Union; if (info.tag_type) |UnionTagType| { try writer.writeAll("{ ."); try writer.writeAll(@tagName(@as(UnionTagType, value))); @@ -456,13 +482,13 @@ pub fn formatType( try format(writer, "@{x}", .{@ptrToInt(&value)}); } }, - .Struct => |StructT| { + .Struct => |info| { try writer.writeAll(@typeName(T)); if (max_depth == 0) { return writer.writeAll("{ ... }"); } try writer.writeAll("{"); - inline for (StructT.fields) |f, i| { + inline for (info.fields) |f, i| { if (i == 0) { try writer.writeAll(" ."); } else { @@ -478,69 +504,83 @@ pub fn formatType( .One => switch (@typeInfo(ptr_info.child)) { .Array => |info| { if (info.child == u8) { - return formatText(value, fmt, options, writer); + if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { + return formatText(value, fmt, options, writer); + } } - return format(writer, "{}@{x}", .{ @typeName(@typeInfo(T).Pointer.child), @ptrToInt(value) }); + return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }); }, .Enum, .Union, .Struct => { return formatType(value.*, fmt, options, writer, max_depth); }, - else => return format(writer, "{}@{x}", .{ @typeName(@typeInfo(T).Pointer.child), @ptrToInt(value) }), + else => return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }), }, .Many, .C => { if (ptr_info.sentinel) |sentinel| { return formatType(mem.span(value), fmt, options, writer, max_depth); } if (ptr_info.child == u8) { - if (fmt.len > 0 and fmt[0] == 's') { + if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { return formatText(mem.span(value), fmt, options, writer); } } - return format(writer, "{}@{x}", .{ @typeName(@typeInfo(T).Pointer.child), @ptrToInt(value) }); + return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }); }, .Slice => { - if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) { - return formatText(value, fmt, options, writer); + if (max_depth == 0) { + return writer.writeAll("{ ... }"); } if (ptr_info.child == u8) { - return formatText(value, fmt, options, writer); + if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { + return formatText(value, fmt, options, writer); + } } - return format(writer, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) }); + try writer.writeAll("{ "); + for (value) |elem, i| { + try formatType(elem, fmt, options, writer, max_depth - 1); + if (i != value.len - 1) { + try writer.writeAll(", "); + } + } + try writer.writeAll(" }"); }, }, .Array => |info| { - const Slice = @Type(builtin.TypeInfo{ - .Pointer = .{ - .size = .Slice, - .is_const = true, - .is_volatile = false, - .is_allowzero = false, - .alignment = @alignOf(info.child), - .child = info.child, - .sentinel = null, - }, - }); - return formatType(@as(Slice, &value), fmt, options, writer, max_depth); + if (max_depth == 0) { + return writer.writeAll("{ ... }"); + } + if (info.child == u8) { + if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) { + return formatText(&value, fmt, options, writer); + } + } + try writer.writeAll("{ "); + for (value) |elem, i| { + try formatType(elem, fmt, options, writer, max_depth - 1); + if (i < value.len - 1) { + try writer.writeAll(", "); + } + } + try writer.writeAll(" }"); }, - .Vector => { - const len = @typeInfo(T).Vector.len; + .Vector => |info| { try writer.writeAll("{ "); var i: usize = 0; - while (i < len) : (i += 1) { + while (i < info.len) : (i += 1) { try formatValue(value[i], fmt, options, writer); - if (i < len - 1) { + if (i < info.len - 1) { try writer.writeAll(", "); } } try writer.writeAll(" }"); }, .Fn => { - return format(writer, "{}@{x}", .{ @typeName(T), @ptrToInt(value) }); + return format(writer, "{s}@{x}", .{ @typeName(T), @ptrToInt(value) }); }, .Type => return formatBuf(@typeName(value), options, writer), .EnumLiteral => { const buffer = [_]u8{'.'} ++ @tagName(value); - return formatType(buffer, fmt, options, writer, max_depth); + return formatBuf(buffer, options, writer); }, .Null => return formatBuf("null", options, writer), else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), @@ -657,7 +697,7 @@ pub fn formatText( options: FormatOptions, writer: anytype, ) !void { - if (comptime std.mem.eql(u8, fmt, "s") or (fmt.len == 0)) { + if (comptime std.mem.eql(u8, fmt, "s")) { return formatBuf(bytes, options, writer); } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) { for (bytes) |c| { @@ -1521,8 +1561,9 @@ test "buffer" { test "array" { { const value: [3]u8 = "abc".*; - try testFmt("array: abc\n", "array: {}\n", .{value}); - try testFmt("array: abc\n", "array: {}\n", .{&value}); + try testFmt("array: abc\n", "array: {s}\n", .{value}); + try testFmt("array: abc\n", "array: {s}\n", .{&value}); + try testFmt("array: { 97, 98, 99 }\n", "array: {d}\n", .{value}); var buf: [100]u8 = undefined; try testFmt( @@ -1536,12 +1577,12 @@ test "array" { test "slice" { { const value: []const u8 = "abc"; - try testFmt("slice: abc\n", "slice: {}\n", .{value}); + try testFmt("slice: abc\n", "slice: {s}\n", .{value}); } { var runtime_zero: usize = 0; const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[runtime_zero..runtime_zero]; - try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value}); + try testFmt("slice: []const u8@deadbeef\n", "slice: {*}\n", .{value}); } { const null_term_slice: [:0]const u8 = "\x00hello\x00"; @@ -1550,6 +1591,15 @@ test "slice" { try testFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"}); try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); + + { + var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 }; + var runtime_zero: usize = 0; + try testFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {}", .{int_slice[runtime_zero..]}); + try testFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {d}", .{int_slice[runtime_zero..]}); + try testFmt("int: { 1, 1000, 5fad3, 423a35c7 }", "int: {x}", .{int_slice[runtime_zero..]}); + try testFmt("int: { 00001, 01000, 5fad3, 423a35c7 }", "int: {x:0>5}", .{int_slice[runtime_zero..]}); + } } test "escape non-printable" { @@ -1854,9 +1904,9 @@ fn testFmt(expected: []const u8, comptime template: []const u8, args: anytype) ! if (mem.eql(u8, result, expected)) return; std.debug.warn("\n====== expected this output: =========\n", .{}); - std.debug.warn("{}", .{expected}); + std.debug.warn("{s}", .{expected}); std.debug.warn("\n======== instead found this: =========\n", .{}); - std.debug.warn("{}", .{result}); + std.debug.warn("{s}", .{result}); std.debug.warn("\n======================================\n", .{}); return error.TestFailed; } @@ -2013,24 +2063,24 @@ test "vector" { } test "enum-literal" { - try testFmt(".hello_world", "{}", .{.hello_world}); + try testFmt(".hello_world", "{s}", .{.hello_world}); } test "padding" { - try testFmt("Simple", "{}", .{"Simple"}); + try testFmt("Simple", "{s}", .{"Simple"}); try testFmt(" true", "{:10}", .{true}); try testFmt(" true", "{:>10}", .{true}); try testFmt("======true", "{:=>10}", .{true}); try testFmt("true======", "{:=<10}", .{true}); try testFmt(" true ", "{:^10}", .{true}); try testFmt("===true===", "{:=^10}", .{true}); - try testFmt(" Minimum width", "{:18} width", .{"Minimum"}); - try testFmt("==================Filled", "{:=>24}", .{"Filled"}); - try testFmt(" Centered ", "{:^24}", .{"Centered"}); - try testFmt("-", "{:-^1}", .{""}); - try testFmt("==crêpe===", "{:=^10}", .{"crêpe"}); - try testFmt("=====crêpe", "{:=>10}", .{"crêpe"}); - try testFmt("crêpe=====", "{:=<10}", .{"crêpe"}); + try testFmt(" Minimum width", "{s:18} width", .{"Minimum"}); + try testFmt("==================Filled", "{s:=>24}", .{"Filled"}); + try testFmt(" Centered ", "{s:^24}", .{"Centered"}); + try testFmt("-", "{s:-^1}", .{""}); + try testFmt("==crêpe===", "{s:=^10}", .{"crêpe"}); + try testFmt("=====crêpe", "{s:=>10}", .{"crêpe"}); + try testFmt("crêpe=====", "{s:=<10}", .{"crêpe"}); } test "decimal float padding" { @@ -2059,15 +2109,15 @@ test "type" { } test "named arguments" { - try testFmt("hello world!", "{} world{c}", .{ "hello", '!' }); - try testFmt("hello world!", "{[greeting]} world{[punctuation]c}", .{ .punctuation = '!', .greeting = "hello" }); - try testFmt("hello world!", "{[1]} world{[0]c}", .{ '!', "hello" }); + try testFmt("hello world!", "{s} world{c}", .{ "hello", '!' }); + try testFmt("hello world!", "{[greeting]s} world{[punctuation]c}", .{ .punctuation = '!', .greeting = "hello" }); + try testFmt("hello world!", "{[1]s} world{[0]c}", .{ '!', "hello" }); } test "runtime width specifier" { var width: usize = 9; - try testFmt("~~hello~~", "{:~^[1]}", .{ "hello", width }); - try testFmt("~~hello~~", "{:~^[width]}", .{ .string = "hello", .width = width }); + try testFmt("~~hello~~", "{s:~^[1]}", .{ "hello", width }); + try testFmt("~~hello~~", "{s:~^[width]}", .{ .string = "hello", .width = width }); } test "runtime precision specifier" { diff --git a/lib/std/fs/wasi.zig b/lib/std/fs/wasi.zig index 761f6e846610..900adc5e2d5a 100644 --- a/lib/std/fs/wasi.zig +++ b/lib/std/fs/wasi.zig @@ -38,7 +38,7 @@ pub const PreopenType = union(PreopenTypeTag) { pub fn format(self: Self, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: anytype) !void { try out_stream.print("PreopenType{{ ", .{}); switch (self) { - PreopenType.Dir => |path| try out_stream.print(".Dir = '{}'", .{path}), + PreopenType.Dir => |path| try out_stream.print(".Dir = '{z}'", .{path}), } return out_stream.print(" }}", .{}); } diff --git a/lib/std/hash/crc.zig b/lib/std/hash/crc.zig index b132713a7e93..a2d6ed429ccd 100644 --- a/lib/std/hash/crc.zig +++ b/lib/std/hash/crc.zig @@ -102,7 +102,11 @@ pub fn Crc32WithPoly(comptime poly: Polynomial) type { }; } +const please_windows_dont_oom = std.Target.current.os.tag == .windows; + test "crc32 ieee" { + if (please_windows_dont_oom) return error.SkipZigTest; + const Crc32Ieee = Crc32WithPoly(.IEEE); testing.expect(Crc32Ieee.hash("") == 0x00000000); @@ -111,6 +115,8 @@ test "crc32 ieee" { } test "crc32 castagnoli" { + if (please_windows_dont_oom) return error.SkipZigTest; + const Crc32Castagnoli = Crc32WithPoly(.Castagnoli); testing.expect(Crc32Castagnoli.hash("") == 0x00000000); @@ -167,6 +173,8 @@ pub fn Crc32SmallWithPoly(comptime poly: Polynomial) type { } test "small crc32 ieee" { + if (please_windows_dont_oom) return error.SkipZigTest; + const Crc32Ieee = Crc32SmallWithPoly(.IEEE); testing.expect(Crc32Ieee.hash("") == 0x00000000); @@ -175,6 +183,8 @@ test "small crc32 ieee" { } test "small crc32 castagnoli" { + if (please_windows_dont_oom) return error.SkipZigTest; + const Crc32Castagnoli = Crc32SmallWithPoly(.Castagnoli); testing.expect(Crc32Castagnoli.hash("") == 0x00000000); diff --git a/lib/std/heap/general_purpose_allocator.zig b/lib/std/heap/general_purpose_allocator.zig index e7766f6445b1..415b206b6a65 100644 --- a/lib/std/heap/general_purpose_allocator.zig +++ b/lib/std/heap/general_purpose_allocator.zig @@ -314,7 +314,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { if (is_used) { const slot_index = @intCast(SlotIndex, used_bits_byte * 8 + bit_index); const stack_trace = bucketStackTrace(bucket, size_class, slot_index, .alloc); - log.err("Memory leak detected: {}", .{stack_trace}); + log.err("Memory leak detected: {s}", .{stack_trace}); leaks = true; } if (bit_index == math.maxInt(u3)) @@ -342,7 +342,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { } var it = self.large_allocations.iterator(); while (it.next()) |large_alloc| { - log.err("Memory leak detected: {}", .{large_alloc.value.getStackTrace()}); + log.err("Memory leak detected: {s}", .{large_alloc.value.getStackTrace()}); leaks = true; } return leaks; @@ -443,7 +443,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { .index = 0, }; std.debug.captureStackTrace(ret_addr, &free_stack_trace); - log.err("Allocation size {} bytes does not match free size {}. Allocation: {} Free: {}", .{ + log.err("Allocation size {d} bytes does not match free size {d}. Allocation: {s} Free: {s}", .{ entry.value.bytes.len, old_mem.len, entry.value.getStackTrace(), @@ -526,7 +526,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { .index = 0, }; std.debug.captureStackTrace(ret_addr, &second_free_stack_trace); - log.err("Double free detected. Allocation: {} First free: {} Second free: {}", .{ + log.err("Double free detected. Allocation: {s} First free: {s} Second free: {s}", .{ alloc_stack_trace, free_stack_trace, second_free_stack_trace, diff --git a/lib/std/io/fixed_buffer_stream.zig b/lib/std/io/fixed_buffer_stream.zig index e698ce061ef7..b36949e53889 100644 --- a/lib/std/io/fixed_buffer_stream.zig +++ b/lib/std/io/fixed_buffer_stream.zig @@ -147,7 +147,7 @@ test "FixedBufferStream output" { var fbs = fixedBufferStream(&buf); const stream = fbs.writer(); - try stream.print("{}{}!", .{ "Hello", "World" }); + try stream.print("{s}{s}!", .{ "Hello", "World" }); testing.expectEqualSlices(u8, "HelloWorld!", fbs.getWritten()); } diff --git a/lib/std/json.zig b/lib/std/json.zig index e412676fa390..f5be3a2094d2 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -2642,9 +2642,9 @@ fn teststringify(expected: []const u8, value: anytype, options: StringifyOptions if (self.expected_remaining.len < bytes.len) { std.debug.warn( \\====== expected this output: ========= - \\{} + \\{s} \\======== instead found this: ========= - \\{} + \\{s} \\====================================== , .{ self.expected_remaining, @@ -2655,9 +2655,9 @@ fn teststringify(expected: []const u8, value: anytype, options: StringifyOptions if (!mem.eql(u8, self.expected_remaining[0..bytes.len], bytes)) { std.debug.warn( \\====== expected this output: ========= - \\{} + \\{s} \\======== instead found this: ========= - \\{} + \\{s} \\====================================== , .{ self.expected_remaining[0..bytes.len], diff --git a/lib/std/linked_list.zig b/lib/std/linked_list.zig index b30a99f70823..2a6b58c8c9af 100644 --- a/lib/std/linked_list.zig +++ b/lib/std/linked_list.zig @@ -62,7 +62,7 @@ pub fn SinglyLinkedList(comptime T: type) type { /// This operation is O(N). pub fn countChildren(node: *const Node) usize { var count: usize = 0; - var it: ?*const Node = node; + var it: ?*const Node = node.next; while (it) |n| : (it = n.next) { count += 1; } @@ -123,6 +123,8 @@ test "basic SinglyLinkedList test" { const L = SinglyLinkedList(u32); var list = L{}; + testing.expect(list.len() == 0); + var one = L.Node{ .data = 1 }; var two = L.Node{ .data = 2 }; var three = L.Node{ .data = 3 }; @@ -135,6 +137,8 @@ test "basic SinglyLinkedList test" { two.insertAfter(&three); // {1, 2, 3, 5} three.insertAfter(&four); // {1, 2, 3, 4, 5} + testing.expect(list.len() == 5); + // Traverse forwards. { var it = list.first; diff --git a/lib/std/meta/trait.zig b/lib/std/meta/trait.zig index 8c8b26cf4595..8e542935339e 100644 --- a/lib/std/meta/trait.zig +++ b/lib/std/meta/trait.zig @@ -298,6 +298,20 @@ pub fn isNumber(comptime T: type) bool { }; } +pub fn isIntegerNumber(comptime T: type) bool { + return switch (@typeInfo(T)) { + .Int, .ComptimeInt => true, + else => false, + }; +} + +pub fn isFloatingNumber(comptime T: type) bool { + return switch (@typeInfo(T)) { + .Float, .ComptimeFloat => true, + else => false, + }; +} + test "std.meta.trait.isNumber" { const NotANumber = struct { number: u8, diff --git a/lib/std/net.zig b/lib/std/net.zig index 5b17714fae1a..b8f48b20202d 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -154,7 +154,7 @@ pub const Address = extern union { unreachable; } - try std.fmt.format(out_stream, "{}", .{&self.un.path}); + try std.fmt.format(out_stream, "{s}", .{&self.un.path}); }, else => unreachable, } diff --git a/lib/std/os.zig b/lib/std/os.zig index fb187252a18b..e9bf6379ccac 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -4256,7 +4256,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { }, .linux => { var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined; - const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable; + const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{d}\x00", .{fd}) catch unreachable; const target = readlinkZ(std.meta.assumeSentinel(proc_path.ptr, 0), out_buffer) catch |err| { switch (err) { @@ -4487,7 +4487,7 @@ pub const UnexpectedError = error{ /// and you get an unexpected error. pub fn unexpectedErrno(err: usize) UnexpectedError { if (unexpected_error_tracing) { - std.debug.warn("unexpected errno: {}\n", .{err}); + std.debug.warn("unexpected errno: {d}\n", .{err}); std.debug.dumpCurrentStackTrace(null); } return error.Unexpected; @@ -4853,6 +4853,7 @@ pub fn send( error.NotDir => unreachable, error.NetworkUnreachable => unreachable, error.AddressNotAvailable => unreachable, + error.SocketNotConnected => unreachable, else => |e| return e, }; } diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 2d46b34bf702..73258ee1aa10 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -1284,6 +1284,9 @@ pub const IORING_SETUP_CLAMP = 1 << 4; /// attach to existing wq pub const IORING_SETUP_ATTACH_WQ = 1 << 5; +/// start with ring disabled +pub const IORING_SETUP_R_DISABLED = 1 << 6; + pub const io_sqring_offsets = extern struct { /// offset of ring head head: u32, @@ -1430,6 +1433,11 @@ pub const io_uring_cqe = extern struct { flags: u32, }; +// io_uring_cqe.flags + +/// If set, the upper 16 bits are the buffer ID +pub const IORING_CQE_F_BUFFER = 1 << 0; + pub const IORING_OFF_SQ_RING = 0; pub const IORING_OFF_CQ_RING = 0x8000000; pub const IORING_OFF_SQES = 0x10000000; @@ -1439,7 +1447,7 @@ pub const IORING_ENTER_GETEVENTS = 1 << 0; pub const IORING_ENTER_SQ_WAKEUP = 1 << 1; // io_uring_register opcodes and arguments -pub const IORING_REGISTER = extern enum(u32) { +pub const IORING_REGISTER = extern enum(u8) { REGISTER_BUFFERS, UNREGISTER_BUFFERS, REGISTER_FILES, @@ -1451,11 +1459,13 @@ pub const IORING_REGISTER = extern enum(u32) { REGISTER_PROBE, REGISTER_PERSONALITY, UNREGISTER_PERSONALITY, + REGISTER_RESTRICTIONS, + REGISTER_ENABLE_RINGS, _, }; -pub const io_uring_files_update = struct { +pub const io_uring_files_update = extern struct { offset: u32, resv: u32, fds: u64, @@ -1463,7 +1473,7 @@ pub const io_uring_files_update = struct { pub const IO_URING_OP_SUPPORTED = 1 << 0; -pub const io_uring_probe_op = struct { +pub const io_uring_probe_op = extern struct { op: IORING_OP, resv: u8, @@ -1474,7 +1484,7 @@ pub const io_uring_probe_op = struct { resv2: u32, }; -pub const io_uring_probe = struct { +pub const io_uring_probe = extern struct { /// last opcode supported last_op: IORING_OP, @@ -1487,6 +1497,39 @@ pub const io_uring_probe = struct { // Followed by up to `ops_len` io_uring_probe_op structures }; +pub const io_uring_restriction = extern struct { + opcode: u16, + arg: extern union { + /// IORING_RESTRICTION_REGISTER_OP + register_op: IORING_REGISTER, + + /// IORING_RESTRICTION_SQE_OP + sqe_op: IORING_OP, + + /// IORING_RESTRICTION_SQE_FLAGS_* + sqe_flags: u8, + }, + resv: u8, + resv2: u32[3], +}; + +/// io_uring_restriction->opcode values +pub const IORING_RESTRICTION = extern enum(u8) { + /// Allow an io_uring_register(2) opcode + REGISTER_OP = 0, + + /// Allow an sqe opcode + SQE_OP = 1, + + /// Allow sqe flags + SQE_FLAGS_ALLOWED = 2, + + /// Require sqe flags (these flags must be set on each submission) + SQE_FLAGS_REQUIRED = 3, + + _, +}; + pub const utsname = extern struct { sysname: [64:0]u8, nodename: [64:0]u8, diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index 32edbe6ac8fb..415d2192c59a 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -28,7 +28,7 @@ pub const IO_Uring = struct { /// call on how many entries the submission and completion queues will ultimately have, /// see https://github.com/torvalds/linux/blob/v5.8/fs/io_uring.c#L8027-L8050. /// Matches the interface of io_uring_queue_init() in liburing. - pub fn init(entries: u12, flags: u32) !IO_Uring { + pub fn init(entries: u13, flags: u32) !IO_Uring { var params = mem.zeroInit(io_uring_params, .{ .flags = flags, .sq_thread_idle = 1000, @@ -39,17 +39,15 @@ pub const IO_Uring = struct { /// A powerful way to setup an io_uring, if you want to tweak io_uring_params such as submission /// queue thread cpu affinity or thread idle timeout (the kernel and our default is 1 second). /// `params` is passed by reference because the kernel needs to modify the parameters. - /// You may only set the `flags`, `sq_thread_cpu` and `sq_thread_idle` parameters. - /// Every other parameter belongs to the kernel and must be zeroed. /// Matches the interface of io_uring_queue_init_params() in liburing. - pub fn init_params(entries: u12, p: *io_uring_params) !IO_Uring { + pub fn init_params(entries: u13, p: *io_uring_params) !IO_Uring { if (entries == 0) return error.EntriesZero; if (!std.math.isPowerOfTwo(entries)) return error.EntriesNotPowerOfTwo; assert(p.sq_entries == 0); - assert(p.cq_entries == 0); + assert(p.cq_entries == 0 or p.flags & linux.IORING_SETUP_CQSIZE != 0); assert(p.features == 0); - assert(p.wq_fd == 0); + assert(p.wq_fd == 0 or p.flags & linux.IORING_SETUP_ATTACH_WQ != 0); assert(p.resv[0] == 0); assert(p.resv[1] == 0); assert(p.resv[2] == 0); diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 40271cdc3880..1c6e5202a368 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1618,7 +1618,7 @@ pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError { null, ); _ = std.unicode.utf16leToUtf8(&buf_u8, buf_u16[0..len]) catch unreachable; - std.debug.warn("error.Unexpected: GetLastError({}): {}\n", .{ @enumToInt(err), buf_u8[0..len] }); + std.debug.warn("error.Unexpected: GetLastError({}): {s}\n", .{ @enumToInt(err), buf_u8[0..len] }); std.debug.dumpCurrentStackTrace(null); } return error.Unexpected; diff --git a/lib/std/process.zig b/lib/std/process.zig index 3f944f10b6d1..3529bf52cb12 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -596,7 +596,7 @@ fn testWindowsCmdLine(input_cmd_line: [*]const u16, expected_args: []const []con for (expected_args) |expected_arg| { const arg = it.next(std.testing.allocator).? catch unreachable; defer std.testing.allocator.free(arg); - testing.expectEqualSlices(u8, expected_arg, arg); + testing.expectEqualStrings(expected_arg, arg); } testing.expect(it.next(std.testing.allocator) == null); } diff --git a/lib/std/rand/ziggurat.zig b/lib/std/rand/ziggurat.zig index c84667603e3f..fe120943d7ea 100644 --- a/lib/std/rand/ziggurat.zig +++ b/lib/std/rand/ziggurat.zig @@ -131,7 +131,11 @@ fn norm_zero_case(random: *Random, u: f64) f64 { } } -test "ziggurant normal dist sanity" { +const please_windows_dont_oom = std.Target.current.os.tag == .windows; + +test "normal dist sanity" { + if (please_windows_dont_oom) return error.SkipZigTest; + var prng = std.rand.DefaultPrng.init(0); var i: usize = 0; while (i < 1000) : (i += 1) { @@ -158,7 +162,9 @@ fn exp_zero_case(random: *Random, _: f64) f64 { return exp_r - math.ln(random.float(f64)); } -test "ziggurant exp dist sanity" { +test "exp dist sanity" { + if (please_windows_dont_oom) return error.SkipZigTest; + var prng = std.rand.DefaultPrng.init(0); var i: usize = 0; while (i < 1000) : (i += 1) { @@ -166,6 +172,8 @@ test "ziggurant exp dist sanity" { } } -test "ziggurat table gen" { +test "table gen" { + if (please_windows_dont_oom) return error.SkipZigTest; + const table = NormDist; } diff --git a/lib/std/special/build_runner.zig b/lib/std/special/build_runner.zig index e3e90e7574f1..f2fa2dc3b806 100644 --- a/lib/std/special/build_runner.zig +++ b/lib/std/special/build_runner.zig @@ -98,7 +98,7 @@ pub fn main() !void { return usageAndErr(builder, false, stderr_stream); }; builder.color = std.meta.stringToEnum(@TypeOf(builder.color), next_arg) orelse { - warn("expected [auto|on|off] after --color, found '{}'", .{next_arg}); + warn("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); return usageAndErr(builder, false, stderr_stream); }; } else if (mem.eql(u8, arg, "--override-lib-dir")) { @@ -126,7 +126,7 @@ pub fn main() !void { builder.args = argsRest(args, arg_idx); break; } else { - warn("Unrecognized argument: {}\n\n", .{arg}); + warn("Unrecognized argument: {s}\n\n", .{arg}); return usageAndErr(builder, false, stderr_stream); } } else { @@ -168,7 +168,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void } try out_stream.print( - \\Usage: {} build [steps] [options] + \\Usage: {s} build [steps] [options] \\ \\Steps: \\ @@ -177,10 +177,10 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void const allocator = builder.allocator; for (builder.top_level_steps.items) |top_level_step| { const name = if (&top_level_step.step == builder.default_step) - try fmt.allocPrint(allocator, "{} (default)", .{top_level_step.step.name}) + try fmt.allocPrint(allocator, "{s} (default)", .{top_level_step.step.name}) else top_level_step.step.name; - try out_stream.print(" {s:<27} {}\n", .{ name, top_level_step.description }); + try out_stream.print(" {s:<27} {s}\n", .{ name, top_level_step.description }); } try out_stream.writeAll( @@ -200,12 +200,12 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void try out_stream.print(" (none)\n", .{}); } else { for (builder.available_options_list.items) |option| { - const name = try fmt.allocPrint(allocator, " -D{}=[{}]", .{ + const name = try fmt.allocPrint(allocator, " -D{s}=[{s}]", .{ option.name, Builder.typeIdName(option.type_id), }); defer allocator.free(name); - try out_stream.print("{s:<29} {}\n", .{ name, option.description }); + try out_stream.print("{s:<29} {s}\n", .{ name, option.description }); } } diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index c92ffa21ac88..51cbafc133f4 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -172,7 +172,7 @@ test "strncmp" { pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { if (builtin.is_test) { @setCold(true); - std.debug.panic("{}", .{msg}); + std.debug.panic("{s}", .{msg}); } if (builtin.os.tag != .freestanding and builtin.os.tag != .other) { std.os.abort(); diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index e0743cd4b70e..a06c568b2e78 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -324,7 +324,7 @@ pub usingnamespace @import("compiler_rt/atomics.zig"); pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { @setCold(true); if (is_test) { - std.debug.panic("{}", .{msg}); + std.debug.panic("{s}", .{msg}); } else { unreachable; } diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig index 6c1a298759b0..f5a93298b531 100644 --- a/lib/std/special/test_runner.zig +++ b/lib/std/special/test_runner.zig @@ -48,7 +48,7 @@ pub fn main() anyerror!void { test_node.activate(); progress.refresh(); if (progress.terminal == null) { - std.debug.print("{}/{} {}... ", .{ i + 1, test_fn_list.len, test_fn.name }); + std.debug.print("{d}/{d} {s}... ", .{ i + 1, test_fn_list.len, test_fn.name }); } const result = if (test_fn.async_frame_size) |size| switch (io_mode) { .evented => blk: { @@ -62,7 +62,7 @@ pub fn main() anyerror!void { .blocking => { skip_count += 1; test_node.end(); - progress.log("{}...SKIP (async test)\n", .{test_fn.name}); + progress.log("{s}...SKIP (async test)\n", .{test_fn.name}); if (progress.terminal == null) std.debug.print("SKIP (async test)\n", .{}); continue; }, @@ -75,7 +75,7 @@ pub fn main() anyerror!void { error.SkipZigTest => { skip_count += 1; test_node.end(); - progress.log("{}...SKIP\n", .{test_fn.name}); + progress.log("{s}...SKIP\n", .{test_fn.name}); if (progress.terminal == null) std.debug.print("SKIP\n", .{}); }, else => { @@ -86,15 +86,15 @@ pub fn main() anyerror!void { } root_node.end(); if (ok_count == test_fn_list.len) { - std.debug.print("All {} tests passed.\n", .{ok_count}); + std.debug.print("All {d} tests passed.\n", .{ok_count}); } else { - std.debug.print("{} passed; {} skipped.\n", .{ ok_count, skip_count }); + std.debug.print("{d} passed; {d} skipped.\n", .{ ok_count, skip_count }); } if (log_err_count != 0) { - std.debug.print("{} errors were logged.\n", .{log_err_count}); + std.debug.print("{d} errors were logged.\n", .{log_err_count}); } if (leaks != 0) { - std.debug.print("{} tests leaked memory.\n", .{leaks}); + std.debug.print("{d} tests leaked memory.\n", .{leaks}); } if (leaks != 0 or log_err_count != 0) { std.process.exit(1); @@ -111,6 +111,6 @@ pub fn log( log_err_count += 1; } if (@enumToInt(message_level) <= @enumToInt(std.testing.log_level)) { - std.debug.print("[{}] ({}): " ++ format ++ "\n", .{ @tagName(scope), @tagName(message_level) } ++ args); + std.debug.print("[{s}] ({s}): " ++ format ++ "\n", .{ @tagName(scope), @tagName(message_level) } ++ args); } } diff --git a/lib/std/start.zig b/lib/std/start.zig index a8fbf251f7c9..718d48130e3b 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -266,7 +266,7 @@ inline fn initEventLoopAndCallMain() u8 { if (std.event.Loop.instance) |loop| { if (!@hasDecl(root, "event_loop")) { loop.init() catch |err| { - std.log.err("{}", .{@errorName(err)}); + std.log.err("{s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } @@ -295,7 +295,7 @@ inline fn initEventLoopAndCallWinMain() std.os.windows.INT { if (std.event.Loop.instance) |loop| { if (!@hasDecl(root, "event_loop")) { loop.init() catch |err| { - std.log.err("{}", .{@errorName(err)}); + std.log.err("{s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } @@ -343,7 +343,7 @@ pub fn callMain() u8 { }, .ErrorUnion => { const result = root.main() catch |err| { - std.log.err("{}", .{@errorName(err)}); + std.log.err("{s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } diff --git a/lib/std/std.zig b/lib/std/std.zig index a1f3621023bf..091095bdb556 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -93,5 +93,41 @@ comptime { } test "" { - testing.refAllDecls(@This()); + if (builtin.os.tag == .windows) { + // We only test the Windows-relevant stuff to save memory because the CI + // server is hitting OOM. TODO revert this after stage2 arrives. + _ = ChildProcess; + _ = DynLib; + _ = mutex; + _ = Mutex; + _ = Progress; + _ = ResetEvent; + _ = SpinLock; + _ = StaticResetEvent; + _ = Target; + _ = Thread; + + _ = atomic; + _ = build; + _ = builtin; + _ = debug; + _ = event; + _ = fs; + _ = heap; + _ = io; + _ = log; + _ = macho; + _ = net; + _ = os; + _ = once; + _ = pdb; + _ = process; + _ = testing; + _ = time; + _ = unicode; + _ = zig; + _ = start; + } else { + testing.refAllDecls(@This()); + } } diff --git a/lib/std/target.zig b/lib/std/target.zig index 165a8fcae735..2b3518bc92f4 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -136,14 +136,14 @@ pub const Target = struct { ) !void { if (fmt.len > 0 and fmt[0] == 's') { if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.latest)) { - try std.fmt.format(out_stream, ".{}", .{@tagName(self)}); + try std.fmt.format(out_stream, ".{s}", .{@tagName(self)}); } else { // TODO this code path breaks zig triples, but it is used in `builtin` try std.fmt.format(out_stream, "@intToEnum(Target.Os.WindowsVersion, 0x{X:0>8})", .{@enumToInt(self)}); } } else { if (@enumToInt(self) >= @enumToInt(WindowsVersion.nt4) and @enumToInt(self) <= @enumToInt(WindowsVersion.latest)) { - try std.fmt.format(out_stream, "WindowsVersion.{}", .{@tagName(self)}); + try std.fmt.format(out_stream, "WindowsVersion.{s}", .{@tagName(self)}); } else { try std.fmt.format(out_stream, "WindowsVersion(0x{X:0>8})", .{@enumToInt(self)}); } @@ -1177,7 +1177,7 @@ pub const Target = struct { } pub fn linuxTripleSimple(allocator: *mem.Allocator, cpu_arch: Cpu.Arch, os_tag: Os.Tag, abi: Abi) ![]u8 { - return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ @tagName(cpu_arch), @tagName(os_tag), @tagName(abi) }); + return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ @tagName(cpu_arch), @tagName(os_tag), @tagName(abi) }); } pub fn linuxTriple(self: Target, allocator: *mem.Allocator) ![]u8 { @@ -1381,7 +1381,7 @@ pub const Target = struct { if (self.abi == .android) { const suffix = if (self.cpu.arch.ptrBitWidth() == 64) "64" else ""; - return print(&result, "/system/bin/linker{}", .{suffix}); + return print(&result, "/system/bin/linker{s}", .{suffix}); } if (self.abi.isMusl()) { @@ -1395,7 +1395,7 @@ pub const Target = struct { else => |arch| @tagName(arch), }; const arch_suffix = if (is_arm and self.abi.floatAbi() == .hard) "hf" else ""; - return print(&result, "/lib/ld-musl-{}{}.so.1", .{ arch_part, arch_suffix }); + return print(&result, "/lib/ld-musl-{s}{s}.so.1", .{ arch_part, arch_suffix }); } switch (self.os.tag) { @@ -1434,7 +1434,7 @@ pub const Target = struct { }; const is_nan_2008 = mips.featureSetHas(self.cpu.features, .nan2008); const loader = if (is_nan_2008) "ld-linux-mipsn8.so.1" else "ld.so.1"; - return print(&result, "/lib{}/{}", .{ lib_suffix, loader }); + return print(&result, "/lib{s}/{s}", .{ lib_suffix, loader }); }, .powerpc => return copy(&result, "/lib/ld.so.1"), diff --git a/lib/std/testing.zig b/lib/std/testing.zig index a6f749b158d4..4f4a46dbf7c2 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -29,10 +29,10 @@ pub var zig_exe_path: []const u8 = undefined; /// and then aborts when actual_error_union is not expected_error. pub fn expectError(expected_error: anyerror, actual_error_union: anytype) void { if (actual_error_union) |actual_payload| { - std.debug.panic("expected error.{}, found {}", .{ @errorName(expected_error), actual_payload }); + std.debug.panic("expected error.{s}, found {}", .{ @errorName(expected_error), actual_payload }); } else |actual_error| { if (expected_error != actual_error) { - std.debug.panic("expected error.{}, found error.{}", .{ + std.debug.panic("expected error.{s}, found error.{s}", .{ @errorName(expected_error), @errorName(actual_error), }); @@ -60,7 +60,7 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void { .Type => { if (actual != expected) { - std.debug.panic("expected type {}, found type {}", .{ @typeName(expected), @typeName(actual) }); + std.debug.panic("expected type {s}, found type {s}", .{ @typeName(expected), @typeName(actual) }); } }, @@ -258,7 +258,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const // If the child type is u8 and no weird bytes, we could print it as strings // Even for the length difference, it would be useful to see the values of the slices probably. if (expected.len != actual.len) { - std.debug.panic("slice lengths differ. expected {}, found {}", .{ expected.len, actual.len }); + std.debug.panic("slice lengths differ. expected {d}, found {d}", .{ expected.len, actual.len }); } var i: usize = 0; while (i < expected.len) : (i += 1) { @@ -360,7 +360,7 @@ pub fn expectEqualStrings(expected: []const u8, actual: []const u8) void { for (expected[0..diff_index]) |value| { if (value == '\n') diff_line_number += 1; } - print("First difference occurs on line {}:\n", .{diff_line_number}); + print("First difference occurs on line {d}:\n", .{diff_line_number}); print("expected:\n", .{}); printIndicatorLine(expected, diff_index); @@ -416,15 +416,15 @@ fn printWithVisibleNewlines(source: []const u8) void { while (std.mem.indexOf(u8, source[i..], "\n")) |nl| : (i += nl + 1) { printLine(source[i .. i + nl]); } - print("{}␃\n", .{source[i..]}); // End of Text symbol (ETX) + print("{s}␃\n", .{source[i..]}); // End of Text symbol (ETX) } fn printLine(line: []const u8) void { if (line.len != 0) switch (line[line.len - 1]) { - ' ', '\t' => print("{}⏎\n", .{line}), // Carriage return symbol, + ' ', '\t' => print("{s}⏎\n", .{line}), // Carriage return symbol, else => {}, }; - print("{}\n", .{line}); + print("{s}\n", .{line}); } test "" { diff --git a/lib/std/thread.zig b/lib/std/thread.zig index 7d63b5f6441b..0fee13b05768 100644 --- a/lib/std/thread.zig +++ b/lib/std/thread.zig @@ -186,7 +186,7 @@ pub const Thread = struct { @compileError(bad_startfn_ret); } startFn(arg) catch |err| { - std.debug.warn("error: {}\n", .{@errorName(err)}); + std.debug.warn("error: {s}\n", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } @@ -247,7 +247,7 @@ pub const Thread = struct { @compileError(bad_startfn_ret); } startFn(arg) catch |err| { - std.debug.warn("error: {}\n", .{@errorName(err)}); + std.debug.warn("error: {s}\n", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } @@ -281,7 +281,7 @@ pub const Thread = struct { @compileError(bad_startfn_ret); } startFn(arg) catch |err| { - std.debug.warn("error: {}\n", .{@errorName(err)}); + std.debug.warn("error: {s}\n", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index c0e72ea82221..eb894fa1f640 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -281,41 +281,41 @@ pub const Error = union(enum) { } } - pub const InvalidToken = SingleTokenError("Invalid token '{}'"); - pub const ExpectedContainerMembers = SingleTokenError("Expected test, comptime, var decl, or container field, found '{}'"); - pub const ExpectedStringLiteral = SingleTokenError("Expected string literal, found '{}'"); - pub const ExpectedIntegerLiteral = SingleTokenError("Expected integer literal, found '{}'"); - pub const ExpectedIdentifier = SingleTokenError("Expected identifier, found '{}'"); - pub const ExpectedStatement = SingleTokenError("Expected statement, found '{}'"); - pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found '{}'"); - pub const ExpectedVarDecl = SingleTokenError("Expected variable declaration, found '{}'"); - pub const ExpectedFn = SingleTokenError("Expected function, found '{}'"); - pub const ExpectedReturnType = SingleTokenError("Expected 'var' or return type expression, found '{}'"); - pub const ExpectedAggregateKw = SingleTokenError("Expected '" ++ Token.Id.Keyword_struct.symbol() ++ "', '" ++ Token.Id.Keyword_union.symbol() ++ "', '" ++ Token.Id.Keyword_enum.symbol() ++ "', or '" ++ Token.Id.Keyword_opaque.symbol() ++ "', found '{}'"); - pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found '{}'"); - pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found '{}'"); - pub const ExpectedSemiOrElse = SingleTokenError("Expected ';' or 'else', found '{}'"); - pub const ExpectedLBrace = SingleTokenError("Expected '{{', found '{}'"); - pub const ExpectedLabelOrLBrace = SingleTokenError("Expected label or '{{', found '{}'"); - pub const ExpectedColonOrRParen = SingleTokenError("Expected ':' or ')', found '{}'"); - pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found '{}'"); - pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found '{}'"); - pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or '" ++ Token.Id.Identifier.symbol() ++ "', found '{}'"); - pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found '{}'"); - pub const ExpectedTypeExpr = SingleTokenError("Expected type expression, found '{}'"); - pub const ExpectedPrimaryTypeExpr = SingleTokenError("Expected primary type expression, found '{}'"); - pub const ExpectedExpr = SingleTokenError("Expected expression, found '{}'"); - pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found '{}'"); - pub const ExpectedParamList = SingleTokenError("Expected parameter list, found '{}'"); - pub const ExpectedPayload = SingleTokenError("Expected loop payload, found '{}'"); - pub const ExpectedBlockOrAssignment = SingleTokenError("Expected block or assignment, found '{}'"); - pub const ExpectedBlockOrExpression = SingleTokenError("Expected block or expression, found '{}'"); - pub const ExpectedExprOrAssignment = SingleTokenError("Expected expression or assignment, found '{}'"); - pub const ExpectedPrefixExpr = SingleTokenError("Expected prefix expression, found '{}'"); - pub const ExpectedLoopExpr = SingleTokenError("Expected loop expression, found '{}'"); - pub const ExpectedDerefOrUnwrap = SingleTokenError("Expected pointer dereference or optional unwrap, found '{}'"); - pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{}'"); - pub const ExpectedBlockOrField = SingleTokenError("Expected block or field, found '{}'"); + pub const InvalidToken = SingleTokenError("Invalid token '{s}'"); + pub const ExpectedContainerMembers = SingleTokenError("Expected test, comptime, var decl, or container field, found '{s}'"); + pub const ExpectedStringLiteral = SingleTokenError("Expected string literal, found '{s}'"); + pub const ExpectedIntegerLiteral = SingleTokenError("Expected integer literal, found '{s}'"); + pub const ExpectedIdentifier = SingleTokenError("Expected identifier, found '{s}'"); + pub const ExpectedStatement = SingleTokenError("Expected statement, found '{s}'"); + pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found '{s}'"); + pub const ExpectedVarDecl = SingleTokenError("Expected variable declaration, found '{s}'"); + pub const ExpectedFn = SingleTokenError("Expected function, found '{s}'"); + pub const ExpectedReturnType = SingleTokenError("Expected 'var' or return type expression, found '{s}'"); + pub const ExpectedAggregateKw = SingleTokenError("Expected '" ++ Token.Id.Keyword_struct.symbol() ++ "', '" ++ Token.Id.Keyword_union.symbol() ++ "', '" ++ Token.Id.Keyword_enum.symbol() ++ "', or '" ++ Token.Id.Keyword_opaque.symbol() ++ "', found '{s}'"); + pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found '{s}'"); + pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found '{s}'"); + pub const ExpectedSemiOrElse = SingleTokenError("Expected ';' or 'else', found '{s}'"); + pub const ExpectedLBrace = SingleTokenError("Expected '{{', found '{s}'"); + pub const ExpectedLabelOrLBrace = SingleTokenError("Expected label or '{{', found '{s}'"); + pub const ExpectedColonOrRParen = SingleTokenError("Expected ':' or ')', found '{s}'"); + pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found '{s}'"); + pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found '{s}'"); + pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or '" ++ Token.Id.Identifier.symbol() ++ "', found '{s}'"); + pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found '{s}'"); + pub const ExpectedTypeExpr = SingleTokenError("Expected type expression, found '{s}'"); + pub const ExpectedPrimaryTypeExpr = SingleTokenError("Expected primary type expression, found '{s}'"); + pub const ExpectedExpr = SingleTokenError("Expected expression, found '{s}'"); + pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found '{s}'"); + pub const ExpectedParamList = SingleTokenError("Expected parameter list, found '{s}'"); + pub const ExpectedPayload = SingleTokenError("Expected loop payload, found '{s}'"); + pub const ExpectedBlockOrAssignment = SingleTokenError("Expected block or assignment, found '{s}'"); + pub const ExpectedBlockOrExpression = SingleTokenError("Expected block or expression, found '{s}'"); + pub const ExpectedExprOrAssignment = SingleTokenError("Expected expression or assignment, found '{s}'"); + pub const ExpectedPrefixExpr = SingleTokenError("Expected prefix expression, found '{s}'"); + pub const ExpectedLoopExpr = SingleTokenError("Expected loop expression, found '{s}'"); + pub const ExpectedDerefOrUnwrap = SingleTokenError("Expected pointer dereference or optional unwrap, found '{s}'"); + pub const ExpectedSuffixOp = SingleTokenError("Expected pointer dereference, optional unwrap, or field access, found '{s}'"); + pub const ExpectedBlockOrField = SingleTokenError("Expected block or field, found '{s}'"); pub const ExpectedParamType = SimpleError("Expected parameter type"); pub const ExpectedPubItem = SimpleError("Expected function or variable declaration after pub"); @@ -332,7 +332,7 @@ pub const Error = union(enum) { node: *Node, pub fn render(self: *const ExpectedCall, tokens: []const Token.Id, stream: anytype) !void { - return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ ", found {}", .{ + return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ ", found {s}", .{ @tagName(self.node.tag), }); } @@ -343,7 +343,7 @@ pub const Error = union(enum) { pub fn render(self: *const ExpectedCallOrFnProto, tokens: []const Token.Id, stream: anytype) !void { return stream.print("expected " ++ @tagName(Node.Tag.Call) ++ " or " ++ - @tagName(Node.Tag.FnProto) ++ ", found {}", .{@tagName(self.node.tag)}); + @tagName(Node.Tag.FnProto) ++ ", found {s}", .{@tagName(self.node.tag)}); } }; @@ -355,11 +355,11 @@ pub const Error = union(enum) { const found_token = tokens[self.token]; switch (found_token) { .Invalid => { - return stream.print("expected '{}', found invalid bytes", .{self.expected_id.symbol()}); + return stream.print("expected '{s}', found invalid bytes", .{self.expected_id.symbol()}); }, else => { const token_name = found_token.symbol(); - return stream.print("expected '{}', found '{}'", .{ self.expected_id.symbol(), token_name }); + return stream.print("expected '{s}', found '{s}'", .{ self.expected_id.symbol(), token_name }); }, } } @@ -371,7 +371,7 @@ pub const Error = union(enum) { pub fn render(self: *const ExpectedCommaOrEnd, tokens: []const Token.Id, stream: anytype) !void { const actual_token = tokens[self.token]; - return stream.print("expected ',' or '{}', found '{}'", .{ + return stream.print("expected ',' or '{s}', found '{s}'", .{ self.end_id.symbol(), actual_token.symbol(), }); @@ -843,7 +843,7 @@ pub const Node = struct { std.debug.warn(" ", .{}); } } - std.debug.warn("{}\n", .{@tagName(self.tag)}); + std.debug.warn("{s}\n", .{@tagName(self.tag)}); var child_i: usize = 0; while (self.iterate(child_i)) |child| : (child_i += 1) { @@ -1418,7 +1418,7 @@ pub const Node = struct { @alignOf(ParamDecl), @ptrCast([*]const u8, self) + @sizeOf(FnProto) + @sizeOf(ParamDecl) * self.params_len, ); - std.debug.print("{*} flags: {b} name_token: {} {*} params_len: {}\n", .{ + std.debug.print("{*} flags: {b} name_token: {s} {*} params_len: {d}\n", .{ self, self.trailer_flags.bits, self.getNameToken(), diff --git a/lib/std/zig/cross_target.zig b/lib/std/zig/cross_target.zig index e00f83e422ed..060dd83caf7f 100644 --- a/lib/std/zig/cross_target.zig +++ b/lib/std/zig/cross_target.zig @@ -519,7 +519,7 @@ pub const CrossTarget = struct { var result = std.ArrayList(u8).init(allocator); defer result.deinit(); - try result.outStream().print("{}-{}", .{ arch_name, os_name }); + try result.outStream().print("{s}-{s}", .{ arch_name, os_name }); // The zig target syntax does not allow specifying a max os version with no min, so // if either are present, we need the min. @@ -539,9 +539,9 @@ pub const CrossTarget = struct { } if (self.glibc_version) |v| { - try result.outStream().print("-{}.{}", .{ @tagName(self.getAbi()), v }); + try result.outStream().print("-{s}.{}", .{ @tagName(self.getAbi()), v }); } else if (self.abi) |abi| { - try result.outStream().print("-{}", .{@tagName(abi)}); + try result.outStream().print("-{s}", .{@tagName(abi)}); } return result.toOwnedSlice(); @@ -595,7 +595,7 @@ pub const CrossTarget = struct { .Dynamic => "", }; - return std.fmt.allocPrint(allocator, "{}-{}{}", .{ arch, os, static_suffix }); + return std.fmt.allocPrint(allocator, "{s}-{s}{s}", .{ arch, os, static_suffix }); } pub const Executor = union(enum) { @@ -790,7 +790,7 @@ test "CrossTarget.parse" { var buf: [256]u8 = undefined; const triple = std.fmt.bufPrint( buf[0..], - "native-native-{}.2.1.1", + "native-native-{s}.2.1.1", .{@tagName(std.Target.current.abi)}, ) catch unreachable; diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 2879f2652b18..63d04f9a80ba 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -3742,9 +3742,9 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b for (tree.errors) |*parse_error| { const token = tree.token_locs[parse_error.loc()]; const loc = tree.tokenLocation(0, parse_error.loc()); - try stderr.print("(memory buffer):{}:{}: error: ", .{ loc.line + 1, loc.column + 1 }); + try stderr.print("(memory buffer):{d}:{d}: error: ", .{ loc.line + 1, loc.column + 1 }); try tree.renderError(parse_error, stderr); - try stderr.print("\n{}\n", .{source[loc.line_start..loc.line_end]}); + try stderr.print("\n{s}\n", .{source[loc.line_start..loc.line_end]}); { var i: usize = 0; while (i < loc.column) : (i += 1) { @@ -3800,7 +3800,7 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void { error.OutOfMemory => { if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) { warn( - "\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n", + "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\n", .{ fail_index, needed_alloc_count, diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index b8b0d8c1f14c..6931b1925010 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -41,7 +41,7 @@ fn renderRoot( for (tree.token_ids) |token_id, i| { if (token_id != .LineComment) break; const token_loc = tree.token_locs[i]; - try ais.writer().print("{}\n", .{mem.trimRight(u8, tree.tokenSliceLoc(token_loc), " ")}); + try ais.writer().print("{s}\n", .{mem.trimRight(u8, tree.tokenSliceLoc(token_loc), " ")}); const next_token = tree.token_locs[i + 1]; const loc = tree.tokenLocationLoc(token_loc.end, next_token); if (loc.line >= 2) { diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 574ccfb32d80..a052c81a7daa 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -22,6 +22,7 @@ pub const getSDKPath = macos.getSDKPath; pub const NativePaths = struct { include_dirs: ArrayList([:0]u8), lib_dirs: ArrayList([:0]u8), + framework_dirs: ArrayList([:0]u8), rpaths: ArrayList([:0]u8), warnings: ArrayList([:0]u8), @@ -29,6 +30,7 @@ pub const NativePaths = struct { var self: NativePaths = .{ .include_dirs = ArrayList([:0]u8).init(allocator), .lib_dirs = ArrayList([:0]u8).init(allocator), + .framework_dirs = ArrayList([:0]u8).init(allocator), .rpaths = ArrayList([:0]u8).init(allocator), .warnings = ArrayList([:0]u8).init(allocator), }; @@ -49,7 +51,7 @@ pub const NativePaths = struct { }; try self.addIncludeDir(include_path); } else { - try self.addWarningFmt("Unrecognized C flag from NIX_CFLAGS_COMPILE: {}", .{word}); + try self.addWarningFmt("Unrecognized C flag from NIX_CFLAGS_COMPILE: {s}", .{word}); break; } } @@ -75,7 +77,7 @@ pub const NativePaths = struct { const lib_path = word[2..]; try self.addLibDir(lib_path); } else { - try self.addWarningFmt("Unrecognized C flag from NIX_LDFLAGS: {}", .{word}); + try self.addWarningFmt("Unrecognized C flag from NIX_LDFLAGS: {s}", .{word}); break; } } @@ -88,6 +90,19 @@ pub const NativePaths = struct { return self; } + if (comptime Target.current.isDarwin()) { + try self.addIncludeDir("/usr/include"); + try self.addIncludeDir("/usr/local/include"); + + try self.addLibDir("/usr/lib"); + try self.addLibDir("/usr/local/lib"); + + try self.addFrameworkDir("/Library/Frameworks"); + try self.addFrameworkDir("/System/Library/Frameworks"); + + return self; + } + if (!is_windows) { const triple = try Target.current.linuxTriple(allocator); const qual = Target.current.cpu.arch.ptrBitWidth(); @@ -98,22 +113,22 @@ pub const NativePaths = struct { // TODO: some of these are suspect and should only be added on some systems. audit needed. try self.addIncludeDir("/usr/local/include"); - try self.addLibDirFmt("/usr/local/lib{}", .{qual}); + try self.addLibDirFmt("/usr/local/lib{d}", .{qual}); try self.addLibDir("/usr/local/lib"); - try self.addIncludeDirFmt("/usr/include/{}", .{triple}); - try self.addLibDirFmt("/usr/lib/{}", .{triple}); + try self.addIncludeDirFmt("/usr/include/{s}", .{triple}); + try self.addLibDirFmt("/usr/lib/{s}", .{triple}); try self.addIncludeDir("/usr/include"); - try self.addLibDirFmt("/lib{}", .{qual}); + try self.addLibDirFmt("/lib{d}", .{qual}); try self.addLibDir("/lib"); - try self.addLibDirFmt("/usr/lib{}", .{qual}); + try self.addLibDirFmt("/usr/lib{d}", .{qual}); try self.addLibDir("/usr/lib"); // example: on a 64-bit debian-based linux distro, with zlib installed from apt: // zlib.h is in /usr/include (added above) // libz.so.1 is in /lib/x86_64-linux-gnu (added here) - try self.addLibDirFmt("/lib/{}", .{triple}); + try self.addLibDirFmt("/lib/{s}", .{triple}); } return self; @@ -122,6 +137,7 @@ pub const NativePaths = struct { pub fn deinit(self: *NativePaths) void { deinitArray(&self.include_dirs); deinitArray(&self.lib_dirs); + deinitArray(&self.framework_dirs); deinitArray(&self.rpaths); deinitArray(&self.warnings); self.* = undefined; @@ -158,6 +174,16 @@ pub const NativePaths = struct { return self.appendArray(&self.warnings, s); } + pub fn addFrameworkDir(self: *NativePaths, s: []const u8) !void { + return self.appendArray(&self.framework_dirs, s); + } + + pub fn addFrameworkDirFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void { + const item = try std.fmt.allocPrint0(self.framework_dirs.allocator, fmt, args); + errdefer self.framework_dirs.allocator.free(item); + try self.framework_dirs.append(item); + } + pub fn addWarningFmt(self: *NativePaths, comptime fmt: []const u8, args: anytype) !void { const item = try std.fmt.allocPrint0(self.warnings.allocator, fmt, args); errdefer self.warnings.allocator.free(item); @@ -237,8 +263,7 @@ pub const NativeTargetInfo = struct { // `---` `` ``--> Sub-version (Starting from Windows 10 onwards) // \ `--> Service pack (Always zero in the constants defined) // `--> OS version (Major & minor) - const os_ver: u16 = - @intCast(u16, version_info.dwMajorVersion & 0xff) << 8 | + const os_ver: u16 = @intCast(u16, version_info.dwMajorVersion & 0xff) << 8 | @intCast(u16, version_info.dwMinorVersion & 0xff); const sp_ver: u8 = 0; const sub_ver: u8 = if (os_ver >= 0x0A00) subver: { diff --git a/lib/std/zig/system/macos.zig b/lib/std/zig/system/macos.zig index fbc0b5149bdf..7c4e255cc592 100644 --- a/lib/std/zig/system/macos.zig +++ b/lib/std/zig/system/macos.zig @@ -450,7 +450,7 @@ test "version_from_build" { for (known) |pair| { var buf: [32]u8 = undefined; const ver = try version_from_build(pair[0]); - const sver = try std.fmt.bufPrint(buf[0..], "{}.{}.{}", .{ ver.major, ver.minor, ver.patch }); + const sver = try std.fmt.bufPrint(buf[0..], "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch }); std.testing.expect(std.mem.eql(u8, sver, pair[1])); } } @@ -468,7 +468,7 @@ pub fn getSDKPath(allocator: *mem.Allocator) ![]u8 { allocator.free(result.stdout); } if (result.stderr.len != 0) { - std.log.err("unexpected 'xcrun --show-sdk-path' stderr: {}", .{result.stderr}); + std.log.err("unexpected 'xcrun --show-sdk-path' stderr: {s}", .{result.stderr}); } if (result.term.Exited != 0) { return error.ProcessTerminated; diff --git a/lib/std/zig/tokenizer.zig b/lib/std/zig/tokenizer.zig index 4e50b558160b..dcbf71763822 100644 --- a/lib/std/zig/tokenizer.zig +++ b/lib/std/zig/tokenizer.zig @@ -334,7 +334,7 @@ pub const Tokenizer = struct { /// For debugging purposes pub fn dump(self: *Tokenizer, token: *const Token) void { - std.debug.warn("{} \"{}\"\n", .{ @tagName(token.id), self.buffer[token.start..token.end] }); + std.debug.warn("{s} \"{s}\"\n", .{ @tagName(token.id), self.buffer[token.start..token.end] }); } pub fn init(buffer: []const u8) Tokenizer { @@ -2046,7 +2046,7 @@ fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void { for (expected_tokens) |expected_token_id| { const token = tokenizer.next(); if (token.id != expected_token_id) { - std.debug.panic("expected {}, found {}\n", .{ @tagName(expected_token_id), @tagName(token.id) }); + std.debug.panic("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.id) }); } } const last_token = tokenizer.next(); diff --git a/src/Cache.zig b/src/Cache.zig index 3d33226f27b3..9f8beaabc72d 100644 --- a/src/Cache.zig +++ b/src/Cache.zig @@ -12,14 +12,6 @@ const mem = std.mem; const fmt = std.fmt; const Allocator = std.mem.Allocator; -/// Process-scoped map keeping track of all locked Cache hashes, to detect deadlocks. -/// This protection is conditionally compiled depending on `want_debug_deadlock`. -var all_cache_digest_set: std.AutoHashMapUnmanaged(BinDigest, void) = .{}; -var all_cache_digest_lock: std.Mutex = .{}; -const want_debug_deadlock = std.debug.runtime_safety; -const DebugBinDigest = if (want_debug_deadlock) BinDigest else void; -const null_debug_bin_digest = if (want_debug_deadlock) ([1]u8{0} ** bin_digest_len) else {}; - /// Be sure to call `Manifest.deinit` after successful initialization. pub fn obtain(cache: *const Cache) Manifest { return Manifest{ @@ -168,15 +160,8 @@ pub const HashHelper = struct { pub const Lock = struct { manifest_file: fs.File, - debug_bin_digest: DebugBinDigest, pub fn release(lock: *Lock) void { - if (want_debug_deadlock) { - const held = all_cache_digest_lock.acquire(); - defer held.release(); - - all_cache_digest_set.removeAssertDiscard(lock.debug_bin_digest); - } lock.manifest_file.close(); lock.* = undefined; } @@ -193,7 +178,6 @@ pub const Manifest = struct { manifest_dirty: bool, files: std.ArrayListUnmanaged(File) = .{}, hex_digest: [hex_digest_len]u8, - debug_bin_digest: DebugBinDigest = null_debug_bin_digest, /// Populated when hit() returns an error because of one /// of the files listed in the manifest. failed_file_index: ?usize = null, @@ -266,23 +250,6 @@ pub const Manifest = struct { var bin_digest: BinDigest = undefined; self.hash.hasher.final(&bin_digest); - if (want_debug_deadlock) { - self.debug_bin_digest = bin_digest; - - const held = all_cache_digest_lock.acquire(); - defer held.release(); - - const gop = try all_cache_digest_set.getOrPut(self.cache.gpa, bin_digest); - if (gop.found_existing) { - std.debug.print("Cache deadlock detected in Cache.hit. Manifest has {d} files:\n", .{self.files.items.len}); - for (self.files.items) |file| { - const p: []const u8 = file.path orelse "(null)"; - std.debug.print(" file: {s}\n", .{p}); - } - @panic("Cache deadlock detected"); - } - } - _ = std.fmt.bufPrint(&self.hex_digest, "{x}", .{bin_digest}) catch unreachable; self.hash.hasher = hasher_init; @@ -549,7 +516,7 @@ pub const Manifest = struct { .target, .target_must_resolve, .prereq => {}, else => |err| { try err.printError(error_buf.writer()); - std.log.err("failed parsing {}: {}", .{ dep_file_basename, error_buf.items }); + std.log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items }); return error.InvalidDepFile; }, } @@ -561,7 +528,7 @@ pub const Manifest = struct { .prereq => |bytes| try self.addFilePost(bytes), else => |err| { try err.printError(error_buf.writer()); - std.log.err("failed parsing {}: {}", .{ dep_file_basename, error_buf.items }); + std.log.err("failed parsing {s}: {s}", .{ dep_file_basename, error_buf.items }); return error.InvalidDepFile; }, } @@ -619,10 +586,8 @@ pub const Manifest = struct { pub fn toOwnedLock(self: *Manifest) Lock { const lock: Lock = .{ .manifest_file = self.manifest_file.?, - .debug_bin_digest = self.debug_bin_digest, }; self.manifest_file = null; - self.debug_bin_digest = null_debug_bin_digest; return lock; } @@ -630,14 +595,6 @@ pub const Manifest = struct { /// `Manifest.hit` must be called first. /// Don't forget to call `writeManifest` before this! pub fn deinit(self: *Manifest) void { - if (want_debug_deadlock) { - if (!mem.eql(u8, &self.debug_bin_digest, &null_debug_bin_digest)) { - const held = all_cache_digest_lock.acquire(); - defer held.release(); - - all_cache_digest_set.removeAssertDiscard(self.debug_bin_digest); - } - } if (self.manifest_file) |file| { file.close(); } @@ -721,10 +678,6 @@ test "cache file and then recall it" { // https://github.com/ziglang/zig/issues/5437 return error.SkipZigTest; } - defer if (want_debug_deadlock) { - testing.expect(all_cache_digest_set.count() == 0); - all_cache_digest_set.clearAndFree(testing.allocator); - }; const cwd = fs.cwd(); @@ -803,10 +756,6 @@ test "check that changing a file makes cache fail" { // https://github.com/ziglang/zig/issues/5437 return error.SkipZigTest; } - defer if (want_debug_deadlock) { - testing.expect(all_cache_digest_set.count() == 0); - all_cache_digest_set.clearAndFree(testing.allocator); - }; const cwd = fs.cwd(); const temp_file = "cache_hash_change_file_test.txt"; @@ -883,10 +832,6 @@ test "no file inputs" { // https://github.com/ziglang/zig/issues/5437 return error.SkipZigTest; } - defer if (want_debug_deadlock) { - testing.expect(all_cache_digest_set.count() == 0); - all_cache_digest_set.clearAndFree(testing.allocator); - }; const cwd = fs.cwd(); const temp_manifest_dir = "no_file_inputs_manifest_dir"; defer cwd.deleteTree(temp_manifest_dir) catch {}; @@ -932,10 +877,6 @@ test "Manifest with files added after initial hash work" { // https://github.com/ziglang/zig/issues/5437 return error.SkipZigTest; } - defer if (want_debug_deadlock) { - testing.expect(all_cache_digest_set.count() == 0); - all_cache_digest_set.clearAndFree(testing.allocator); - }; const cwd = fs.cwd(); const temp_file1 = "cache_hash_post_file_test1.txt"; diff --git a/src/Compilation.zig b/src/Compilation.zig index cd3db84ec286..991252043743 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1173,6 +1173,7 @@ pub fn destroy(self: *Compilation) void { const gpa = self.gpa; self.work_queue.deinit(); + self.c_object_work_queue.deinit(); { var it = self.crt_files.iterator(); @@ -1202,6 +1203,10 @@ pub fn destroy(self: *Compilation) void { crt_file.deinit(gpa); } + if (self.glibc_so_files) |*glibc_file| { + glibc_file.deinit(gpa); + } + for (self.c_object_table.items()) |entry| { entry.key.destroy(gpa); } @@ -1456,27 +1461,34 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor => continue, .complete, .codegen_failure_retryable => { + if (build_options.omit_stage2) + @panic("sadly stage2 is omitted from this build to save memory on the CI server"); const module = self.bin_file.options.module.?; if (decl.typed_value.most_recent.typed_value.val.castTag(.function)) |payload| { const func = payload.data; - switch (func.analysis) { + switch (func.state) { .queued => module.analyzeFnBody(decl, func) catch |err| switch (err) { error.AnalysisFail => { - assert(func.analysis != .in_progress); + assert(func.state != .in_progress); continue; }, error.OutOfMemory => return error.OutOfMemory, }, .in_progress => unreachable, + .inline_only => unreachable, // don't queue work for this .sema_failure, .dependency_failure => continue, .success => {}, } - // Here we tack on additional allocations to the Decl's arena. The allocations are - // lifetime annotations in the ZIR. + // Here we tack on additional allocations to the Decl's arena. The allocations + // are lifetime annotations in the ZIR. var decl_arena = decl.typed_value.most_recent.arena.?.promote(module.gpa); defer decl.typed_value.most_recent.arena.?.* = decl_arena.state; - log.debug("analyze liveness of {}\n", .{decl.name}); - try liveness.analyze(module.gpa, &decl_arena.allocator, func.analysis.success); + log.debug("analyze liveness of {s}\n", .{decl.name}); + try liveness.analyze(module.gpa, &decl_arena.allocator, func.body); + + if (std.builtin.mode == .Debug and self.verbose_ir) { + func.dump(module.*); + } } assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); @@ -1492,7 +1504,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( module.gpa, decl.src(), - "unable to codegen: {}", + "unable to codegen: {s}", .{@errorName(err)}, )); decl.analysis = .codegen_failure_retryable; @@ -1512,7 +1524,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( module.gpa, decl.src(), - "unable to generate C header: {}", + "unable to generate C header: {s}", .{@errorName(err)}, )); decl.analysis = .codegen_failure_retryable; @@ -1522,6 +1534,8 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }, }, .analyze_decl => |decl| { + if (build_options.omit_stage2) + @panic("sadly stage2 is omitted from this build to save memory on the CI server"); const module = self.bin_file.options.module.?; module.ensureDeclAnalyzed(decl) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, @@ -1529,13 +1543,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor }; }, .update_line_number => |decl| { + if (build_options.omit_stage2) + @panic("sadly stage2 is omitted from this build to save memory on the CI server"); const module = self.bin_file.options.module.?; self.bin_file.updateDeclLineNumber(module, decl) catch |err| { try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( module.gpa, decl.src(), - "unable to update line number: {}", + "unable to update line number: {s}", .{@errorName(err)}, )); decl.analysis = .codegen_failure_retryable; @@ -1544,56 +1560,56 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor .glibc_crt_file => |crt_file| { glibc.buildCRTFile(self, crt_file) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build glibc CRT file: {}", .{@errorName(err)}); + fatal("unable to build glibc CRT file: {s}", .{@errorName(err)}); }; }, .glibc_shared_objects => { glibc.buildSharedObjects(self) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build glibc shared objects: {}", .{@errorName(err)}); + fatal("unable to build glibc shared objects: {s}", .{@errorName(err)}); }; }, .musl_crt_file => |crt_file| { musl.buildCRTFile(self, crt_file) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build musl CRT file: {}", .{@errorName(err)}); + fatal("unable to build musl CRT file: {s}", .{@errorName(err)}); }; }, .mingw_crt_file => |crt_file| { mingw.buildCRTFile(self, crt_file) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build mingw-w64 CRT file: {}", .{@errorName(err)}); + fatal("unable to build mingw-w64 CRT file: {s}", .{@errorName(err)}); }; }, .windows_import_lib => |index| { const link_lib = self.bin_file.options.system_libs.items()[index].key; mingw.buildImportLib(self, link_lib) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to generate DLL import .lib file: {}", .{@errorName(err)}); + fatal("unable to generate DLL import .lib file: {s}", .{@errorName(err)}); }; }, .libunwind => { libunwind.buildStaticLib(self) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build libunwind: {}", .{@errorName(err)}); + fatal("unable to build libunwind: {s}", .{@errorName(err)}); }; }, .libcxx => { libcxx.buildLibCXX(self) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build libcxx: {}", .{@errorName(err)}); + fatal("unable to build libcxx: {s}", .{@errorName(err)}); }; }, .libcxxabi => { libcxx.buildLibCXXABI(self) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build libcxxabi: {}", .{@errorName(err)}); + fatal("unable to build libcxxabi: {s}", .{@errorName(err)}); }; }, .libtsan => { libtsan.buildTsan(self) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build TSAN library: {}", .{@errorName(err)}); + fatal("unable to build TSAN library: {s}", .{@errorName(err)}); }; }, .compiler_rt_lib => { @@ -1611,20 +1627,20 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor .libssp => { self.buildOutputFromZig("ssp.zig", .Lib, &self.libssp_static_lib) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build libssp: {}", .{@errorName(err)}); + fatal("unable to build libssp: {s}", .{@errorName(err)}); }; }, .zig_libc => { self.buildOutputFromZig("c.zig", .Lib, &self.libc_static_lib) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to build zig's multitarget libc: {}", .{@errorName(err)}); + fatal("unable to build zig's multitarget libc: {s}", .{@errorName(err)}); }; }, .generate_builtin_zig => { // This Job is only queued up if there is a zig module. self.updateBuiltinZigFile(self.bin_file.options.module.?) catch |err| { // TODO Expose this as a normal compile error rather than crashing here. - fatal("unable to update builtin.zig file: {}", .{@errorName(err)}); + fatal("unable to update builtin.zig file: {s}", .{@errorName(err)}); }; }, .stage1_module => { @@ -1704,11 +1720,11 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { const out_h_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ tmp_dir_sub_path, cimport_basename, }); - const out_dep_path = try std.fmt.allocPrint(arena, "{}.d", .{out_h_path}); + const out_dep_path = try std.fmt.allocPrint(arena, "{s}.d", .{out_h_path}); try zig_cache_tmp_dir.writeFile(cimport_basename, c_src); if (comp.verbose_cimport) { - log.info("C import source: {}", .{out_h_path}); + log.info("C import source: {s}", .{out_h_path}); } var argv = std.ArrayList([]const u8).init(comp.gpa); @@ -1755,7 +1771,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { defer tree.deinit(); if (comp.verbose_cimport) { - log.info("C import .d file: {}", .{out_dep_path}); + log.info("C import .d file: {s}", .{out_dep_path}); } const dep_basename = std.fs.path.basename(out_dep_path); @@ -1775,7 +1791,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { try bos.flush(); man.writeManifest() catch |err| { - log.warn("failed to write cache manifest for C import: {}", .{@errorName(err)}); + log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)}); }; break :digest digest; @@ -1785,7 +1801,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { "o", &digest, cimport_zig_basename, }); if (comp.verbose_cimport) { - log.info("C import output: {}\n", .{out_zig_path}); + log.info("C import output: {s}\n", .{out_zig_path}); } return CImportResult{ .out_zig_path = out_zig_path, @@ -1946,7 +1962,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * child.stderr_behavior = .Inherit; const term = child.spawnAndWait() catch |err| { - return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) }); + return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); }; switch (term) { .Exited => |code| { @@ -1974,7 +1990,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); const term = child.wait() catch |err| { - return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) }); + return comp.failCObj(c_object, "unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) }); }; switch (term) { @@ -1982,12 +1998,12 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * if (code != 0) { // TODO parse clang stderr and turn it into an error message // and then call failCObjWithOwnedErrorMsg - log.err("clang failed with stderr: {}", .{stderr}); - return comp.failCObj(c_object, "clang exited with code {}", .{code}); + log.err("clang failed with stderr: {s}", .{stderr}); + return comp.failCObj(c_object, "clang exited with code {d}", .{code}); } }, else => { - log.err("clang terminated with stderr: {}", .{stderr}); + log.err("clang terminated with stderr: {s}", .{stderr}); return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); }, } @@ -1999,7 +2015,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); // Just to save disk space, we delete the file because it is never needed again. zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { - log.warn("failed to delete '{}': {}", .{ dep_file_path, @errorName(err) }); + log.warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }); }; } @@ -2015,7 +2031,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: * try std.fs.rename(zig_cache_tmp_dir, tmp_basename, o_dir, o_basename); man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when compiling '{}': {}", .{ c_object.src.src_path, @errorName(err) }); + log.warn("failed to write cache manifest when compiling '{s}': {s}", .{ c_object.src.src_path, @errorName(err) }); }; break :blk digest; }; @@ -2034,7 +2050,7 @@ pub fn tmpFilePath(comp: *Compilation, arena: *Allocator, suffix: []const u8) er const s = std.fs.path.sep_str; const rand_int = std.crypto.random.int(u64); if (comp.local_cache_directory.path) |p| { - return std.fmt.allocPrint(arena, "{}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix }); + return std.fmt.allocPrint(arena, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix }); } else { return std.fmt.allocPrint(arena, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix }); } @@ -2079,12 +2095,6 @@ pub fn addCCArgs( try argv.append("-ffunction-sections"); } - try argv.ensureCapacity(argv.items.len + comp.bin_file.options.framework_dirs.len * 2); - for (comp.bin_file.options.framework_dirs) |framework_dir| { - argv.appendAssumeCapacity("-iframework"); - argv.appendAssumeCapacity(framework_dir); - } - if (comp.bin_file.options.link_libcpp) { const libcxx_include_path = try std.fs.path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, "libcxx", "include", @@ -2150,7 +2160,7 @@ pub fn addCCArgs( } const mcmodel = comp.bin_file.options.machine_code_model; if (mcmodel != .default) { - try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={}", .{@tagName(mcmodel)})); + try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={s}", .{@tagName(mcmodel)})); } switch (target.os.tag) { @@ -2503,22 +2513,22 @@ fn detectLibCIncludeDirs( const s = std.fs.path.sep_str; const arch_include_dir = try std.fmt.allocPrint( arena, - "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}", + "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}", .{ zig_lib_dir, arch_name, os_name, abi_name }, ); const generic_include_dir = try std.fmt.allocPrint( arena, - "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{}", + "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}", .{ zig_lib_dir, generic_name }, ); const arch_os_include_dir = try std.fmt.allocPrint( arena, - "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-any", + "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any", .{ zig_lib_dir, @tagName(target.cpu.arch), os_name }, ); const generic_os_include_dir = try std.fmt.allocPrint( arena, - "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{}-any", + "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any", .{ zig_lib_dir, os_name }, ); @@ -2637,9 +2647,9 @@ fn updateBuiltinZigFile(comp: *Compilation, mod: *Module) !void { pub fn dump_argv(argv: []const []const u8) void { for (argv[0 .. argv.len - 1]) |arg| { - std.debug.print("{} ", .{arg}); + std.debug.print("{s} ", .{arg}); } - std.debug.print("{}\n", .{argv[argv.len - 1]}); + std.debug.print("{s}\n", .{argv[argv.len - 1]}); } pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 { @@ -2659,15 +2669,15 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 \\pub const arch = Target.current.cpu.arch; \\/// Deprecated \\pub const endian = Target.current.cpu.arch.endian(); - \\pub const output_mode = OutputMode.{}; - \\pub const link_mode = LinkMode.{}; + \\pub const output_mode = OutputMode.{z}; + \\pub const link_mode = LinkMode.{z}; \\pub const is_test = {}; \\pub const single_threaded = {}; - \\pub const abi = Abi.{}; + \\pub const abi = Abi.{z}; \\pub const cpu: Cpu = Cpu{{ - \\ .arch = .{}, - \\ .model = &Target.{}.cpu.{}, - \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{ + \\ .arch = .{z}, + \\ .model = &Target.{z}.cpu.{z}, + \\ .features = Target.{z}.featureSet(&[_]Target.{z}.Feature{{ \\ , .{ @tagName(comp.bin_file.options.output_mode), @@ -2698,7 +2708,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 \\ }}), \\}}; \\pub const os = Os{{ - \\ .tag = .{}, + \\ .tag = .{z}, \\ .version_range = .{{ , .{@tagName(target.os.tag)}, @@ -2784,8 +2794,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 (comp.bin_file.options.skip_linker_dependencies and comp.bin_file.options.parent_compilation_link_libc); try buffer.writer().print( - \\pub const object_format = ObjectFormat.{}; - \\pub const mode = Mode.{}; + \\pub const object_format = ObjectFormat.{z}; + \\pub const mode = Mode.{z}; \\pub const link_libc = {}; \\pub const link_libcpp = {}; \\pub const have_error_return_tracing = {}; @@ -2793,7 +2803,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 \\pub const position_independent_code = {}; \\pub const position_independent_executable = {}; \\pub const strip_debug_info = {}; - \\pub const code_model = CodeModel.{}; + \\pub const code_model = CodeModel.{z}; \\ , .{ @tagName(comp.bin_file.options.object_format), @@ -3019,7 +3029,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node id_symlink_basename, &prev_digest_buf, ) catch |err| blk: { - log.debug("stage1 {} new_digest={} error: {}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) }); + log.debug("stage1 {s} new_digest={} error: {s}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) }); // Handle this as a cache miss. break :blk prev_digest_buf[0..0]; }; @@ -3027,7 +3037,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node if (!mem.eql(u8, prev_digest[0..digest.len], &digest)) break :hit; - log.debug("stage1 {} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest }); + log.debug("stage1 {s} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest }); var flags_bytes: [1]u8 = undefined; _ = std.fmt.hexToBytes(&flags_bytes, prev_digest[digest.len..]) catch { log.warn("bad cache stage1 digest: '{s}'", .{prev_digest}); @@ -3050,7 +3060,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]); return; } - log.debug("stage1 {} prev_digest={} new_digest={}", .{ mod.root_pkg.root_src_path, prev_digest, digest }); + log.debug("stage1 {s} prev_digest={} new_digest={}", .{ mod.root_pkg.root_src_path, prev_digest, digest }); man.unhit(prev_hash_state, input_file_count); } @@ -3195,7 +3205,7 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node // Update the small file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. const stage1_flags_byte = @bitCast(u8, mod.stage1_flags); - log.debug("stage1 {} final digest={} flags={x}", .{ + log.debug("stage1 {s} final digest={} flags={x}", .{ mod.root_pkg.root_src_path, digest, stage1_flags_byte, }); var digest_plus_flags: [digest.len + 2]u8 = undefined; @@ -3208,11 +3218,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node digest_plus_flags, stage1_flags_byte, mod.stage1_flags.have_winmain_crt_startup, }); Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest_plus_flags) catch |err| { - log.warn("failed to save stage1 hash digest file: {}", .{@errorName(err)}); + log.warn("failed to save stage1 hash digest file: {s}", .{@errorName(err)}); }; // Failure here only means an unnecessary cache miss. man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); }; // We hang on to this lock so that the output file path can be used without // other processes clobbering it. diff --git a/src/DepTokenizer.zig b/src/DepTokenizer.zig index 99db6e4b3ca6..0fe1310cd8a4 100644 --- a/src/DepTokenizer.zig +++ b/src/DepTokenizer.zig @@ -366,14 +366,14 @@ pub const Token = union(enum) { .incomplete_quoted_prerequisite, .incomplete_target, => |index_and_bytes| { - try writer.print("{} '", .{self.errStr()}); + try writer.print("{s} '", .{self.errStr()}); if (self == .incomplete_target) { const tmp = Token{ .target_must_resolve = index_and_bytes.bytes }; try tmp.resolve(writer); } else { try printCharValues(writer, index_and_bytes.bytes); } - try writer.print("' at position {}", .{index_and_bytes.index}); + try writer.print("' at position {d}", .{index_and_bytes.index}); }, .invalid_target, .bad_target_escape, @@ -383,7 +383,7 @@ pub const Token = union(enum) { => |index_and_char| { try writer.writeAll("illegal char "); try printUnderstandableChar(writer, index_and_char.char); - try writer.print(" at position {}: {}", .{ index_and_char.index, self.errStr() }); + try writer.print(" at position {d}: {s}", .{ index_and_char.index, self.errStr() }); }, } } @@ -927,7 +927,7 @@ fn depTokenizer(input: []const u8, expect: []const u8) !void { try out.writeAll("\n"); try printSection(out, "<<<< input", input); try printSection(out, "==== expect", expect); - try printSection(out, ">>>> got", got); + try printSection(out, ">>>> got", buffer.items); try printRuler(out); testing.expect(false); @@ -943,7 +943,7 @@ fn printSection(out: anytype, label: []const u8, bytes: []const u8) !void { fn printLabel(out: anytype, label: []const u8, bytes: []const u8) !void { var buf: [80]u8 = undefined; - var text = try std.fmt.bufPrint(buf[0..], "{} {} bytes ", .{ label, bytes.len }); + var text = try std.fmt.bufPrint(buf[0..], "{s} {d} bytes ", .{ label, bytes.len }); try out.writeAll(text); var i: usize = text.len; const end = 79; diff --git a/src/Module.zig b/src/Module.zig index ca5d6dbe7acc..fdb782ee1a1b 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -23,6 +23,8 @@ const trace = @import("tracy.zig").trace; const astgen = @import("astgen.zig"); const zir_sema = @import("zir_sema.zig"); +const default_eval_branch_quota = 1000; + /// General-purpose allocator. Used for both temporary and long-term storage. gpa: *Allocator, comp: *Compilation, @@ -248,7 +250,7 @@ pub const Decl = struct { pub fn dump(self: *Decl) void { const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src); - std.debug.print("{}:{}:{} name={} status={}", .{ + std.debug.print("{s}:{d}:{d} name={s} status={s}", .{ self.scope.sub_file_path, loc.line + 1, loc.column + 1, @@ -268,6 +270,11 @@ pub const Decl = struct { } } + /// Asserts that the `Decl` is part of AST and not ZIRModule. + pub fn getFileScope(self: *Decl) *Scope.File { + return self.scope.cast(Scope.Container).?.file_scope; + } + fn removeDependant(self: *Decl, other: *Decl) void { self.dependants.removeAssertDiscard(other); } @@ -281,46 +288,32 @@ pub const Decl = struct { /// Extern functions do not have this data structure; they are represented by /// the `Decl` only, with a `Value` tag of `extern_fn`. pub const Fn = struct { - /// This memory owned by the Decl's TypedValue.Managed arena allocator. - analysis: union(enum) { - queued: *ZIR, + owner_decl: *Decl, + /// Contains un-analyzed ZIR instructions generated from Zig source AST. + /// Even after we finish analysis, the ZIR is kept in memory, so that + /// comptime and inline function calls can happen. + zir: zir.Module.Body, + /// undefined unless analysis state is `success`. + body: Body, + state: Analysis, + + pub const Analysis = enum { + queued, + /// This function intentionally only has ZIR generated because it is marked + /// inline, which means no runtime version of the function will be generated. + inline_only, in_progress, /// There will be a corresponding ErrorMsg in Module.failed_decls sema_failure, - /// This Fn might be OK but it depends on another Decl which did not successfully complete - /// semantic analysis. + /// This Fn might be OK but it depends on another Decl which did not + /// successfully complete semantic analysis. dependency_failure, - success: Body, - }, - owner_decl: *Decl, - - /// This memory is temporary and points to stack memory for the duration - /// of Fn analysis. - pub const Analysis = struct { - inner_block: Scope.Block, - }; - - /// Contains un-analyzed ZIR instructions generated from Zig source AST. - pub const ZIR = struct { - body: zir.Module.Body, - arena: std.heap.ArenaAllocator.State, + success, }; /// For debugging purposes. pub fn dump(self: *Fn, mod: Module) void { - std.debug.print("Module.Function(name={}) ", .{self.owner_decl.name}); - switch (self.analysis) { - .queued => { - std.debug.print("queued\n", .{}); - }, - .in_progress => { - std.debug.print("in_progress\n", .{}); - }, - else => { - std.debug.print("\n", .{}); - zir.dumpFn(mod, self); - }, - } + zir.dumpFn(mod, self); } }; @@ -632,7 +625,7 @@ pub const Scope = struct { pub fn dumpSrc(self: *File, src: usize) void { const loc = std.zig.findLineColumn(self.source.bytes, src); - std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); + std.debug.print("{s}:{d}:{d}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); } pub fn getSource(self: *File, module: *Module) ![:0]const u8 { @@ -730,7 +723,7 @@ pub const Scope = struct { pub fn dumpSrc(self: *ZIRModule, src: usize) void { const loc = std.zig.findLineColumn(self.source.bytes, src); - std.debug.print("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); + std.debug.print("{s}:{d}:{d}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); } pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 { @@ -761,21 +754,61 @@ pub const Scope = struct { /// during semantic analysis of the block. pub const Block = struct { pub const base_tag: Tag = .block; + base: Scope = Scope{ .tag = base_tag }, parent: ?*Block, + /// Maps ZIR to TZIR. Shared to sub-blocks. + inst_table: *InstTable, func: ?*Fn, decl: *Decl, instructions: ArrayListUnmanaged(*Inst), /// Points to the arena allocator of DeclAnalysis arena: *Allocator, label: ?Label = null, + inlining: ?*Inlining, is_comptime: bool, + /// Shared to sub-blocks. + branch_quota: *u32, + pub const InstTable = std.AutoHashMap(*zir.Inst, *Inst); + + /// This `Block` maps a block ZIR instruction to the corresponding + /// TZIR instruction for break instruction analysis. pub const Label = struct { zir_block: *zir.Inst.Block, + merges: Merges, + }; + + /// This `Block` indicates that an inline function call is happening + /// and return instructions should be analyzed as a break instruction + /// to this TZIR block instruction. + /// It is shared among all the blocks in an inline or comptime called + /// function. + pub const Inlining = struct { + /// Shared state among the entire inline/comptime call stack. + shared: *Shared, + /// We use this to count from 0 so that arg instructions know + /// which parameter index they are, without having to store + /// a parameter index with each arg instruction. + param_index: usize, + casted_args: []*Inst, + merges: Merges, + + pub const Shared = struct { + caller: ?*Fn, + branch_count: u32, + }; + }; + + pub const Merges = struct { results: ArrayListUnmanaged(*Inst), block_inst: *Inst.Block, }; + + /// For debugging purposes. + pub fn dump(self: *Block, mod: Module) void { + zir.dumpBlock(mod, self); + } }; /// This is a temporary structure, references to it are valid only @@ -918,7 +951,7 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { .complete => return, .outdated => blk: { - log.debug("re-analyzing {}\n", .{decl.name}); + log.debug("re-analyzing {s}\n", .{decl.name}); // The exports this Decl performs will be re-discovered, so we remove them here // prior to re-analysis. @@ -953,7 +986,7 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { self.failed_decls.putAssumeCapacityNoClobber(decl, try Compilation.ErrorMsg.create( self.gpa, decl.src(), - "unable to analyze: {}", + "unable to analyze: {s}", .{@errorName(err)}, )); decl.analysis = .sema_failure_retryable; @@ -992,11 +1025,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { defer tracy.end(); const container_scope = decl.scope.cast(Scope.Container).?; - const tree = try self.getAstTree(container_scope); + const tree = try self.getAstTree(container_scope.file_scope); const ast_node = tree.root_node.decls()[decl.src_index]; switch (ast_node.tag) { .FnProto => { - const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node); + const fn_proto = ast_node.castTag(.FnProto).?; decl.analysis = .in_progress; @@ -1062,7 +1095,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .param_types = param_types, }, .{}); - if (self.comp.verbose_ir) { + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {}; } @@ -1071,13 +1104,21 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { errdefer decl_arena.deinit(); const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); + var inst_table = Scope.Block.InstTable.init(self.gpa); + defer inst_table.deinit(); + + var branch_quota: u32 = default_eval_branch_quota; + var block_scope: Scope.Block = .{ .parent = null, + .inst_table = &inst_table, .func = null, .decl = decl, .instructions = .{}, .arena = &decl_arena.allocator, + .inlining = null, .is_comptime = false, + .branch_quota = &branch_quota, }; defer block_scope.instructions.deinit(self.gpa); @@ -1113,14 +1154,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { const new_func = try decl_arena.allocator.create(Fn); const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); - const fn_zir = blk: { - // This scope's arena memory is discarded after the ZIR generation - // pass completes, and semantic analysis of it completes. - var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa); - errdefer gen_scope_arena.deinit(); + const fn_zir: zir.Module.Body = blk: { + // We put the ZIR inside the Decl arena. var gen_scope: Scope.GenZIR = .{ .decl = decl, - .arena = &gen_scope_arena.allocator, + .arena = &decl_arena.allocator, .parent = decl.scope, }; defer gen_scope.instructions.deinit(self.gpa); @@ -1131,8 +1169,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { for (fn_proto.params()) |param, i| { const name_token = param.name_token.?; const src = tree.token_locs[name_token].start; - const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString - const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg); + const param_name = try self.identifierTokenString(&gen_scope.base, name_token); + const arg = try decl_arena.allocator.create(zir.Inst.Arg); arg.* = .{ .base = .{ .tag = .arg, @@ -1144,7 +1182,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .kw_args = .{}, }; gen_scope.instructions.items[i] = &arg.base; - const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVal); + const sub_scope = try decl_arena.allocator.create(Scope.LocalVal); sub_scope.* = .{ .parent = params_scope, .gen_zir = &gen_scope, @@ -1165,22 +1203,29 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid); } - if (self.comp.verbose_ir) { + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {}; } - const fn_zir = try gen_scope_arena.allocator.create(Fn.ZIR); - fn_zir.* = .{ - .body = .{ - .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), - }, - .arena = gen_scope_arena.state, + break :blk .{ + .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), }; - break :blk fn_zir; }; + const is_inline = blk: { + if (fn_proto.getExternExportInlineToken()) |maybe_inline_token| { + if (tree.token_ids[maybe_inline_token] == .Keyword_inline) { + break :blk true; + } + } + break :blk false; + }; + const anal_state = ([2]Fn.Analysis{ .queued, .inline_only })[@boolToInt(is_inline)]; + new_func.* = .{ - .analysis = .{ .queued = fn_zir }, + .state = anal_state, + .zir = fn_zir, + .body = undefined, .owner_decl = decl, }; fn_payload.* = .{ @@ -1189,11 +1234,16 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { }; var prev_type_has_bits = false; + var prev_is_inline = false; var type_changed = true; if (decl.typedValueManaged()) |tvm| { prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); type_changed = !tvm.typed_value.ty.eql(fn_type); + if (tvm.typed_value.val.castTag(.function)) |payload| { + const prev_func = payload.data; + prev_is_inline = prev_func.state == .inline_only; + } tvm.deinit(self.gpa); } @@ -1211,18 +1261,26 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { decl.analysis = .complete; decl.generation = self.generation; - if (fn_type.hasCodeGenBits()) { + if (!is_inline and fn_type.hasCodeGenBits()) { // We don't fully codegen the decl until later, but we do need to reserve a global // offset table index for it. This allows us to codegen decls out of dependency order, // increasing how many computations can be done in parallel. try self.comp.bin_file.allocateDeclIndexes(decl); try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); - } else if (prev_type_has_bits) { + } else if (!prev_is_inline and prev_type_has_bits) { self.comp.bin_file.freeDecl(decl); } if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { if (tree.token_ids[maybe_export_token] == .Keyword_export) { + if (is_inline) { + return self.failTok( + &block_scope.base, + maybe_export_token, + "export of inline function", + .{}, + ); + } const export_src = tree.token_locs[maybe_export_token].start; const name_loc = tree.token_locs[fn_proto.getNameToken().?]; const name = tree.tokenSliceLoc(name_loc); @@ -1230,7 +1288,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { try self.analyzeExport(&block_scope.base, export_src, name, decl); } } - return type_changed; + return type_changed or is_inline != prev_is_inline; }, .VarDecl => { const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node); @@ -1242,13 +1300,21 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { errdefer decl_arena.deinit(); const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); + var decl_inst_table = Scope.Block.InstTable.init(self.gpa); + defer decl_inst_table.deinit(); + + var branch_quota: u32 = default_eval_branch_quota; + var block_scope: Scope.Block = .{ .parent = null, + .inst_table = &decl_inst_table, .func = null, .decl = decl, .instructions = .{}, .arena = &decl_arena.allocator, + .inlining = null, .is_comptime = true, + .branch_quota = &branch_quota, }; defer block_scope.instructions.deinit(self.gpa); @@ -1303,23 +1369,32 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { const src = tree.token_locs[init_node.firstToken()].start; const init_inst = try astgen.expr(self, &gen_scope.base, init_result_loc, init_node); - if (self.comp.verbose_ir) { + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {}; } + var var_inst_table = Scope.Block.InstTable.init(self.gpa); + defer var_inst_table.deinit(); + + var branch_quota_vi: u32 = default_eval_branch_quota; var inner_block: Scope.Block = .{ .parent = null, + .inst_table = &var_inst_table, .func = null, .decl = decl, .instructions = .{}, .arena = &gen_scope_arena.allocator, + .inlining = null, .is_comptime = true, + .branch_quota = &branch_quota_vi, }; defer inner_block.instructions.deinit(self.gpa); - try zir_sema.analyzeBody(self, &inner_block.base, .{ .instructions = gen_scope.instructions.items }); + try zir_sema.analyzeBody(self, &inner_block, .{ + .instructions = gen_scope.instructions.items, + }); // The result location guarantees the type coercion. - const analyzed_init_inst = init_inst.analyzed_inst.?; + const analyzed_init_inst = var_inst_table.get(init_inst).?; // The is_comptime in the Scope.Block guarantees the result is comptime-known. const val = analyzed_init_inst.value().?; @@ -1347,7 +1422,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { .val = Value.initTag(.type_type), }); const var_type = try astgen.expr(self, &type_scope.base, .{ .ty = type_type }, type_node); - if (self.comp.verbose_ir) { + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {}; } @@ -1423,21 +1498,29 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { defer gen_scope.instructions.deinit(self.gpa); _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr); - if (self.comp.verbose_ir) { + if (std.builtin.mode == .Debug and self.comp.verbose_ir) { zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {}; } + var inst_table = Scope.Block.InstTable.init(self.gpa); + defer inst_table.deinit(); + + var branch_quota: u32 = default_eval_branch_quota; + var block_scope: Scope.Block = .{ .parent = null, + .inst_table = &inst_table, .func = null, .decl = decl, .instructions = .{}, .arena = &analysis_arena.allocator, + .inlining = null, .is_comptime = true, + .branch_quota = &branch_quota, }; defer block_scope.instructions.deinit(self.gpa); - _ = try zir_sema.analyzeBody(self, &block_scope.base, .{ + _ = try zir_sema.analyzeBody(self, &block_scope, .{ .instructions = gen_scope.instructions.items, }); @@ -1475,7 +1558,7 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { if (zir_module.error_msg) |src_err_msg| { self.failed_files.putAssumeCapacityNoClobber( &root_scope.base, - try Compilation.ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{}", .{src_err_msg.msg}), + try Compilation.ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{s}", .{src_err_msg.msg}), ); root_scope.status = .unloaded_parse_failure; return error.AnalysisFail; @@ -1496,12 +1579,10 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { } } -fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree { +pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { const tracy = trace(@src()); defer tracy.end(); - const root_scope = container_scope.file_scope; - switch (root_scope.status) { .never_loaded, .unloaded_success => { try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1); @@ -1549,7 +1630,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void // We may be analyzing it for the first time, or this may be // an incremental update. This code handles both cases. - const tree = try self.getAstTree(container_scope); + const tree = try self.getAstTree(container_scope.file_scope); const decls = tree.root_node.decls(); try self.comp.work_queue.ensureUnusedCapacity(decls.len); @@ -1581,7 +1662,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void decl.src_index = decl_i; if (deleted_decls.remove(decl) == null) { decl.analysis = .sema_failure; - const err_msg = try Compilation.ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{}'", .{decl.name}); + const err_msg = try Compilation.ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{s}'", .{decl.name}); errdefer err_msg.destroy(self.gpa); try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); } else { @@ -1623,7 +1704,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void decl.src_index = decl_i; if (deleted_decls.remove(decl) == null) { decl.analysis = .sema_failure; - const err_msg = try Compilation.ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{}'", .{decl.name}); + const err_msg = try Compilation.ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{s}'", .{decl.name}); errdefer err_msg.destroy(self.gpa); try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); } else if (!srcHashEql(decl.contents_hash, contents_hash)) { @@ -1641,7 +1722,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void } } else if (src_decl.castTag(.Comptime)) |comptime_node| { const name_index = self.getNextAnonNameIndex(); - const name = try std.fmt.allocPrint(self.gpa, "__comptime_{}", .{name_index}); + const name = try std.fmt.allocPrint(self.gpa, "__comptime_{d}", .{name_index}); defer self.gpa.free(name); const name_hash = container_scope.fullyQualifiedNameHash(name); @@ -1663,7 +1744,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void // Handle explicitly deleted decls from the source code. Not to be confused // with when we delete decls because they are no longer referenced. for (deleted_decls.items()) |entry| { - log.debug("noticed '{}' deleted from source\n", .{entry.key.name}); + log.debug("noticed '{s}' deleted from source\n", .{entry.key.name}); try self.deleteDecl(entry.key); } } @@ -1716,7 +1797,7 @@ pub fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { // Handle explicitly deleted decls from the source code. Not to be confused // with when we delete decls because they are no longer referenced. for (deleted_decls.items()) |entry| { - log.debug("noticed '{}' deleted from source\n", .{entry.key.name}); + log.debug("noticed '{s}' deleted from source\n", .{entry.key.name}); try self.deleteDecl(entry.key); } } @@ -1728,7 +1809,7 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void { // not be present in the set, and this does nothing. decl.scope.removeDecl(decl); - log.debug("deleting decl '{}'\n", .{decl.name}); + log.debug("deleting decl '{s}'\n", .{decl.name}); const name_hash = decl.fullyQualifiedNameHash(); self.decl_table.removeAssertDiscard(name_hash); // Remove itself from its dependencies, because we are about to destroy the decl pointer. @@ -1806,30 +1887,36 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void { // Use the Decl's arena for function memory. var arena = decl.typed_value.most_recent.arena.?.promote(self.gpa); defer decl.typed_value.most_recent.arena.?.* = arena.state; + var inst_table = Scope.Block.InstTable.init(self.gpa); + defer inst_table.deinit(); + var branch_quota: u32 = default_eval_branch_quota; + var inner_block: Scope.Block = .{ .parent = null, + .inst_table = &inst_table, .func = func, .decl = decl, .instructions = .{}, .arena = &arena.allocator, + .inlining = null, .is_comptime = false, + .branch_quota = &branch_quota, }; defer inner_block.instructions.deinit(self.gpa); - const fn_zir = func.analysis.queued; - defer fn_zir.arena.promote(self.gpa).deinit(); - func.analysis = .{ .in_progress = {} }; - log.debug("set {} to in_progress\n", .{decl.name}); + func.state = .in_progress; + log.debug("set {s} to in_progress\n", .{decl.name}); - try zir_sema.analyzeBody(self, &inner_block.base, fn_zir.body); + try zir_sema.analyzeBody(self, &inner_block, func.zir); const instructions = try arena.allocator.dupe(*Inst, inner_block.instructions.items); - func.analysis = .{ .success = .{ .instructions = instructions } }; - log.debug("set {} to success\n", .{decl.name}); + func.state = .success; + func.body = .{ .instructions = instructions }; + log.debug("set {s} to success\n", .{decl.name}); } fn markOutdatedDecl(self: *Module, decl: *Decl) !void { - log.debug("mark {} outdated\n", .{decl.name}); + log.debug("mark {s} outdated\n", .{decl.name}); try self.comp.work_queue.writeItem(.{ .analyze_decl = decl }); if (self.failed_decls.remove(decl)) |entry| { entry.value.destroy(self.gpa); @@ -1991,7 +2078,7 @@ pub fn analyzeExport( self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create( self.gpa, src, - "exported symbol collision: {}", + "exported symbol collision: {s}", .{symbol_name}, )); // TODO: add a note @@ -2007,7 +2094,7 @@ pub fn analyzeExport( self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create( self.gpa, src, - "unable to export: {}", + "unable to export: {s}", .{@errorName(err)}, )); new_export.status = .failed_retryable; @@ -2277,7 +2364,7 @@ pub fn createAnonymousDecl( ) !*Decl { const name_index = self.getNextAnonNameIndex(); const scope_decl = scope.decl().?; - const name = try std.fmt.allocPrint(self.gpa, "{}__anon_{}", .{ scope_decl.name, name_index }); + const name = try std.fmt.allocPrint(self.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index }); defer self.gpa.free(name); const name_hash = scope.namespace().fullyQualifiedNameHash(name); const src_hash: std.zig.SrcHash = undefined; @@ -2321,7 +2408,7 @@ pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) Inn self.ensureDeclAnalyzed(decl) catch |err| { if (scope.cast(Scope.Block)) |block| { if (block.func) |func| { - func.analysis = .dependency_failure; + func.state = .dependency_failure; } else { block.decl.analysis = .dependency_failure; } @@ -2384,7 +2471,7 @@ pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_sr pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst { const decl = self.lookupDeclName(scope, decl_name) orelse - return self.fail(scope, src, "decl '{}' not found", .{decl_name}); + return self.fail(scope, src, "decl '{s}' not found", .{decl_name}); return self.analyzeDeclRef(scope, src, decl); } @@ -2555,7 +2642,7 @@ pub fn cmpNumeric( if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { - return self.fail(scope, src, "vector length mismatch: {} and {}", .{ + return self.fail(scope, src, "vector length mismatch: {d} and {d}", .{ lhs.ty.arrayLen(), rhs.ty.arrayLen(), }); @@ -2700,7 +2787,7 @@ pub fn cmpNumeric( const dest_type = if (dest_float_type) |ft| ft else blk: { const max_bits = std.math.max(lhs_bits, rhs_bits); const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) { - error.Overflow => return self.fail(scope, src, "{} exceeds maximum integer bit count", .{max_bits}), + error.Overflow => return self.fail(scope, src, "{d} exceeds maximum integer bit count", .{max_bits}), }; break :blk try self.makeIntType(scope, dest_int_is_signed, casted_bits); }; @@ -3029,11 +3116,20 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com }, .block => { const block = scope.cast(Scope.Block).?; - if (block.func) |func| { - func.analysis = .sema_failure; + if (block.inlining) |inlining| { + if (inlining.shared.caller) |func| { + func.state = .sema_failure; + } else { + block.decl.analysis = .sema_failure; + block.decl.generation = self.generation; + } } else { - block.decl.analysis = .sema_failure; - block.decl.generation = self.generation; + if (block.func) |func| { + func.state = .sema_failure; + } else { + block.decl.analysis = .sema_failure; + block.decl.generation = self.generation; + } } self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg); }, @@ -3328,7 +3424,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source"); const loc = std.zig.findLineColumn(source, inst.src); if (inst.tag == .constant) { - std.debug.print("constant ty={} val={} src={}:{}:{}\n", .{ + std.debug.print("constant ty={} val={} src={s}:{d}:{d}\n", .{ inst.ty, inst.castTag(.constant).?.val, zir_module.subFilePath(), @@ -3336,7 +3432,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { loc.column + 1, }); } else if (inst.deaths == 0) { - std.debug.print("{} ty={} src={}:{}:{}\n", .{ + std.debug.print("{s} ty={} src={s}:{d}:{d}\n", .{ @tagName(inst.tag), inst.ty, zir_module.subFilePath(), @@ -3344,7 +3440,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { loc.column + 1, }); } else { - std.debug.print("{} ty={} deaths={b} src={}:{}:{}\n", .{ + std.debug.print("{s} ty={} deaths={b} src={s}:{d}:{d}\n", .{ @tagName(inst.tag), inst.ty, inst.deaths, @@ -3389,12 +3485,16 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic var fail_block: Scope.Block = .{ .parent = parent_block, + .inst_table = parent_block.inst_table, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, .arena = parent_block.arena, + .inlining = parent_block.inlining, .is_comptime = parent_block.is_comptime, + .branch_quota = parent_block.branch_quota, }; + defer fail_block.instructions.deinit(mod.gpa); _ = try mod.safetyPanic(&fail_block, ok.src, panic_id); @@ -3436,3 +3536,34 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void return mod.fail(scope, src, "variable of type '{}' must be const or comptime", .{ty}); } } + +/// Identifier token -> String (allocated in scope.arena()) +pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { + const tree = scope.tree(); + + const ident_name = tree.tokenSlice(token); + if (mem.startsWith(u8, ident_name, "@")) { + const raw_string = ident_name[1..]; + var bad_index: usize = undefined; + return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) { + error.InvalidCharacter => { + const bad_byte = raw_string[bad_index]; + const src = tree.token_locs[token].start; + return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte}); + }, + else => |e| return e, + }; + } + return ident_name; +} + +pub fn emitBackwardBranch(mod: *Module, block: *Scope.Block, src: usize) !void { + const shared = block.inlining.?.shared; + shared.branch_count += 1; + if (shared.branch_count > block.branch_quota.*) { + // TODO show the "called from here" stack + return mod.fail(&block.base, src, "evaluation exceeded {d} backwards branches", .{ + block.branch_quota.*, + }); + } +} diff --git a/src/astgen.zig b/src/astgen.zig index 3670cb260eee..8275a05d774c 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -384,8 +384,8 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpr .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, else => if (node.getLabel()) |break_label| { - const label_name = try identifierTokenString(mod, parent_scope, break_label); - return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name}); + const label_name = try mod.identifierTokenString(parent_scope, break_label); + return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); } else { return mod.failTok(parent_scope, src, "break expression outside loop", .{}); }, @@ -426,8 +426,8 @@ fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowE .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, else => if (node.getLabel()) |break_label| { - const label_name = try identifierTokenString(mod, parent_scope, break_label); - return mod.failTok(parent_scope, break_label, "label not found: '{}'", .{label_name}); + const label_name = try mod.identifierTokenString(parent_scope, break_label); + return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); } else { return mod.failTok(parent_scope, src, "continue expression outside loop", .{}); }, @@ -551,7 +551,7 @@ fn varDecl( } const tree = scope.tree(); const name_src = tree.token_locs[node.name_token].start; - const ident_name = try identifierTokenString(mod, scope, node.name_token); + const ident_name = try mod.identifierTokenString(scope, node.name_token); // Local variables shadowing detection, including function parameters. { @@ -560,14 +560,14 @@ fn varDecl( .local_val => { const local_val = s.cast(Scope.LocalVal).?; if (mem.eql(u8, local_val.name, ident_name)) { - return mod.fail(scope, name_src, "redefinition of '{}'", .{ident_name}); + return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name}); } s = local_val.parent; }, .local_ptr => { const local_ptr = s.cast(Scope.LocalPtr).?; if (mem.eql(u8, local_ptr.name, ident_name)) { - return mod.fail(scope, name_src, "redefinition of '{}'", .{ident_name}); + return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name}); } s = local_ptr.parent; }, @@ -578,7 +578,7 @@ fn varDecl( // Namespace vars shadowing detection if (mod.lookupDeclName(scope, ident_name)) |_| { - return mod.fail(scope, name_src, "redefinition of '{}'", .{ident_name}); + return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name}); } const init_node = node.getInitNode() orelse return mod.fail(scope, name_src, "variables must be initialized", .{}); @@ -843,7 +843,7 @@ fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_ins fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.name].start; - const name = try identifierTokenString(mod, scope, node.name); + const name = try mod.identifierTokenString(scope, node.name); return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{}); } @@ -864,7 +864,7 @@ fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Erro for (decls) |decl, i| { const tag = decl.castTag(.ErrorTag).?; - fields[i] = try identifierTokenString(mod, scope, tag.name_token); + fields[i] = try mod.identifierTokenString(scope, tag.name_token); } // analyzing the error set results in a decl ref, so we might need to dereference it @@ -988,36 +988,16 @@ fn orelseCatchExpr( /// Return whether the identifier names of two tokens are equal. Resolves @"" tokens without allocating. /// OK in theory it could do it without allocating. This implementation allocates when the @"" form is used. fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool { - const ident_name_1 = try identifierTokenString(mod, scope, token1); - const ident_name_2 = try identifierTokenString(mod, scope, token2); + const ident_name_1 = try mod.identifierTokenString(scope, token1); + const ident_name_2 = try mod.identifierTokenString(scope, token2); return mem.eql(u8, ident_name_1, ident_name_2); } -/// Identifier token -> String (allocated in scope.arena()) -fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { - const tree = scope.tree(); - - const ident_name = tree.tokenSlice(token); - if (mem.startsWith(u8, ident_name, "@")) { - const raw_string = ident_name[1..]; - var bad_index: usize = undefined; - return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) { - error.InvalidCharacter => { - const bad_byte = raw_string[bad_index]; - const src = tree.token_locs[token].start; - return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte}); - }, - else => |e| return e, - }; - } - return ident_name; -} - pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst { const tree = scope.tree(); const src = tree.token_locs[node.token].start; - const ident_name = try identifierTokenString(mod, scope, node.token); + const ident_name = try mod.identifierTokenString(scope, node.token); return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{}); } @@ -1936,7 +1916,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo defer tracy.end(); const tree = scope.tree(); - const ident_name = try identifierTokenString(mod, scope, ident.token); + const ident_name = try mod.identifierTokenString(scope, ident.token); const src = tree.token_locs[ident.token].start; if (mem.eql(u8, ident_name, "_")) { return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{}); @@ -1955,7 +1935,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo error.Overflow => return mod.failNode( scope, &ident.base, - "primitive integer type '{}' exceeds maximum bit width of 65535", + "primitive integer type '{s}' exceeds maximum bit width of 65535", .{ident_name}, ), error.InvalidCharacter => break :integer, @@ -2010,7 +1990,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{})); } - return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name}); + return mod.failNode(scope, &ident.base, "use of undeclared identifier '{s}'", .{ident_name}); } fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) InnerError!*zir.Inst { @@ -2204,7 +2184,7 @@ fn ensureBuiltinParamCount(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinC return; const s = if (count == 1) "" else "s"; - return mod.failTok(scope, call.builtin_token, "expected {} parameter{}, found {}", .{ count, s, call.params_len }); + return mod.failTok(scope, call.builtin_token, "expected {d} parameter{s}, found {d}", .{ count, s, call.params_len }); } fn simpleCast( @@ -2337,6 +2317,19 @@ fn compileError(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerE return addZIRUnOp(mod, scope, src, .compileerror, target); } +fn setEvalBranchQuota(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { + try ensureBuiltinParamCount(mod, scope, call, 1); + const tree = scope.tree(); + const src = tree.token_locs[call.builtin_token].start; + const params = call.params(); + const u32_type = try addZIRInstConst(mod, scope, src, .{ + .ty = Type.initTag(.type), + .val = Value.initTag(.u32_type), + }); + const quota = try expr(mod, scope, .{ .ty = u32_type }, params[0]); + return addZIRUnOp(mod, scope, src, .set_eval_branch_quota, quota); +} + fn typeOf(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { const tree = scope.tree(); const arena = scope.arena(); @@ -2382,8 +2375,10 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built return rlWrap(mod, scope, rl, try import(mod, scope, call)); } else if (mem.eql(u8, builtin_name, "@compileError")) { return compileError(mod, scope, call); + } else if (mem.eql(u8, builtin_name, "@setEvalBranchQuota")) { + return setEvalBranchQuota(mod, scope, call); } else { - return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name}); + return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{s}'", .{builtin_name}); } } diff --git a/src/codegen.zig b/src/codegen.zig index 9e6de711d485..c2537a1ca0fb 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -228,7 +228,7 @@ pub fn generateSymbol( .fail = try ErrorMsg.create( bin_file.allocator, src, - "TODO implement generateSymbol for type '{}'", + "TODO implement generateSymbol for type '{s}'", .{@tagName(t)}, ), }; @@ -532,7 +532,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { self.code.items.len += 4; try self.dbgSetPrologueEnd(); - try self.genBody(self.mod_fn.analysis.success); + try self.genBody(self.mod_fn.body); const stack_end = self.max_end_stack; if (stack_end > math.maxInt(i32)) @@ -543,13 +543,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (self.code.items.len >= math.maxInt(i32)) { return self.fail(self.src, "unable to perform relocation: jump too far", .{}); } - for (self.exitlude_jump_relocs.items) |jmp_reloc| { + if (self.exitlude_jump_relocs.items.len == 1) { + self.code.items.len -= 5; + } else for (self.exitlude_jump_relocs.items) |jmp_reloc| { const amt = self.code.items.len - (jmp_reloc + 4); - // If it wouldn't jump at all, elide it. - if (amt == 0) { - self.code.items.len -= 5; - continue; - } const s32_amt = @intCast(i32, amt); mem.writeIntLittle(i32, self.code.items[jmp_reloc..][0..4], s32_amt); } @@ -576,7 +573,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }); } else { try self.dbgSetPrologueEnd(); - try self.genBody(self.mod_fn.analysis.success); + try self.genBody(self.mod_fn.body); try self.dbgSetEpilogueBegin(); } }, @@ -593,7 +590,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { try self.dbgSetPrologueEnd(); - try self.genBody(self.mod_fn.analysis.success); + try self.genBody(self.mod_fn.body); // Backpatch stack offset const stack_end = self.max_end_stack; @@ -638,13 +635,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { writeInt(u32, try self.code.addManyAsArray(4), Instruction.pop(.al, .{ .fp, .pc }).toU32()); } else { try self.dbgSetPrologueEnd(); - try self.genBody(self.mod_fn.analysis.success); + try self.genBody(self.mod_fn.body); try self.dbgSetEpilogueBegin(); } }, else => { try self.dbgSetPrologueEnd(); - try self.genBody(self.mod_fn.analysis.success); + try self.genBody(self.mod_fn.body); try self.dbgSetEpilogueBegin(); }, } @@ -2029,7 +2026,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }); break :blk 0x84; }, - else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }), + else => return self.fail(inst.base.src, "TODO implement condbr {s} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }), }; self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode }); const reloc = Reloc{ .rel32 = self.code.items.len }; @@ -2376,11 +2373,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .arm, .armeb => { for (inst.inputs) |input, i| { if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); + return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input}); } const reg_name = input[1 .. input.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); try self.genSetReg(inst.base.src, reg, arg); } @@ -2393,11 +2390,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (inst.output) |output| { if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output}); + return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); } const reg_name = output[2 .. output.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); return MCValue{ .register = reg }; } else { return MCValue.none; @@ -2406,11 +2403,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .aarch64 => { for (inst.inputs) |input, i| { if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); + return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input}); } const reg_name = input[1 .. input.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); try self.genSetReg(inst.base.src, reg, arg); } @@ -2425,11 +2422,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (inst.output) |output| { if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output}); + return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); } const reg_name = output[2 .. output.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); return MCValue{ .register = reg }; } else { return MCValue.none; @@ -2438,11 +2435,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .riscv64 => { for (inst.inputs) |input, i| { if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); + return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input}); } const reg_name = input[1 .. input.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); try self.genSetReg(inst.base.src, reg, arg); } @@ -2455,11 +2452,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (inst.output) |output| { if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output}); + return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); } const reg_name = output[2 .. output.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); return MCValue{ .register = reg }; } else { return MCValue.none; @@ -2468,11 +2465,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .x86_64, .i386 => { for (inst.inputs) |input, i| { if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input}); + return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input}); } const reg_name = input[1 .. input.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); try self.genSetReg(inst.base.src, reg, arg); } @@ -2485,11 +2482,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (inst.output) |output| { if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { - return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output}); + return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output}); } const reg_name = output[2 .. output.len - 1]; const reg = parseRegName(reg_name) orelse - return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name}); + return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); return MCValue{ .register = reg }; } else { return MCValue.none; @@ -2837,7 +2834,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 48), 48).toU32()); } }, - .register => return self.fail(src, "TODO implement genSetReg for aarch64 {}", .{mcv}), + .register => |src_reg| { + // If the registers are the same, nothing to do. + if (src_reg.id() == reg.id()) + return; + + // mov reg, src_reg + writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr( + reg, + .xzr, + src_reg, + Instruction.Shift.none, + ).toU32()); + }, .memory => |addr| { if (self.bin_file.options.pie) { // For MachO, the binary, with the exception of object files, has to be a PIE. @@ -3417,7 +3426,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { next_int_reg += 1; } }, - else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}), + else => return self.fail(src, "TODO implement function parameters of type {s}", .{@tagName(ty.zigTypeTag())}), } } result.stack_byte_count = next_stack_offset; @@ -3475,6 +3484,59 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { else => return self.fail(src, "TODO implement function parameters for {} on arm", .{cc}), } }, + .aarch64 => { + switch (cc) { + .Naked => { + assert(result.args.len == 0); + result.return_value = .{ .unreach = {} }; + result.stack_byte_count = 0; + result.stack_align = 1; + return result; + }, + .Unspecified, .C => { + // ARM64 Procedure Call Standard + var ncrn: usize = 0; // Next Core Register Number + var nsaa: u32 = 0; // Next stacked argument address + + for (param_types) |ty, i| { + // We round up NCRN only for non-Apple platforms which allow the 16-byte aligned + // values to spread across odd-numbered registers. + if (ty.abiAlignment(self.target.*) == 16 and !self.target.isDarwin()) { + // Round up NCRN to the next even number + ncrn += ncrn % 2; + } + + const param_size = @intCast(u32, ty.abiSize(self.target.*)); + if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) { + if (param_size <= 8) { + result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] }; + ncrn += 1; + } else { + return self.fail(src, "TODO MCValues with multiple registers", .{}); + } + } else if (ncrn < 8 and nsaa == 0) { + return self.fail(src, "TODO MCValues split between registers and stack", .{}); + } else { + ncrn = 8; + // TODO Apple allows the arguments on the stack to be non-8-byte aligned provided + // that the entire stack space consumed by the arguments is 8-byte aligned. + if (ty.abiAlignment(self.target.*) == 8) { + if (nsaa % 8 != 0) { + nsaa += 8 - (nsaa % 8); + } + } + + result.args[i] = .{ .stack_offset = nsaa }; + nsaa += param_size; + } + } + + result.stack_byte_count = nsaa; + result.stack_align = 16; + }, + else => return self.fail(src, "TODO implement function parameters for {} on aarch64", .{cc}), + } + }, else => if (param_types.len != 0) return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}), } diff --git a/src/codegen/c.zig b/src/codegen/c.zig index c6c29942d98c..1a89e22d4878 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -235,7 +235,7 @@ fn renderFunctionSignature( try writer.writeAll(", "); } try renderType(ctx, writer, tv.ty.fnParamType(index)); - try writer.print(" arg{}", .{index}); + try writer.print(" arg{d}", .{index}); } } try writer.writeByte(')'); @@ -275,7 +275,7 @@ pub fn generate(file: *C, module: *Module, decl: *Decl) !void { try writer.writeAll(" {"); const func: *Module.Fn = func_payload.data; - const instructions = func.analysis.success.instructions; + const instructions = func.body.instructions; if (instructions.len > 0) { try writer.writeAll("\n"); for (instructions) |inst| { @@ -383,7 +383,7 @@ const Context = struct { } fn name(self: *Context) ![]u8 { - const val = try std.fmt.allocPrint(&self.arena.allocator, "__temp_{}", .{self.unnamed_index}); + const val = try std.fmt.allocPrint(&self.arena.allocator, "__temp_{d}", .{self.unnamed_index}); self.unnamed_index += 1; return val; } @@ -420,7 +420,7 @@ fn genAlloc(ctx: *Context, file: *C, alloc: *Inst.NoOp) !?[]u8 { } fn genArg(ctx: *Context) !?[]u8 { - const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{}", .{ctx.argdex}); + const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{d}", .{ctx.argdex}); ctx.argdex += 1; return name; } @@ -528,7 +528,7 @@ fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 { try renderValue(ctx, writer, arg.ty, val); } else { const val = try ctx.resolveInst(arg); - try writer.print("{}", .{val}); + try writer.print("{s}", .{val}); } } } @@ -587,7 +587,7 @@ fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 { const arg = as.args[index]; try writer.writeAll("register "); try renderType(ctx, writer, arg.ty); - try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg }); + try writer.print(" {s}_constant __asm__(\"{s}\") = ", .{ reg, reg }); // TODO merge constant handling into inst_map as well if (arg.castTag(.constant)) |c| { try renderValue(ctx, writer, arg.ty, c.val); @@ -597,13 +597,13 @@ fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 { if (!gop.found_existing) { return ctx.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{}); } - try writer.print("{};\n ", .{gop.entry.value}); + try writer.print("{s};\n ", .{gop.entry.value}); } } else { return ctx.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{}); } } - try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source }); + try writer.print("__asm {s} (\"{s}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source }); if (as.output) |o| { return ctx.fail(ctx.decl.src(), "TODO inline asm output", .{}); } @@ -619,7 +619,7 @@ fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 { if (index > 0) { try writer.writeAll(", "); } - try writer.print("\"\"({}_constant)", .{reg}); + try writer.print("\"\"({s}_constant)", .{reg}); } else { // This is blocked by the earlier test unreachable; diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index c7ad59f5d178..036243dccac5 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -63,7 +63,7 @@ pub fn genCode(buf: *ArrayList(u8), decl: *Decl) !void { // TODO: check for and handle death of instructions const tv = decl.typed_value.most_recent.typed_value; const mod_fn = tv.val.castTag(.function).?.data; - for (mod_fn.analysis.success.instructions) |inst| try genInst(buf, decl, inst); + for (mod_fn.body.instructions) |inst| try genInst(buf, decl, inst); // Write 'end' opcode try writer.writeByte(0x0B); diff --git a/src/config.zig.in b/src/config.zig.in index 9d16cf382441..b672581ea4a8 100644 --- a/src/config.zig.in +++ b/src/config.zig.in @@ -2,7 +2,7 @@ pub const have_llvm = true; pub const version: [:0]const u8 = "@ZIG_VERSION@"; pub const semver = try @import("std").SemanticVersion.parse(version); pub const log_scopes: []const []const u8 = &[_][]const u8{}; -pub const zir_dumps: []const []const u8 = &[_][]const u8{}; pub const enable_tracy = false; pub const is_stage1 = true; pub const skip_non_native = false; +pub const omit_stage2: bool = @ZIG_OMIT_STAGE2_BOOL@; diff --git a/src/glibc.zig b/src/glibc.zig index 0c2787272057..e7b7b1b1cf03 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -72,7 +72,7 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! errdefer version_table.deinit(gpa); var glibc_dir = zig_lib_dir.openDir("libc" ++ path.sep_str ++ "glibc", .{}) catch |err| { - std.log.err("unable to open glibc dir: {}", .{@errorName(err)}); + std.log.err("unable to open glibc dir: {s}", .{@errorName(err)}); return error.ZigInstallationCorrupt; }; defer glibc_dir.close(); @@ -81,7 +81,7 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! const vers_txt_contents = glibc_dir.readFileAlloc(gpa, "vers.txt", max_txt_size) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => { - std.log.err("unable to read vers.txt: {}", .{@errorName(err)}); + std.log.err("unable to read vers.txt: {s}", .{@errorName(err)}); return error.ZigInstallationCorrupt; }, }; @@ -91,7 +91,7 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! const fns_txt_contents = glibc_dir.readFileAlloc(arena, "fns.txt", max_txt_size) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => { - std.log.err("unable to read fns.txt: {}", .{@errorName(err)}); + std.log.err("unable to read fns.txt: {s}", .{@errorName(err)}); return error.ZigInstallationCorrupt; }, }; @@ -99,7 +99,7 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! const abi_txt_contents = glibc_dir.readFileAlloc(gpa, "abi.txt", max_txt_size) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => { - std.log.err("unable to read abi.txt: {}", .{@errorName(err)}); + std.log.err("unable to read abi.txt: {s}", .{@errorName(err)}); return error.ZigInstallationCorrupt; }, }; @@ -111,12 +111,12 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! while (it.next()) |line| : (line_i += 1) { const prefix = "GLIBC_"; if (!mem.startsWith(u8, line, prefix)) { - std.log.err("vers.txt:{}: expected 'GLIBC_' prefix", .{line_i}); + std.log.err("vers.txt:{d}: expected 'GLIBC_' prefix", .{line_i}); return error.ZigInstallationCorrupt; } const adjusted_line = line[prefix.len..]; const ver = std.builtin.Version.parse(adjusted_line) catch |err| { - std.log.err("vers.txt:{}: unable to parse glibc version '{}': {}", .{ line_i, line, @errorName(err) }); + std.log.err("vers.txt:{d}: unable to parse glibc version '{s}': {s}", .{ line_i, line, @errorName(err) }); return error.ZigInstallationCorrupt; }; try all_versions.append(arena, ver); @@ -128,15 +128,15 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! while (file_it.next()) |line| : (line_i += 1) { var line_it = mem.tokenize(line, " "); const fn_name = line_it.next() orelse { - std.log.err("fns.txt:{}: expected function name", .{line_i}); + std.log.err("fns.txt:{d}: expected function name", .{line_i}); return error.ZigInstallationCorrupt; }; const lib_name = line_it.next() orelse { - std.log.err("fns.txt:{}: expected library name", .{line_i}); + std.log.err("fns.txt:{d}: expected library name", .{line_i}); return error.ZigInstallationCorrupt; }; const lib = findLib(lib_name) orelse { - std.log.err("fns.txt:{}: unknown library name: {}", .{ line_i, lib_name }); + std.log.err("fns.txt:{d}: unknown library name: {s}", .{ line_i, lib_name }); return error.ZigInstallationCorrupt; }; try all_functions.append(arena, .{ @@ -158,27 +158,27 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! while (line_it.next()) |target_string| { var component_it = mem.tokenize(target_string, "-"); const arch_name = component_it.next() orelse { - std.log.err("abi.txt:{}: expected arch name", .{line_i}); + std.log.err("abi.txt:{d}: expected arch name", .{line_i}); return error.ZigInstallationCorrupt; }; const os_name = component_it.next() orelse { - std.log.err("abi.txt:{}: expected OS name", .{line_i}); + std.log.err("abi.txt:{d}: expected OS name", .{line_i}); return error.ZigInstallationCorrupt; }; const abi_name = component_it.next() orelse { - std.log.err("abi.txt:{}: expected ABI name", .{line_i}); + std.log.err("abi.txt:{d}: expected ABI name", .{line_i}); return error.ZigInstallationCorrupt; }; const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse { - std.log.err("abi.txt:{}: unrecognized arch: '{}'", .{ line_i, arch_name }); + std.log.err("abi.txt:{d}: unrecognized arch: '{s}'", .{ line_i, arch_name }); return error.ZigInstallationCorrupt; }; if (!mem.eql(u8, os_name, "linux")) { - std.log.err("abi.txt:{}: expected OS 'linux', found '{}'", .{ line_i, os_name }); + std.log.err("abi.txt:{d}: expected OS 'linux', found '{s}'", .{ line_i, os_name }); return error.ZigInstallationCorrupt; } const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse { - std.log.err("abi.txt:{}: unrecognized ABI: '{}'", .{ line_i, abi_name }); + std.log.err("abi.txt:{d}: unrecognized ABI: '{s}'", .{ line_i, abi_name }); return error.ZigInstallationCorrupt; }; @@ -193,7 +193,7 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! }; for (ver_list_base) |*ver_list| { const line = file_it.next() orelse { - std.log.err("abi.txt:{}: missing version number line", .{line_i}); + std.log.err("abi.txt:{d}: missing version number line", .{line_i}); return error.ZigInstallationCorrupt; }; line_i += 1; @@ -206,12 +206,12 @@ pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError! while (line_it.next()) |version_index_string| { if (ver_list.len >= ver_list.versions.len) { // If this happens with legit data, increase the array len in the type. - std.log.err("abi.txt:{}: too many versions", .{line_i}); + std.log.err("abi.txt:{d}: too many versions", .{line_i}); return error.ZigInstallationCorrupt; } const version_index = std.fmt.parseInt(u8, version_index_string, 10) catch |err| { // If this happens with legit data, increase the size of the integer type in the struct. - std.log.err("abi.txt:{}: unable to parse version: {}", .{ line_i, @errorName(err) }); + std.log.err("abi.txt:{d}: unable to parse version: {s}", .{ line_i, @errorName(err) }); return error.ZigInstallationCorrupt; }; @@ -531,7 +531,7 @@ fn add_include_dirs(comp: *Compilation, arena: *Allocator, args: *std.ArrayList( try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc ++ "glibc" })); try args.append("-I"); - try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}", .{ + try args.append(try std.fmt.allocPrint(arena, "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}", .{ comp.zig_lib_directory.path.?, @tagName(arch), @tagName(target.os.tag), @tagName(target.abi), })); @@ -539,7 +539,7 @@ fn add_include_dirs(comp: *Compilation, arena: *Allocator, args: *std.ArrayList( try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc")); try args.append("-I"); - try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-linux-any", .{ + try args.append(try std.fmt.allocPrint(arena, "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-linux-any", .{ comp.zig_lib_directory.path.?, @tagName(arch), })); @@ -881,7 +881,7 @@ pub fn buildSharedObjects(comp: *Compilation) !void { if (o_directory.handle.createFile(ok_basename, .{})) |file| { file.close(); } else |err| { - std.log.warn("glibc shared objects: failed to mark completion: {}", .{@errorName(err)}); + std.log.warn("glibc shared objects: failed to mark completion: {s}", .{@errorName(err)}); } } diff --git a/src/libc_installation.zig b/src/libc_installation.zig index 0b1029eeb8e0..cc96146e0b19 100644 --- a/src/libc_installation.zig +++ b/src/libc_installation.zig @@ -83,7 +83,7 @@ pub const LibCInstallation = struct { } inline for (fields) |field, i| { if (!found_keys[i].found) { - log.err("missing field: {}\n", .{field.name}); + log.err("missing field: {s}\n", .{field.name}); return error.ParseError; } } @@ -96,18 +96,18 @@ pub const LibCInstallation = struct { return error.ParseError; } if (self.crt_dir == null and !is_darwin) { - log.err("crt_dir may not be empty for {}\n", .{@tagName(Target.current.os.tag)}); + log.err("crt_dir may not be empty for {s}\n", .{@tagName(Target.current.os.tag)}); return error.ParseError; } if (self.msvc_lib_dir == null and is_windows and !is_gnu) { - log.err("msvc_lib_dir may not be empty for {}-{}\n", .{ + log.err("msvc_lib_dir may not be empty for {s}-{s}\n", .{ @tagName(Target.current.os.tag), @tagName(Target.current.abi), }); return error.ParseError; } if (self.kernel32_lib_dir == null and is_windows and !is_gnu) { - log.err("kernel32_lib_dir may not be empty for {}-{}\n", .{ + log.err("kernel32_lib_dir may not be empty for {s}-{s}\n", .{ @tagName(Target.current.os.tag), @tagName(Target.current.abi), }); @@ -128,25 +128,25 @@ pub const LibCInstallation = struct { try out.print( \\# The directory that contains `stdlib.h`. \\# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null` - \\include_dir={} + \\include_dir={s} \\ \\# The system-specific include directory. May be the same as `include_dir`. \\# On Windows it's the directory that includes `vcruntime.h`. \\# On POSIX it's the directory that includes `sys/errno.h`. - \\sys_include_dir={} + \\sys_include_dir={s} \\ \\# The directory that contains `crt1.o` or `crt2.o`. \\# On POSIX, can be found with `cc -print-file-name=crt1.o`. \\# Not needed when targeting MacOS. - \\crt_dir={} + \\crt_dir={s} \\ \\# The directory that contains `vcruntime.lib`. \\# Only needed when targeting MSVC on Windows. - \\msvc_lib_dir={} + \\msvc_lib_dir={s} \\ \\# The directory that contains `kernel32.lib`. \\# Only needed when targeting MSVC on Windows. - \\kernel32_lib_dir={} + \\kernel32_lib_dir={s} \\ , .{ include_dir, @@ -338,7 +338,7 @@ pub const LibCInstallation = struct { for (searches) |search| { result_buf.shrink(0); - try result_buf.outStream().print("{}\\Include\\{}\\ucrt", .{ search.path, search.version }); + try result_buf.outStream().print("{s}\\Include\\{s}\\ucrt", .{ search.path, search.version }); var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { error.FileNotFound, @@ -384,7 +384,7 @@ pub const LibCInstallation = struct { for (searches) |search| { result_buf.shrink(0); - try result_buf.outStream().print("{}\\Lib\\{}\\ucrt\\{}", .{ search.path, search.version, arch_sub_dir }); + try result_buf.outStream().print("{s}\\Lib\\{s}\\ucrt\\{s}", .{ search.path, search.version, arch_sub_dir }); var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { error.FileNotFound, @@ -439,7 +439,7 @@ pub const LibCInstallation = struct { for (searches) |search| { result_buf.shrink(0); const stream = result_buf.outStream(); - try stream.print("{}\\Lib\\{}\\um\\{}", .{ search.path, search.version, arch_sub_dir }); + try stream.print("{s}\\Lib\\{s}\\um\\{s}", .{ search.path, search.version, arch_sub_dir }); var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) { error.FileNotFound, @@ -520,7 +520,7 @@ fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 { const allocator = args.allocator; const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe; - const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", .{args.search_basename}); + const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={s}", .{args.search_basename}); defer allocator.free(arg1); const argv = [_][]const u8{ cc_exe, arg1 }; @@ -584,17 +584,17 @@ fn printVerboseInvocation( if (!verbose) return; if (search_basename) |s| { - std.debug.warn("Zig attempted to find the file '{}' by executing this command:\n", .{s}); + std.debug.warn("Zig attempted to find the file '{s}' by executing this command:\n", .{s}); } else { std.debug.warn("Zig attempted to find the path to native system libc headers by executing this command:\n", .{}); } for (argv) |arg, i| { if (i != 0) std.debug.warn(" ", .{}); - std.debug.warn("{}", .{arg}); + std.debug.warn("{s}", .{arg}); } std.debug.warn("\n", .{}); if (stderr) |s| { - std.debug.warn("Output:\n==========\n{}\n==========\n", .{s}); + std.debug.warn("Output:\n==========\n{s}\n==========\n", .{s}); } } diff --git a/src/link.zig b/src/link.zig index 468f23bffd7e..3dbfb3b92202 100644 --- a/src/link.zig +++ b/src/link.zig @@ -523,7 +523,7 @@ pub const File = struct { id_symlink_basename, &prev_digest_buf, ) catch |err| b: { - log.debug("archive new_digest={} readFile error: {}", .{ digest, @errorName(err) }); + log.debug("archive new_digest={} readFile error: {s}", .{ digest, @errorName(err) }); break :b prev_digest_buf[0..0]; }; if (mem.eql(u8, prev_digest, &digest)) { @@ -560,9 +560,9 @@ pub const File = struct { const full_out_path_z = try arena.dupeZ(u8, full_out_path); if (base.options.verbose_link) { - std.debug.print("ar rcs {}", .{full_out_path_z}); + std.debug.print("ar rcs {s}", .{full_out_path_z}); for (object_files.items) |arg| { - std.debug.print(" {}", .{arg}); + std.debug.print(" {s}", .{arg}); } std.debug.print("\n", .{}); } @@ -574,11 +574,11 @@ pub const File = struct { if (!base.options.disable_lld_caching) { Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save archive hash digest file: {}", .{@errorName(err)}); + log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)}); }; man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when archiving: {}", .{@errorName(err)}); + log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)}); }; base.lock = man.toOwnedLock(); diff --git a/src/link/C.zig b/src/link/C.zig index 1059e52115a7..c3ab506b2cfb 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -112,7 +112,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { try writer.writeByte('\n'); } if (self.constants.items.len > 0) { - try writer.print("{}\n", .{self.constants.items}); + try writer.print("{s}\n", .{self.constants.items}); } if (self.main.items.len > 1) { const last_two = self.main.items[self.main.items.len - 2 ..]; diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 096fa2cd0bb4..117bdef4a758 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -686,7 +686,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { if (need_realloc) { const curr_vaddr = self.getDeclVAddr(decl); const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment); - log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr }); + log.debug("growing {s} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr }); if (vaddr != curr_vaddr) { log.debug(" (writing new offset table entry)\n", .{}); self.offset_table.items[decl.link.coff.offset_table_index] = vaddr; @@ -697,7 +697,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { } } else { const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment); - log.debug("allocated text block for {} at 0x{x} (size: {Bi})\n", .{ mem.spanZ(decl.name), vaddr, code.len }); + log.debug("allocated text block for {s} at 0x{x} (size: {Bi})\n", .{ mem.spanZ(decl.name), vaddr, code.len }); errdefer self.freeTextBlock(&decl.link.coff); self.offset_table.items[decl.link.coff.offset_table_index] = vaddr; try self.writeOffsetTableEntry(decl.link.coff.offset_table_index); @@ -811,8 +811,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { - const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; - if (use_stage1) { + // Both stage1 and stage2 LLVM backend put the object file in the cache directory. + if (self.base.options.use_llvm) { + // Stage2 has to call flushModule since that outputs the LLVM object file. + if (!build_options.is_stage1) try self.flushModule(comp); + const obj_basename = try std.zig.binNameAlloc(arena, .{ .root_name = self.base.options.root_name, .target = self.base.options.target, @@ -880,7 +883,7 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { id_symlink_basename, &prev_digest_buf, ) catch |err| blk: { - log.debug("COFF LLD new_digest={} error: {}", .{ digest, @errorName(err) }); + log.debug("COFF LLD new_digest={} error: {s}", .{ digest, @errorName(err) }); // Handle this as a cache miss. break :blk prev_digest_buf[0..0]; }; @@ -1236,11 +1239,11 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save linking hash digest file: {}", .{@errorName(err)}); + log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); }; // Again failure here only means an unnecessary cache miss. man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); }; // We hang on to this lock so that the output file path can be used without // other processes clobbering it. diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 4b2b95fc7220..b6b8fd775024 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1251,8 +1251,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { - const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; - if (use_stage1) { + // Both stage1 and stage2 LLVM backend put the object file in the cache directory. + if (self.base.options.use_llvm) { + // Stage2 has to call flushModule since that outputs the LLVM object file. + if (!build_options.is_stage1) try self.flushModule(comp); + const obj_basename = try std.zig.binNameAlloc(arena, .{ .root_name = self.base.options.root_name, .target = self.base.options.target, @@ -1362,7 +1365,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { id_symlink_basename, &prev_digest_buf, ) catch |err| blk: { - log.debug("ELF LLD new_digest={} error: {}", .{ digest, @errorName(err) }); + log.debug("ELF LLD new_digest={} error: {s}", .{ digest, @errorName(err) }); // Handle this as a cache miss. break :blk prev_digest_buf[0..0]; }; @@ -1396,7 +1399,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { if (self.base.options.output_mode == .Exe) { try argv.append("-z"); - try argv.append(try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size})); + try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size})); } if (self.base.options.image_base_override) |image_base| { @@ -1438,7 +1441,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { if (getLDMOption(target)) |ldm| { // Any target ELF will use the freebsd osabi if suffixed with "_fbsd". const arg = if (target.os.tag == .freebsd) - try std.fmt.allocPrint(arena, "{}_fbsd", .{ldm}) + try std.fmt.allocPrint(arena, "{s}_fbsd", .{ldm}) else ldm; try argv.append("-m"); @@ -1599,7 +1602,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { // (the check for that needs to be earlier), but they could be full paths to .so files, in which // case we want to avoid prepending "-l". const ext = Compilation.classifyFileExt(link_lib); - const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib}); + const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{s}", .{link_lib}); argv.appendAssumeCapacity(arg); } @@ -1733,11 +1736,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save linking hash digest file: {}", .{@errorName(err)}); + log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); }; // Again failure here only means an unnecessary cache miss. man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); }; // We hang on to this lock so that the output file path can be used without // other processes clobbering it. @@ -2082,10 +2085,10 @@ pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void { try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); if (self.local_symbol_free_list.popOrNull()) |i| { - log.debug("reusing symbol index {} for {}\n", .{ i, decl.name }); + log.debug("reusing symbol index {d} for {s}\n", .{ i, decl.name }); decl.link.elf.local_sym_index = i; } else { - log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name }); + log.debug("allocating symbol index {d} for {s}\n", .{ self.local_symbols.items.len, decl.name }); decl.link.elf.local_sym_index = @intCast(u32, self.local_symbols.items.len); _ = self.local_symbols.addOneAssumeCapacity(); } @@ -2178,16 +2181,6 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { else => false, }; if (is_fn) { - const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps; - if (zir_dumps.len != 0) { - for (zir_dumps) |fn_name| { - if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) { - std.debug.print("\n{}\n", .{decl.name}); - typed_value.val.castTag(.function).?.data.dump(module.*); - } - } - } - // For functions we need to add a prologue to the debug line program. try dbg_line_buffer.ensureCapacity(26); @@ -2300,7 +2293,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); if (need_realloc) { const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment); - log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr }); + log.debug("growing {s} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr }); if (vaddr != local_sym.st_value) { local_sym.st_value = vaddr; @@ -2322,7 +2315,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { const decl_name = mem.spanZ(decl.name); const name_str_index = try self.makeString(decl_name); const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment); - log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr }); + log.debug("allocated text block for {s} at 0x{x}\n", .{ decl_name, vaddr }); errdefer self.freeTextBlock(&decl.link.elf); local_sym.* = .{ @@ -2432,7 +2425,7 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) { const new_offset = self.findFreeSpace(needed_size, 1); const existing_size = last_src_fn.off; - log.debug("moving .debug_line section: {} bytes from 0x{x} to 0x{x}\n", .{ + log.debug("moving .debug_line section: {d} bytes from 0x{x} to 0x{x}\n", .{ existing_size, debug_line_sect.sh_offset, new_offset, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 6abbae2c268c..efb16e4d1c7e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -298,7 +298,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio self.base.file = file; // Create dSYM bundle. - const d_sym_path = try fmt.allocPrint(allocator, "{}.dSYM/Contents/Resources/DWARF/", .{sub_path}); + const d_sym_path = try fmt.allocPrint(allocator, "{s}.dSYM/Contents/Resources/DWARF/", .{sub_path}); defer allocator.free(d_sym_path); var d_sym_bundle = try options.emit.?.directory.handle.makeOpenPath(d_sym_path, .{}); defer d_sym_bundle.close(); @@ -520,7 +520,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { id_symlink_basename, &prev_digest_buf, ) catch |err| blk: { - log.debug("MachO LLD new_digest={} error: {}", .{ digest, @errorName(err) }); + log.debug("MachO LLD new_digest={} error: {s}", .{ digest, @errorName(err) }); // Handle this as a cache miss. break :blk prev_digest_buf[0..0]; }; @@ -620,7 +620,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { try argv.append(cur_vers); } - const dylib_install_name = try std.fmt.allocPrint(arena, "@rpath/{}", .{self.base.options.emit.?.sub_path}); + const dylib_install_name = try std.fmt.allocPrint(arena, "@rpath/{s}", .{self.base.options.emit.?.sub_path}); try argv.append("-install_name"); try argv.append(dylib_install_name); } @@ -706,7 +706,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which // case we want to avoid prepending "-l". const ext = Compilation.classifyFileExt(link_lib); - const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib}); + const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{s}", .{link_lib}); argv.appendAssumeCapacity(arg); } @@ -759,15 +759,15 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { self.base.allocator.free(result.stderr); } if (result.stdout.len != 0) { - log.warn("unexpected LD stdout: {}", .{result.stdout}); + log.warn("unexpected LD stdout: {s}", .{result.stdout}); } if (result.stderr.len != 0) { - log.warn("unexpected LD stderr: {}", .{result.stderr}); + log.warn("unexpected LD stderr: {s}", .{result.stderr}); } if (result.term != .Exited or result.term.Exited != 0) { // TODO parse this output and surface with the Compilation API rather than // directly outputting to stderr here. - log.err("{}", .{result.stderr}); + log.err("{s}", .{result.stderr}); return error.LDReportedFailure; } } else { @@ -980,11 +980,11 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save linking hash digest file: {}", .{@errorName(err)}); + log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)}); }; // Again failure here only means an unnecessary cache miss. man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); }; // We hang on to this lock so that the output file path can be used without // other processes clobbering it. @@ -1088,10 +1088,10 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); if (self.local_symbol_free_list.popOrNull()) |i| { - log.debug("reusing symbol index {} for {}", .{ i, decl.name }); + log.debug("reusing symbol index {d} for {s}", .{ i, decl.name }); decl.link.macho.local_sym_index = i; } else { - log.debug("allocating symbol index {} for {}", .{ self.local_symbols.items.len, decl.name }); + log.debug("allocating symbol index {d} for {s}", .{ self.local_symbols.items.len, decl.name }); decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len); _ = self.local_symbols.addOneAssumeCapacity(); } @@ -1165,7 +1165,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment); if (need_realloc) { const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment); - log.debug("growing {} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr }); + log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, symbol.n_value, vaddr }); if (vaddr != symbol.n_value) { symbol.n_value = vaddr; log.debug(" (writing new offset table entry)", .{}); @@ -1188,7 +1188,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { const decl_name = mem.spanZ(decl.name); const name_str_index = try self.makeString(decl_name); const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment); - log.debug("allocated text block for {} at 0x{x}", .{ decl_name, addr }); + log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, addr }); errdefer self.freeTextBlock(&decl.link.macho); symbol.* = .{ diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index c70fcc5825ce..11f87d5495ba 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -936,16 +936,6 @@ pub fn initDeclDebugBuffers( const typed_value = decl.typed_value.most_recent.typed_value; switch (typed_value.ty.zigTypeTag()) { .Fn => { - const zir_dumps = if (std.builtin.is_test) &[0][]const u8{} else build_options.zir_dumps; - if (zir_dumps.len != 0) { - for (zir_dumps) |fn_name| { - if (mem.eql(u8, mem.spanZ(decl.name), fn_name)) { - std.debug.print("\n{}\n", .{decl.name}); - typed_value.val.cast(Value.Payload.Function).?.func.dump(module.*); - } - } - } - // For functions we need to add a prologue to the debug line program. try dbg_line_buffer.ensureCapacity(26); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 374fa2230bb2..cbb3e83147a6 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -321,7 +321,7 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { id_symlink_basename, &prev_digest_buf, ) catch |err| blk: { - log.debug("WASM LLD new_digest={} error: {}", .{ digest, @errorName(err) }); + log.debug("WASM LLD new_digest={} error: {s}", .{ digest, @errorName(err) }); // Handle this as a cache miss. break :blk prev_digest_buf[0..0]; }; @@ -463,11 +463,11 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { - log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)}); + log.warn("failed to save linking hash digest symlink: {s}", .{@errorName(err)}); }; // Again failure here only means an unnecessary cache miss. man.writeManifest() catch |err| { - log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)}); }; // We hang on to this lock so that the output file path can be used without // other processes clobbering it. diff --git a/src/liveness.zig b/src/liveness.zig index 0d759f83123a..b0aafa28f114 100644 --- a/src/liveness.zig +++ b/src/liveness.zig @@ -1,6 +1,7 @@ const std = @import("std"); const ir = @import("ir.zig"); const trace = @import("tracy.zig").trace; +const log = std.log.scoped(.liveness); /// Perform Liveness Analysis over the `Body`. Each `Inst` will have its `deaths` field populated. pub fn analyze( @@ -248,5 +249,5 @@ fn analyzeInst( @panic("Handle liveness analysis for instructions with many parameters"); } - std.log.scoped(.liveness).debug("analyze {}: 0b{b}\n", .{ base.tag, base.deaths }); + log.debug("analyze {}: 0b{b}\n", .{ base.tag, base.deaths }); } diff --git a/src/llvm_backend.zig b/src/llvm_backend.zig index 294e6f540072..b0e59e3ffa0a 100644 --- a/src/llvm_backend.zig +++ b/src/llvm_backend.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const assert = std.debug.assert; const Allocator = std.mem.Allocator; const Compilation = @import("Compilation.zig"); const llvm = @import("llvm_bindings.zig"); @@ -132,7 +133,7 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { .macabi => "macabi", }; - return std.fmt.allocPrintZ(allocator, "{}-unknown-{}-{}", .{ llvm_arch, llvm_os, llvm_abi }); + return std.fmt.allocPrintZ(allocator, "{s}-unknown-{s}-{s}", .{ llvm_arch, llvm_os, llvm_abi }); } pub const LLVMIRModule = struct { @@ -141,17 +142,36 @@ pub const LLVMIRModule = struct { target_machine: *const llvm.TargetMachineRef, builder: *const llvm.BuilderRef, - output_path: []const u8, + object_path: []const u8, gpa: *Allocator, err_msg: ?*Compilation.ErrorMsg = null, + /// This stores the LLVM values used in a function, such that they can be + /// referred to in other instructions. This table is cleared before every function is generated. + func_inst_table: std.AutoHashMapUnmanaged(*Inst, *const llvm.ValueRef) = .{}, + + /// These fields are used to refer to the LLVM value of the function paramaters in an Arg instruction. + args: []*const llvm.ValueRef = &[_]*const llvm.ValueRef{}, + arg_index: usize = 0, + pub fn create(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*LLVMIRModule { const self = try allocator.create(LLVMIRModule); errdefer allocator.destroy(self); const gpa = options.module.?.gpa; + const obj_basename = try std.zig.binNameAlloc(gpa, .{ + .root_name = options.root_name, + .target = options.target, + .output_mode = .Obj, + }); + defer gpa.free(obj_basename); + + const o_directory = options.module.?.zig_cache_artifact_directory; + const object_path = try o_directory.join(gpa, &[_][]const u8{obj_basename}); + errdefer gpa.free(object_path); + initializeLLVMTargets(); const root_nameZ = try gpa.dupeZ(u8, options.root_name); @@ -203,7 +223,7 @@ pub const LLVMIRModule = struct { .llvm_module = llvm_module, .target_machine = target_machine, .builder = builder, - .output_path = sub_path, + .object_path = object_path, .gpa = gpa, }; return self; @@ -213,6 +233,10 @@ pub const LLVMIRModule = struct { self.builder.disposeBuilder(); self.target_machine.disposeTargetMachine(); self.llvm_module.disposeModule(); + + self.func_inst_table.deinit(self.gpa); + self.gpa.free(self.object_path); + allocator.destroy(self); } @@ -245,15 +269,13 @@ pub const LLVMIRModule = struct { } } - const output_pathZ = try self.gpa.dupeZ(u8, self.output_path); - defer self.gpa.free(output_pathZ); + const object_pathZ = try self.gpa.dupeZ(u8, self.object_path); + defer self.gpa.free(object_pathZ); var error_message: [*:0]const u8 = undefined; - // TODO: where to put the output object, zig-cache something? - // TODO: caching? if (self.target_machine.emitToFile( self.llvm_module, - output_pathZ.ptr, + object_pathZ.ptr, .ObjectFile, &error_message, )) { @@ -271,6 +293,7 @@ pub const LLVMIRModule = struct { error.CodegenFail => { decl.analysis = .codegen_failure; try module.failed_decls.put(module.gpa, decl, self.err_msg.?); + self.err_msg = null; return; }, else => |e| return e, @@ -278,47 +301,71 @@ pub const LLVMIRModule = struct { } fn gen(self: *LLVMIRModule, module: *Module, typed_value: TypedValue, src: usize) !void { - switch (typed_value.ty.zigTypeTag()) { - .Fn => { - const func = typed_value.val.castTag(.function).?.data; + if (typed_value.val.castTag(.function)) |func_inst| { + const func = func_inst.data; - const llvm_func = try self.resolveLLVMFunction(func); + const llvm_func = try self.resolveLLVMFunction(func, src); - // We remove all the basic blocks of a function to support incremental - // compilation! - // TODO: remove all basic blocks if functions can have more than one - if (llvm_func.getFirstBasicBlock()) |bb| { - bb.deleteBasicBlock(); - } + // This gets the LLVM values from the function and stores them in `self.args`. + const fn_param_len = func.owner_decl.typed_value.most_recent.typed_value.ty.fnParamLen(); + var args = try self.gpa.alloc(*const llvm.ValueRef, fn_param_len); + defer self.gpa.free(args); - const entry_block = llvm_func.appendBasicBlock("Entry"); - self.builder.positionBuilderAtEnd(entry_block); - - const instructions = func.analysis.success.instructions; - for (instructions) |inst| { - switch (inst.tag) { - .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), - .call => try self.genCall(inst.castTag(.call).?), - .unreach => self.genUnreach(inst.castTag(.unreach).?), - .retvoid => self.genRetVoid(inst.castTag(.retvoid).?), - .arg => self.genArg(inst.castTag(.arg).?), - .dbg_stmt => { - // TODO: implement debug info - }, - else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}), - } - } - }, - else => |ty| return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{ty}), + for (args) |*arg, i| { + arg.* = llvm.getParam(llvm_func, @intCast(c_uint, i)); + } + self.args = args; + self.arg_index = 0; + + // Make sure no other LLVM values from other functions can be referenced + self.func_inst_table.clearRetainingCapacity(); + + // We remove all the basic blocks of a function to support incremental + // compilation! + // TODO: remove all basic blocks if functions can have more than one + if (llvm_func.getFirstBasicBlock()) |bb| { + bb.deleteBasicBlock(); + } + + const entry_block = llvm_func.appendBasicBlock("Entry"); + self.builder.positionBuilderAtEnd(entry_block); + + const instructions = func.body.instructions; + for (instructions) |inst| { + const opt_llvm_val: ?*const llvm.ValueRef = switch (inst.tag) { + .add => try self.genAdd(inst.castTag(.add).?), + .alloc => try self.genAlloc(inst.castTag(.alloc).?), + .arg => try self.genArg(inst.castTag(.arg).?), + .bitcast => try self.genBitCast(inst.castTag(.bitcast).?), + .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), + .call => try self.genCall(inst.castTag(.call).?), + .intcast => try self.genIntCast(inst.castTag(.intcast).?), + .load => try self.genLoad(inst.castTag(.load).?), + .not => try self.genNot(inst.castTag(.not).?), + .ret => try self.genRet(inst.castTag(.ret).?), + .retvoid => self.genRetVoid(inst.castTag(.retvoid).?), + .store => try self.genStore(inst.castTag(.store).?), + .sub => try self.genSub(inst.castTag(.sub).?), + .unreach => self.genUnreach(inst.castTag(.unreach).?), + .dbg_stmt => blk: { + // TODO: implement debug info + break :blk null; + }, + else => |tag| return self.fail(src, "TODO implement LLVM codegen for Zir instruction: {}", .{tag}), + }; + if (opt_llvm_val) |llvm_val| try self.func_inst_table.putNoClobber(self.gpa, inst, llvm_val); + } + } else { + return self.fail(src, "TODO implement LLVM codegen for top-level decl type: {}", .{typed_value.ty}); } } - fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !void { + fn genCall(self: *LLVMIRModule, inst: *Inst.Call) !?*const llvm.ValueRef { if (inst.func.value()) |func_value| { if (func_value.castTag(.function)) |func_payload| { const func = func_payload.data; const zig_fn_type = func.owner_decl.typed_value.most_recent.typed_value.ty; - const llvm_fn = try self.resolveLLVMFunction(func); + const llvm_fn = try self.resolveLLVMFunction(func, inst.base.src); const num_args = inst.args.len; @@ -338,54 +385,165 @@ pub const LLVMIRModule = struct { "", ); - if (zig_fn_type.fnReturnType().zigTypeTag() == .NoReturn) { + const return_type = zig_fn_type.fnReturnType(); + if (return_type.tag() == .noreturn) { _ = self.builder.buildUnreachable(); } + + // No need to store the LLVM value if the return type is void or noreturn + if (!return_type.hasCodeGenBits()) return null; + + return call; } } + return self.fail(inst.base.src, "TODO implement calling runtime known function pointer LLVM backend", .{}); } - fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) void { + fn genRetVoid(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef { _ = self.builder.buildRetVoid(); + return null; + } + + fn genRet(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef { + _ = self.builder.buildRet(try self.resolveInst(inst.operand)); + return null; + } + + fn genNot(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef { + return self.builder.buildNot(try self.resolveInst(inst.operand), ""); } - fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) void { + fn genUnreach(self: *LLVMIRModule, inst: *Inst.NoOp) ?*const llvm.ValueRef { _ = self.builder.buildUnreachable(); + return null; } - fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) void { - // TODO: implement this + fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef { + const lhs = try self.resolveInst(inst.lhs); + const rhs = try self.resolveInst(inst.rhs); + + if (!inst.base.ty.isInt()) + return self.fail(inst.base.src, "TODO implement 'genAdd' for type {}", .{inst.base.ty}); + + return if (inst.base.ty.isSignedInt()) + self.builder.buildNSWAdd(lhs, rhs, "") + else + self.builder.buildNUWAdd(lhs, rhs, ""); } - fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !void { - // TODO: Store this function somewhere such that we dont have to add it again - const fn_type = llvm.TypeRef.functionType(llvm.voidType(), null, 0, false); - const func = self.llvm_module.addFunction("llvm.debugtrap", fn_type); - // TODO: add assertion: LLVMGetIntrinsicID - _ = self.builder.buildCall(func, null, 0, ""); + fn genSub(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef { + const lhs = try self.resolveInst(inst.lhs); + const rhs = try self.resolveInst(inst.rhs); + + if (!inst.base.ty.isInt()) + return self.fail(inst.base.src, "TODO implement 'genSub' for type {}", .{inst.base.ty}); + + return if (inst.base.ty.isSignedInt()) + self.builder.buildNSWSub(lhs, rhs, "") + else + self.builder.buildNUWSub(lhs, rhs, ""); + } + + fn genIntCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef { + const val = try self.resolveInst(inst.operand); + + const signed = inst.base.ty.isSignedInt(); + // TODO: Should we use intcast here or just a simple bitcast? + // LLVM does truncation vs bitcast (+signed extension) in the intcast depending on the sizes + return self.builder.buildIntCast2(val, try self.getLLVMType(inst.base.ty, inst.base.src), signed, ""); + } + + fn genBitCast(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef { + const val = try self.resolveInst(inst.operand); + const dest_type = try self.getLLVMType(inst.base.ty, inst.base.src); + + return self.builder.buildBitCast(val, dest_type, ""); + } + + fn genArg(self: *LLVMIRModule, inst: *Inst.Arg) !?*const llvm.ValueRef { + const arg_val = self.args[self.arg_index]; + self.arg_index += 1; + + const ptr_val = self.builder.buildAlloca(try self.getLLVMType(inst.base.ty, inst.base.src), ""); + _ = self.builder.buildStore(arg_val, ptr_val); + return self.builder.buildLoad(ptr_val, ""); + } + + fn genAlloc(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.ValueRef { + // buildAlloca expects the pointee type, not the pointer type, so assert that + // a Payload.PointerSimple is passed to the alloc instruction. + const pointee_type = inst.base.ty.castPointer().?.data; + + // TODO: figure out a way to get the name of the var decl. + // TODO: set alignment and volatile + return self.builder.buildAlloca(try self.getLLVMType(pointee_type, inst.base.src), ""); + } + + fn genStore(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.ValueRef { + const val = try self.resolveInst(inst.rhs); + const ptr = try self.resolveInst(inst.lhs); + _ = self.builder.buildStore(val, ptr); + return null; + } + + fn genLoad(self: *LLVMIRModule, inst: *Inst.UnOp) !?*const llvm.ValueRef { + const ptr_val = try self.resolveInst(inst.operand); + return self.builder.buildLoad(ptr_val, ""); + } + + fn genBreakpoint(self: *LLVMIRModule, inst: *Inst.NoOp) !?*const llvm.ValueRef { + const llvn_fn = self.getIntrinsic("llvm.debugtrap"); + _ = self.builder.buildCall(llvn_fn, null, 0, ""); + return null; + } + + fn getIntrinsic(self: *LLVMIRModule, name: []const u8) *const llvm.ValueRef { + const id = llvm.lookupIntrinsicID(name.ptr, name.len); + assert(id != 0); + // TODO: add support for overload intrinsics by passing the prefix of the intrinsic + // to `lookupIntrinsicID` and then passing the correct types to + // `getIntrinsicDeclaration` + return self.llvm_module.getIntrinsicDeclaration(id, null, 0); } fn resolveInst(self: *LLVMIRModule, inst: *ir.Inst) !*const llvm.ValueRef { - if (inst.castTag(.constant)) |const_inst| { - return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val }); + if (inst.value()) |val| { + return self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = val }); } - return self.fail(inst.src, "TODO implement resolveInst", .{}); + if (self.func_inst_table.get(inst)) |value| return value; + + return self.fail(inst.src, "TODO implement global llvm values (or the value is not in the func_inst_table table)", .{}); } - fn genTypedValue(self: *LLVMIRModule, src: usize, typed_value: TypedValue) !*const llvm.ValueRef { - const llvm_type = self.getLLVMType(typed_value.ty); + fn genTypedValue(self: *LLVMIRModule, src: usize, tv: TypedValue) !*const llvm.ValueRef { + const llvm_type = try self.getLLVMType(tv.ty, src); - if (typed_value.val.isUndef()) + if (tv.val.isUndef()) return llvm_type.getUndef(); - switch (typed_value.ty.zigTypeTag()) { - .Bool => return if (typed_value.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(), - else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}), + switch (tv.ty.zigTypeTag()) { + .Bool => return if (tv.val.toBool()) llvm_type.constAllOnes() else llvm_type.constNull(), + .Int => { + var bigint_space: Value.BigIntSpace = undefined; + const bigint = tv.val.toBigInt(&bigint_space); + + if (bigint.eqZero()) return llvm_type.constNull(); + + if (bigint.limbs.len != 1) { + return self.fail(src, "TODO implement bigger bigint", .{}); + } + const llvm_int = llvm_type.constInt(bigint.limbs[0], false); + if (!bigint.positive) { + return llvm.constNeg(llvm_int); + } + return llvm_int; + }, + else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}), } } /// If the llvm function does not exist, create it - fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn) !*const llvm.ValueRef { + fn resolveLLVMFunction(self: *LLVMIRModule, func: *Module.Fn, src: usize) !*const llvm.ValueRef { // TODO: do we want to store this in our own datastructure? if (self.llvm_module.getNamedFunction(func.owner_decl.name)) |llvm_fn| return llvm_fn; @@ -402,25 +560,25 @@ pub const LLVMIRModule = struct { defer self.gpa.free(llvm_param); for (fn_param_types) |fn_param, i| { - llvm_param[i] = self.getLLVMType(fn_param); + llvm_param[i] = try self.getLLVMType(fn_param, src); } const fn_type = llvm.TypeRef.functionType( - self.getLLVMType(return_type), + try self.getLLVMType(return_type, src), if (fn_param_len == 0) null else llvm_param.ptr, @intCast(c_uint, fn_param_len), false, ); const llvm_fn = self.llvm_module.addFunction(func.owner_decl.name, fn_type); - if (return_type.zigTypeTag() == .NoReturn) { + if (return_type.tag() == .noreturn) { llvm_fn.addFnAttr("noreturn"); } return llvm_fn; } - fn getLLVMType(self: *LLVMIRModule, t: Type) *const llvm.TypeRef { + fn getLLVMType(self: *LLVMIRModule, t: Type, src: usize) error{ OutOfMemory, CodegenFail }!*const llvm.TypeRef { switch (t.zigTypeTag()) { .Void => return llvm.voidType(), .NoReturn => return llvm.voidType(), @@ -429,13 +587,18 @@ pub const LLVMIRModule = struct { return llvm.intType(info.bits); }, .Bool => return llvm.intType(1), - else => unreachable, + .Pointer => { + const pointer = t.castPointer().?; + const elem_type = try self.getLLVMType(pointer.data, src); + return elem_type.pointerType(0); + }, + else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}), } } pub fn fail(self: *LLVMIRModule, src: usize, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { @setCold(true); - std.debug.assert(self.err_msg == null); + assert(self.err_msg == null); self.err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args); return error.CodegenFail; } diff --git a/src/llvm_bindings.zig b/src/llvm_bindings.zig index 388acc4ba2a1..aa9553732850 100644 --- a/src/llvm_bindings.zig +++ b/src/llvm_bindings.zig @@ -43,8 +43,14 @@ pub const TypeRef = opaque { pub const constAllOnes = LLVMConstAllOnes; extern fn LLVMConstAllOnes(Ty: *const TypeRef) *const ValueRef; + pub const constInt = LLVMConstInt; + extern fn LLVMConstInt(IntTy: *const TypeRef, N: c_ulonglong, SignExtend: LLVMBool) *const ValueRef; + pub const getUndef = LLVMGetUndef; extern fn LLVMGetUndef(Ty: *const TypeRef) *const ValueRef; + + pub const pointerType = LLVMPointerType; + extern fn LLVMPointerType(ElementType: *const TypeRef, AddressSpace: c_uint) *const TypeRef; }; pub const ModuleRef = opaque { @@ -63,10 +69,16 @@ pub const ModuleRef = opaque { pub const getNamedFunction = LLVMGetNamedFunction; extern fn LLVMGetNamedFunction(*const ModuleRef, Name: [*:0]const u8) ?*const ValueRef; + pub const getIntrinsicDeclaration = LLVMGetIntrinsicDeclaration; + extern fn LLVMGetIntrinsicDeclaration(Mod: *const ModuleRef, ID: c_uint, ParamTypes: ?[*]*const TypeRef, ParamCount: usize) *const ValueRef; + pub const printToString = LLVMPrintModuleToString; extern fn LLVMPrintModuleToString(*const ModuleRef) [*:0]const u8; }; +pub const lookupIntrinsicID = LLVMLookupIntrinsicID; +extern fn LLVMLookupIntrinsicID(Name: [*]const u8, NameLen: usize) c_uint; + pub const disposeMessage = LLVMDisposeMessage; extern fn LLVMDisposeMessage(Message: [*:0]const u8) void; @@ -76,9 +88,15 @@ pub const VerifierFailureAction = extern enum { ReturnStatus, }; +pub const constNeg = LLVMConstNeg; +extern fn LLVMConstNeg(ConstantVal: *const ValueRef) *const ValueRef; + pub const voidType = LLVMVoidType; extern fn LLVMVoidType() *const TypeRef; +pub const getParam = LLVMGetParam; +extern fn LLVMGetParam(Fn: *const ValueRef, Index: c_uint) *const ValueRef; + pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName; extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint; @@ -117,11 +135,41 @@ pub const BuilderRef = opaque { pub const buildRetVoid = LLVMBuildRetVoid; extern fn LLVMBuildRetVoid(*const BuilderRef) *const ValueRef; + pub const buildRet = LLVMBuildRet; + extern fn LLVMBuildRet(*const BuilderRef, V: *const ValueRef) *const ValueRef; + pub const buildUnreachable = LLVMBuildUnreachable; extern fn LLVMBuildUnreachable(*const BuilderRef) *const ValueRef; pub const buildAlloca = LLVMBuildAlloca; extern fn LLVMBuildAlloca(*const BuilderRef, Ty: *const TypeRef, Name: [*:0]const u8) *const ValueRef; + + pub const buildStore = LLVMBuildStore; + extern fn LLVMBuildStore(*const BuilderRef, Val: *const ValueRef, Ptr: *const ValueRef) *const ValueRef; + + pub const buildLoad = LLVMBuildLoad; + extern fn LLVMBuildLoad(*const BuilderRef, PointerVal: *const ValueRef, Name: [*:0]const u8) *const ValueRef; + + pub const buildNot = LLVMBuildNot; + extern fn LLVMBuildNot(*const BuilderRef, V: *const ValueRef, Name: [*:0]const u8) *const ValueRef; + + pub const buildNSWAdd = LLVMBuildNSWAdd; + extern fn LLVMBuildNSWAdd(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef; + + pub const buildNUWAdd = LLVMBuildNUWAdd; + extern fn LLVMBuildNUWAdd(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef; + + pub const buildNSWSub = LLVMBuildNSWSub; + extern fn LLVMBuildNSWSub(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef; + + pub const buildNUWSub = LLVMBuildNUWSub; + extern fn LLVMBuildNUWSub(*const BuilderRef, LHS: *const ValueRef, RHS: *const ValueRef, Name: [*:0]const u8) *const ValueRef; + + pub const buildIntCast2 = LLVMBuildIntCast2; + extern fn LLVMBuildIntCast2(*const BuilderRef, Val: *const ValueRef, DestTy: *const TypeRef, IsSigned: LLVMBool, Name: [*:0]const u8) *const ValueRef; + + pub const buildBitCast = LLVMBuildBitCast; + extern fn LLVMBuildBitCast(*const BuilderRef, Val: *const ValueRef, DestTy: *const TypeRef, Name: [*:0]const u8) *const ValueRef; }; pub const BasicBlockRef = opaque { diff --git a/src/main.zig b/src/main.zig index 247f8327f0f9..f026b6b4b484 100644 --- a/src/main.zig +++ b/src/main.zig @@ -104,7 +104,10 @@ pub fn log( var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; pub fn main() anyerror!void { - const gpa = if (std.builtin.link_libc) std.heap.raw_c_allocator else &general_purpose_allocator.allocator; + const gpa = if (std.builtin.link_libc) + std.heap.raw_c_allocator + else + &general_purpose_allocator.allocator; defer if (!std.builtin.link_libc) { _ = general_purpose_allocator.deinit(); }; @@ -118,7 +121,7 @@ pub fn main() anyerror!void { pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !void { if (args.len <= 1) { - std.log.info("{}", .{usage}); + std.log.info("{s}", .{usage}); fatal("expected command argument", .{}); } @@ -204,8 +207,8 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) { try io.getStdOut().writeAll(usage); } else { - std.log.info("{}", .{usage}); - fatal("unknown command: {}", .{args[1]}); + std.log.info("{s}", .{usage}); + fatal("unknown command: {s}", .{args[1]}); } } @@ -332,7 +335,7 @@ const usage_build_generic = \\ -dynamic Force output to be dynamically linked \\ -static Force output to be statically linked \\ -Bsymbolic Bind global references locally - \\ --subsystem [subsystem] (Windows) /SUBSYSTEM: to the linker\n" + \\ --subsystem [subsystem] (Windows) /SUBSYSTEM: to the linker \\ --stack [size] Override default stack size \\ --image-base [addr] Set base address for executable image \\ -framework [name] (Darwin) link against framework @@ -615,7 +618,7 @@ fn buildOutputType( fatal("unexpected end-of-parameter mark: --", .{}); } } else if (mem.eql(u8, arg, "--pkg-begin")) { - if (i + 2 >= args.len) fatal("Expected 2 arguments after {}", .{arg}); + if (i + 2 >= args.len) fatal("Expected 2 arguments after {s}", .{arg}); i += 1; const pkg_name = args[i]; i += 1; @@ -626,7 +629,7 @@ fn buildOutputType( fs.path.dirname(pkg_path), fs.path.basename(pkg_path), ) catch |err| { - fatal("Failed to add package at path {}: {}", .{ pkg_path, @errorName(err) }); + fatal("Failed to add package at path {s}: {s}", .{ pkg_path, @errorName(err) }); }; new_cur_pkg.parent = cur_pkg; try cur_pkg.add(gpa, pkg_name, new_cur_pkg); @@ -635,7 +638,7 @@ fn buildOutputType( cur_pkg = cur_pkg.parent orelse fatal("encountered --pkg-end with no matching --pkg-begin", .{}); } else if (mem.eql(u8, arg, "--main-pkg-path")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; main_pkg_path = args[i]; } else if (mem.eql(u8, arg, "-cflags")) { @@ -653,10 +656,10 @@ fn buildOutputType( i += 1; const next_arg = args[i]; color = std.meta.stringToEnum(Color, next_arg) orelse { - fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg}); + fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); }; } else if (mem.eql(u8, arg, "--subsystem")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; if (mem.eql(u8, args[i], "console")) { subsystem = .Console; @@ -689,51 +692,51 @@ fn buildOutputType( }); } } else if (mem.eql(u8, arg, "-O")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; optimize_mode_string = args[i]; } else if (mem.eql(u8, arg, "--stack")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; stack_size_override = std.fmt.parseUnsigned(u64, args[i], 0) catch |err| { - fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; } else if (mem.eql(u8, arg, "--image-base")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; image_base_override = std.fmt.parseUnsigned(u64, args[i], 0) catch |err| { - fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; } else if (mem.eql(u8, arg, "--name")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; provided_name = args[i]; } else if (mem.eql(u8, arg, "-rpath")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; try rpath_list.append(args[i]); } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; try lib_dirs.append(args[i]); } else if (mem.eql(u8, arg, "-F")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; try framework_dirs.append(args[i]); } else if (mem.eql(u8, arg, "-framework")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; try frameworks.append(args[i]); } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; linker_script = args[i]; } else if (mem.eql(u8, arg, "--version-script")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; version_script = args[i]; } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); // We don't know whether this library is part of libc or libc++ until we resolve the target. // So we simply append to the list for now. i += 1; @@ -743,7 +746,7 @@ fn buildOutputType( mem.eql(u8, arg, "-I") or mem.eql(u8, arg, "-dirafter")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; try clang_argv.append(arg); try clang_argv.append(args[i]); @@ -753,19 +756,19 @@ fn buildOutputType( } i += 1; version = std.builtin.Version.parse(args[i]) catch |err| { - fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) }); + fatal("unable to parse --version '{s}': {s}", .{ args[i], @errorName(err) }); }; have_version = true; } else if (mem.eql(u8, arg, "-target")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; target_arch_os_abi = args[i]; } else if (mem.eql(u8, arg, "-mcpu")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; target_mcpu = args[i]; } else if (mem.eql(u8, arg, "-mcmodel")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; machine_code_model = parseCodeModel(args[i]); } else if (mem.startsWith(u8, arg, "-ofmt=")) { @@ -777,35 +780,35 @@ fn buildOutputType( } else if (mem.startsWith(u8, arg, "-O")) { optimize_mode_string = arg["-O".len..]; } else if (mem.eql(u8, arg, "--dynamic-linker")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; target_dynamic_linker = args[i]; } else if (mem.eql(u8, arg, "--libc")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; libc_paths_file = args[i]; } else if (mem.eql(u8, arg, "--test-filter")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; test_filter = args[i]; } else if (mem.eql(u8, arg, "--test-name-prefix")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; test_name_prefix = args[i]; } else if (mem.eql(u8, arg, "--test-cmd")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; try test_exec_args.append(args[i]); } else if (mem.eql(u8, arg, "--cache-dir")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; override_local_cache_dir = args[i]; } else if (mem.eql(u8, arg, "--global-cache-dir")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; override_global_cache_dir = args[i]; } else if (mem.eql(u8, arg, "--override-lib-dir")) { - if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg}); i += 1; override_lib_dir = args[i]; } else if (mem.eql(u8, arg, "-fcompiler-rt")) { @@ -968,7 +971,7 @@ fn buildOutputType( { try clang_argv.append(arg); } else { - fatal("unrecognized parameter: '{}'", .{arg}); + fatal("unrecognized parameter: '{s}'", .{arg}); } } else switch (Compilation.classifyFileExt(arg)) { .object, .static_library, .shared_library => { @@ -982,19 +985,19 @@ fn buildOutputType( }, .zig, .zir => { if (root_src_file) |other| { - fatal("found another zig file '{}' after root source file '{}'", .{ arg, other }); + fatal("found another zig file '{s}' after root source file '{s}'", .{ arg, other }); } else { root_src_file = arg; } }, .unknown => { - fatal("unrecognized file extension of parameter '{}'", .{arg}); + fatal("unrecognized file extension of parameter '{s}'", .{arg}); }, } } if (optimize_mode_string) |s| { optimize_mode = std.meta.stringToEnum(std.builtin.Mode, s) orelse - fatal("unrecognized optimization mode: '{}'", .{s}); + fatal("unrecognized optimization mode: '{s}'", .{s}); } }, .cc, .cpp => { @@ -1018,7 +1021,7 @@ fn buildOutputType( var it = ClangArgIterator.init(arena, all_args); while (it.has_next) { it.next() catch |err| { - fatal("unable to parse command line parameters: {}", .{@errorName(err)}); + fatal("unable to parse command line parameters: {s}", .{@errorName(err)}); }; switch (it.zig_equivalent) { .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown @@ -1038,7 +1041,7 @@ fn buildOutputType( }, .zig, .zir => { if (root_src_file) |other| { - fatal("found another zig file '{}' after root source file '{}'", .{ it.only_arg, other }); + fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other }); } else { root_src_file = it.only_arg; } @@ -1153,7 +1156,7 @@ fn buildOutputType( if (mem.eql(u8, arg, "-soname")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } const name = linker_args.items[i]; soname = .{ .yes = name }; @@ -1185,7 +1188,7 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-rpath")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } try rpath_list.append(linker_args.items[i]); } else if (mem.eql(u8, arg, "-I") or @@ -1194,7 +1197,7 @@ fn buildOutputType( { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } target_dynamic_linker = linker_args.items[i]; } else if (mem.eql(u8, arg, "-E") or @@ -1205,7 +1208,7 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "--version-script")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } version_script = linker_args.items[i]; } else if (mem.startsWith(u8, arg, "-O")) { @@ -1227,7 +1230,7 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-z")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } const z_arg = linker_args.items[i]; if (mem.eql(u8, z_arg, "nodelete")) { @@ -1235,44 +1238,44 @@ fn buildOutputType( } else if (mem.eql(u8, z_arg, "defs")) { linker_z_defs = true; } else { - warn("unsupported linker arg: -z {}", .{z_arg}); + warn("unsupported linker arg: -z {s}", .{z_arg}); } } else if (mem.eql(u8, arg, "--major-image-version")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } version.major = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| { - fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; have_version = true; } else if (mem.eql(u8, arg, "--minor-image-version")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } version.minor = std.fmt.parseUnsigned(u32, linker_args.items[i], 10) catch |err| { - fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; have_version = true; } else if (mem.eql(u8, arg, "--stack")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } stack_size_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| { - fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; } else if (mem.eql(u8, arg, "--image-base")) { i += 1; if (i >= linker_args.items.len) { - fatal("expected linker arg after '{}'", .{arg}); + fatal("expected linker arg after '{s}'", .{arg}); } image_base_override = std.fmt.parseUnsigned(u64, linker_args.items[i], 0) catch |err| { - fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; } else { - warn("unsupported linker arg: {}", .{arg}); + warn("unsupported linker arg: {s}", .{arg}); } } @@ -1328,7 +1331,7 @@ fn buildOutputType( } if (arg_mode == .translate_c and c_source_files.items.len != 1) { - fatal("translate-c expects exactly 1 source file (found {})", .{c_source_files.items.len}); + fatal("translate-c expects exactly 1 source file (found {d})", .{c_source_files.items.len}); } if (root_src_file == null and arg_mode == .zig_test) { @@ -1373,25 +1376,25 @@ fn buildOutputType( help: { var help_text = std.ArrayList(u8).init(arena); for (diags.arch.?.allCpuModels()) |cpu| { - help_text.writer().print(" {}\n", .{cpu.name}) catch break :help; + help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help; } - std.log.info("Available CPUs for architecture '{}': {}", .{ + std.log.info("Available CPUs for architecture '{s}': {s}", .{ @tagName(diags.arch.?), help_text.items, }); } - fatal("Unknown CPU: '{}'", .{diags.cpu_name.?}); + fatal("Unknown CPU: '{s}'", .{diags.cpu_name.?}); }, error.UnknownCpuFeature => { help: { var help_text = std.ArrayList(u8).init(arena); for (diags.arch.?.allFeaturesList()) |feature| { - help_text.writer().print(" {}: {}\n", .{ feature.name, feature.description }) catch break :help; + help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help; } - std.log.info("Available CPU features for architecture '{}': {}", .{ + std.log.info("Available CPU features for architecture '{s}': {s}", .{ @tagName(diags.arch.?), help_text.items, }); } - fatal("Unknown CPU feature: '{}'", .{diags.unknown_feature_name}); + fatal("Unknown CPU feature: '{s}'", .{diags.unknown_feature_name}); }, else => |e| return e, }; @@ -1431,16 +1434,40 @@ fn buildOutputType( if (cross_target.isNativeOs() and (system_libs.items.len != 0 or want_native_include_dirs)) { const paths = std.zig.system.NativePaths.detect(arena) catch |err| { - fatal("unable to detect native system paths: {}", .{@errorName(err)}); + fatal("unable to detect native system paths: {s}", .{@errorName(err)}); }; for (paths.warnings.items) |warning| { - warn("{}", .{warning}); + warn("{s}", .{warning}); } + + const has_sysroot = if (comptime std.Target.current.isDarwin()) outer: { + const at_least_big_sur = target_info.target.os.getVersionRange().semver.min.major >= 11; + if (at_least_big_sur) { + const sdk_path = try std.zig.system.getSDKPath(arena); + try clang_argv.ensureCapacity(clang_argv.items.len + 2); + clang_argv.appendAssumeCapacity("-isysroot"); + clang_argv.appendAssumeCapacity(sdk_path); + break :outer true; + } + break :outer false; + } else false; + try clang_argv.ensureCapacity(clang_argv.items.len + paths.include_dirs.items.len * 2); + const isystem_flag = if (has_sysroot) "-iwithsysroot" else "-isystem"; for (paths.include_dirs.items) |include_dir| { - clang_argv.appendAssumeCapacity("-isystem"); + clang_argv.appendAssumeCapacity(isystem_flag); clang_argv.appendAssumeCapacity(include_dir); } + + try clang_argv.ensureCapacity(clang_argv.items.len + paths.framework_dirs.items.len * 2); + try framework_dirs.ensureCapacity(framework_dirs.items.len + paths.framework_dirs.items.len); + const iframework_flag = if (has_sysroot) "-iframeworkwithsysroot" else "-iframework"; + for (paths.framework_dirs.items) |framework_dir| { + clang_argv.appendAssumeCapacity(iframework_flag); + clang_argv.appendAssumeCapacity(framework_dir); + framework_dirs.appendAssumeCapacity(framework_dir); + } + for (paths.lib_dirs.items) |lib_dir| { try lib_dirs.append(lib_dir); } @@ -1468,7 +1495,7 @@ fn buildOutputType( } else if (mem.eql(u8, ofmt, "raw")) { break :blk .raw; } else { - fatal("unsupported object format: {}", .{ofmt}); + fatal("unsupported object format: {s}", .{ofmt}); } }; @@ -1538,7 +1565,7 @@ fn buildOutputType( } if (fs.path.dirname(full_path)) |dirname| { const handle = fs.cwd().openDir(dirname, .{}) catch |err| { - fatal("unable to open output directory '{}': {}", .{ dirname, @errorName(err) }); + fatal("unable to open output directory '{s}': {s}", .{ dirname, @errorName(err) }); }; cleanup_emit_bin_dir = handle; break :b Compilation.EmitLoc{ @@ -1561,19 +1588,19 @@ fn buildOutputType( }, }; - const default_h_basename = try std.fmt.allocPrint(arena, "{}.h", .{root_name}); + const default_h_basename = try std.fmt.allocPrint(arena, "{s}.h", .{root_name}); var emit_h_resolved = try emit_h.resolve(default_h_basename); defer emit_h_resolved.deinit(); - const default_asm_basename = try std.fmt.allocPrint(arena, "{}.s", .{root_name}); + const default_asm_basename = try std.fmt.allocPrint(arena, "{s}.s", .{root_name}); var emit_asm_resolved = try emit_asm.resolve(default_asm_basename); defer emit_asm_resolved.deinit(); - const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{}.ll", .{root_name}); + const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{s}.ll", .{root_name}); var emit_llvm_ir_resolved = try emit_llvm_ir.resolve(default_llvm_ir_basename); defer emit_llvm_ir_resolved.deinit(); - const default_analysis_basename = try std.fmt.allocPrint(arena, "{}-analysis.json", .{root_name}); + const default_analysis_basename = try std.fmt.allocPrint(arena, "{s}-analysis.json", .{root_name}); var emit_analysis_resolved = try emit_analysis.resolve(default_analysis_basename); defer emit_analysis_resolved.deinit(); @@ -1585,10 +1612,10 @@ fn buildOutputType( .yes_default_path => blk: { if (root_src_file) |rsf| { if (mem.endsWith(u8, rsf, ".zir")) { - break :blk try std.fmt.allocPrint(arena, "{}.out.zir", .{root_name}); + break :blk try std.fmt.allocPrint(arena, "{s}.out.zir", .{root_name}); } } - break :blk try std.fmt.allocPrint(arena, "{}.zir", .{root_name}); + break :blk try std.fmt.allocPrint(arena, "{s}.zir", .{root_name}); }, .yes => |p| p, }; @@ -1618,7 +1645,7 @@ fn buildOutputType( } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { - fatal("unable to find zig installation directory: {}", .{@errorName(err)}); + fatal("unable to find zig installation directory: {s}", .{@errorName(err)}); }; defer zig_lib_directory.handle.close(); @@ -1631,7 +1658,7 @@ fn buildOutputType( if (libc_paths_file) |paths_file| { libc_installation = LibCInstallation.parse(gpa, paths_file) catch |err| { - fatal("unable to parse libc paths file: {}", .{@errorName(err)}); + fatal("unable to parse libc paths file: {s}", .{@errorName(err)}); }; } @@ -1767,7 +1794,7 @@ fn buildOutputType( .disable_lld_caching = !have_enable_cache, .subsystem = subsystem, }) catch |err| { - fatal("unable to create compilation: {}", .{@errorName(err)}); + fatal("unable to create compilation: {s}", .{@errorName(err)}); }; var comp_destroyed = false; defer if (!comp_destroyed) comp.destroy(); @@ -1794,7 +1821,7 @@ fn buildOutputType( }; updateModule(gpa, comp, zir_out_path, hook) catch |err| switch (err) { - error.SemanticAnalyzeFail => process.exit(1), + error.SemanticAnalyzeFail => if (!watch) process.exit(1), else => |e| return e, }; try comp.makeBinFileExecutable(); @@ -1829,9 +1856,16 @@ fn buildOutputType( else => unreachable, } } - try argv.appendSlice(&[_][]const u8{ - exe_path, self_exe_path, - }); + // when testing pass the zig_exe_path to argv + if (arg_mode == .zig_test) + try argv.appendSlice(&[_][]const u8{ + exe_path, self_exe_path, + }) + // when running just pass the current exe + else + try argv.appendSlice(&[_][]const u8{ + exe_path, + }); } else { for (test_exec_args.items) |arg| { if (arg) |a| { @@ -1890,12 +1924,12 @@ fn buildOutputType( if (!watch) return cleanExit(); } else { const cmd = try argvCmd(arena, argv.items); - fatal("the following test command failed with exit code {}:\n{}", .{ code, cmd }); + fatal("the following test command failed with exit code {d}:\n{s}", .{ code, cmd }); } }, else => { const cmd = try argvCmd(arena, argv.items); - fatal("the following test command crashed:\n{}", .{cmd}); + fatal("the following test command crashed:\n{s}", .{cmd}); }, } }, @@ -1912,7 +1946,7 @@ fn buildOutputType( try stderr.print("(zig) ", .{}); try comp.makeBinFileExecutable(); if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| { - try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)}); + try stderr.print("\nUnable to parse command: {s}\n", .{@errorName(err)}); continue; }) |line| { const actual_line = mem.trimRight(u8, line, "\r\n "); @@ -1930,7 +1964,7 @@ fn buildOutputType( } else if (mem.eql(u8, actual_line, "help")) { try stderr.writeAll(repl_help); } else { - try stderr.print("unknown command: {}\n", .{actual_line}); + try stderr.print("unknown command: {s}\n", .{actual_line}); } } else { break; @@ -1988,14 +2022,14 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi assert(comp.c_source_files.len == 1); const c_source_file = comp.c_source_files[0]; - const translated_zig_basename = try std.fmt.allocPrint(arena, "{}.zig", .{comp.bin_file.options.root_name}); + const translated_zig_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.bin_file.options.root_name}); var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); defer if (enable_cache) man.deinit(); man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects _ = man.addFile(c_source_file.src_path, null) catch |err| { - fatal("unable to process '{}': {}", .{ c_source_file.src_path, @errorName(err) }); + fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) }); }; const digest = if (try man.hit()) man.final() else digest: { @@ -2010,7 +2044,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi break :blk null; const c_src_basename = fs.path.basename(c_source_file.src_path); - const dep_basename = try std.fmt.allocPrint(arena, "{}.d", .{c_src_basename}); + const dep_basename = try std.fmt.allocPrint(arena, "{s}.d", .{c_src_basename}); const out_dep_path = try comp.tmpFilePath(arena, dep_basename); break :blk out_dep_path; }; @@ -2045,7 +2079,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}), error.SemanticAnalyzeFail => { for (clang_errors) |clang_err| { - std.debug.print("{}:{}:{}: {}\n", .{ + std.debug.print("{s}:{d}:{d}: {s}\n", .{ if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", clang_err.line + 1, clang_err.column + 1, @@ -2063,7 +2097,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); // Just to save disk space, we delete the file because it is never needed again. zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { - warn("failed to delete '{}': {}", .{ dep_file_path, @errorName(err) }); + warn("failed to delete '{s}': {s}", .{ dep_file_path, @errorName(err) }); }; } @@ -2078,7 +2112,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi _ = try std.zig.render(comp.gpa, bos.writer(), tree); try bos.flush(); - man.writeManifest() catch |err| warn("failed to write cache manifest: {}", .{@errorName(err)}); + man.writeManifest() catch |err| warn("failed to write cache manifest: {s}", .{@errorName(err)}); break :digest digest; }; @@ -2087,7 +2121,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename, }); - try io.getStdOut().writer().print("{}\n", .{full_zig_path}); + try io.getStdOut().writer().print("{s}\n", .{full_zig_path}); return cleanExit(); } else { const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename }); @@ -2124,10 +2158,10 @@ pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void { try stdout.writeAll(usage_libc); return cleanExit(); } else { - fatal("unrecognized parameter: '{}'", .{arg}); + fatal("unrecognized parameter: '{s}'", .{arg}); } } else if (input_file != null) { - fatal("unexpected extra parameter: '{}'", .{arg}); + fatal("unexpected extra parameter: '{s}'", .{arg}); } else { input_file = arg; } @@ -2135,7 +2169,7 @@ pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void { } if (input_file) |libc_file| { var libc = LibCInstallation.parse(gpa, libc_file) catch |err| { - fatal("unable to parse libc file: {}", .{@errorName(err)}); + fatal("unable to parse libc file: {s}", .{@errorName(err)}); }; defer libc.deinit(gpa); } else { @@ -2143,7 +2177,7 @@ pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void { .allocator = gpa, .verbose = true, }) catch |err| { - fatal("unable to detect native libc: {}", .{@errorName(err)}); + fatal("unable to detect native libc: {s}", .{@errorName(err)}); }; defer libc.deinit(gpa); @@ -2181,16 +2215,16 @@ pub fn cmdInit( try io.getStdOut().writeAll(usage_init); return cleanExit(); } else { - fatal("unrecognized parameter: '{}'", .{arg}); + fatal("unrecognized parameter: '{s}'", .{arg}); } } else { - fatal("unexpected extra parameter: '{}'", .{arg}); + fatal("unexpected extra parameter: '{s}'", .{arg}); } } } const self_exe_path = try fs.selfExePathAlloc(arena); var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { - fatal("unable to find zig installation directory: {}\n", .{@errorName(err)}); + fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)}); }; defer zig_lib_directory.handle.close(); @@ -2208,7 +2242,7 @@ pub fn cmdInit( const max_bytes = 10 * 1024 * 1024; const build_zig_contents = template_dir.readFileAlloc(arena, "build.zig", max_bytes) catch |err| { - fatal("unable to read template file 'build.zig': {}", .{@errorName(err)}); + fatal("unable to read template file 'build.zig': {s}", .{@errorName(err)}); }; var modified_build_zig_contents = std.ArrayList(u8).init(arena); try modified_build_zig_contents.ensureCapacity(build_zig_contents.len); @@ -2220,13 +2254,13 @@ pub fn cmdInit( } } const main_zig_contents = template_dir.readFileAlloc(arena, "src" ++ s ++ "main.zig", max_bytes) catch |err| { - fatal("unable to read template file 'main.zig': {}", .{@errorName(err)}); + fatal("unable to read template file 'main.zig': {s}", .{@errorName(err)}); }; if (fs.cwd().access("build.zig", .{})) |_| { fatal("existing build.zig file would be overwritten", .{}); } else |err| switch (err) { error.FileNotFound => {}, - else => fatal("unable to test existence of build.zig: {}\n", .{@errorName(err)}), + else => fatal("unable to test existence of build.zig: {s}\n", .{@errorName(err)}), } var src_dir = try fs.cwd().makeOpenPath("src", .{}); defer src_dir.close(); @@ -2287,23 +2321,23 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v const arg = args[i]; if (mem.startsWith(u8, arg, "-")) { if (mem.eql(u8, arg, "--build-file")) { - if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); i += 1; build_file = args[i]; continue; } else if (mem.eql(u8, arg, "--override-lib-dir")) { - if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); i += 1; override_lib_dir = args[i]; try child_argv.appendSlice(&[_][]const u8{ arg, args[i] }); continue; } else if (mem.eql(u8, arg, "--cache-dir")) { - if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); i += 1; override_local_cache_dir = args[i]; continue; } else if (mem.eql(u8, arg, "--global-cache-dir")) { - if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + if (i + 1 >= args.len) fatal("expected argument after '{s}'", .{arg}); i += 1; override_global_cache_dir = args[i]; continue; @@ -2320,7 +2354,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { - fatal("unable to find zig installation directory: {}", .{@errorName(err)}); + fatal("unable to find zig installation directory: {s}", .{@errorName(err)}); }; defer zig_lib_directory.handle.close(); @@ -2361,7 +2395,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v } else |err| switch (err) { error.FileNotFound => { dirname = fs.path.dirname(dirname) orelse { - std.log.info("{}", .{ + std.log.info("{s}", .{ \\Initialize a 'build.zig' template file with `zig init-lib` or `zig init-exe`, \\or see `zig --help` for more options. }); @@ -2443,7 +2477,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v .self_exe_path = self_exe_path, .thread_pool = &thread_pool, }) catch |err| { - fatal("unable to create compilation: {}", .{@errorName(err)}); + fatal("unable to create compilation: {s}", .{@errorName(err)}); }; defer comp.destroy(); @@ -2469,11 +2503,11 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v .Exited => |code| { if (code == 0) return cleanExit(); const cmd = try argvCmd(arena, child_argv); - fatal("the following build command failed with exit code {}:\n{}", .{ code, cmd }); + fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd }); }, else => { const cmd = try argvCmd(arena, child_argv); - fatal("the following build command crashed:\n{}", .{cmd}); + fatal("the following build command crashed:\n{s}", .{cmd}); }, } } @@ -2540,14 +2574,14 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { i += 1; const next_arg = args[i]; color = std.meta.stringToEnum(Color, next_arg) orelse { - fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg}); + fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); }; } else if (mem.eql(u8, arg, "--stdin")) { stdin_flag = true; } else if (mem.eql(u8, arg, "--check")) { check_flag = true; } else { - fatal("unrecognized parameter: '{}'", .{arg}); + fatal("unrecognized parameter: '{s}'", .{arg}); } } else { try input_files.append(arg); @@ -2566,7 +2600,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { defer gpa.free(source_code); const tree = std.zig.parse(gpa, source_code) catch |err| { - fatal("error parsing stdin: {}", .{err}); + fatal("error parsing stdin: {s}", .{err}); }; defer tree.deinit(); @@ -2605,7 +2639,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { for (input_files.items) |file_path| { // Get the real path here to avoid Windows failing on relative file paths with . or .. in them. const real_path = fs.realpathAlloc(gpa, file_path) catch |err| { - fatal("unable to open '{}': {}", .{ file_path, err }); + fatal("unable to open '{s}': {s}", .{ file_path, @errorName(err) }); }; defer gpa.free(real_path); @@ -2644,7 +2678,7 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_ fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) { error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path), else => { - warn("unable to format '{}': {}", .{ file_path, err }); + warn("unable to format '{s}': {s}", .{ file_path, @errorName(err) }); fmt.any_error = true; return; }, @@ -2678,7 +2712,7 @@ fn fmtPathDir( try fmtPathDir(fmt, full_path, check_mode, dir, entry.name); } else { fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| { - warn("unable to format '{}': {}", .{ full_path, err }); + warn("unable to format '{s}': {s}", .{ full_path, @errorName(err) }); fmt.any_error = true; return; }; @@ -2737,7 +2771,7 @@ fn fmtPathFile( const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree); if (anything_changed) { const stdout = io.getStdOut().writer(); - try stdout.print("{}\n", .{file_path}); + try stdout.print("{s}\n", .{file_path}); fmt.any_error = true; } } else { @@ -2755,7 +2789,7 @@ fn fmtPathFile( try af.file.writeAll(fmt.out_buffer.items); try af.finish(); const stdout = io.getStdOut().writer(); - try stdout.print("{}\n", .{file_path}); + try stdout.print("{s}\n", .{file_path}); } } @@ -2788,7 +2822,7 @@ fn printErrMsgToFile( const text = text_buf.items; const stream = file.outStream(); - try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text }); + try stream.print("{s}:{d}:{d}: error: {s}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text }); if (!color_on) return; @@ -2960,7 +2994,7 @@ pub const ClangArgIterator = struct { const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit const resp_file_path = arg[1..]; const resp_contents = fs.cwd().readFileAlloc(allocator, resp_file_path, max_bytes) catch |err| { - fatal("unable to read response file '{}': {}", .{ resp_file_path, @errorName(err) }); + fatal("unable to read response file '{s}': {s}", .{ resp_file_path, @errorName(err) }); }; defer allocator.free(resp_contents); // TODO is there a specification for this file format? Let's find it and make this parsing more robust @@ -3033,7 +3067,7 @@ pub const ClangArgIterator = struct { const prefix_len = clang_arg.matchStartsWith(arg); if (prefix_len == arg.len) { if (self.next_index >= self.argv.len) { - fatal("Expected parameter after '{}'", .{arg}); + fatal("Expected parameter after '{s}'", .{arg}); } self.only_arg = self.argv[self.next_index]; self.incrementArgIndex(); @@ -3054,7 +3088,7 @@ pub const ClangArgIterator = struct { if (prefix_len != 0) { self.only_arg = arg[prefix_len..]; if (self.next_index >= self.argv.len) { - fatal("Expected parameter after '{}'", .{arg}); + fatal("Expected parameter after '{s}'", .{arg}); } self.second_arg = self.argv[self.next_index]; self.incrementArgIndex(); @@ -3065,7 +3099,7 @@ pub const ClangArgIterator = struct { }, .separate => if (clang_arg.matchEql(arg) > 0) { if (self.next_index >= self.argv.len) { - fatal("Expected parameter after '{}'", .{arg}); + fatal("Expected parameter after '{s}'", .{arg}); } self.only_arg = self.argv[self.next_index]; self.incrementArgIndex(); @@ -3091,7 +3125,7 @@ pub const ClangArgIterator = struct { }, } else { - fatal("Unknown Clang option: '{}'", .{arg}); + fatal("Unknown Clang option: '{s}'", .{arg}); } } @@ -3119,7 +3153,7 @@ pub const ClangArgIterator = struct { fn parseCodeModel(arg: []const u8) std.builtin.CodeModel { return std.meta.stringToEnum(std.builtin.CodeModel, arg) orelse - fatal("unsupported machine code model: '{}'", .{arg}); + fatal("unsupported machine code model: '{s}'", .{arg}); } /// Raise the open file descriptor limit. Ask and ye shall receive. @@ -3239,7 +3273,7 @@ fn detectNativeTargetInfo(gpa: *Allocator, cross_target: std.zig.CrossTarget) !s // CPU model & feature detection is todo so here we rely on LLVM. // https://github.com/ziglang/zig/issues/4591 if (!build_options.have_llvm) - fatal("CPU features detection is not yet available for {} without LLVM extensions", .{@tagName(arch)}); + fatal("CPU features detection is not yet available for {s} without LLVM extensions", .{@tagName(arch)}); const llvm = @import("llvm_bindings.zig"); const llvm_cpu_name = llvm.GetHostCPUName(); diff --git a/src/mingw.zig b/src/mingw.zig index 246b0f33dcf8..d55cc28b2ba6 100644 --- a/src/mingw.zig +++ b/src/mingw.zig @@ -381,7 +381,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { const term = child.wait() catch |err| { // TODO surface a proper error here - log.err("unable to spawn {}: {}", .{ args[0], @errorName(err) }); + log.err("unable to spawn {s}: {s}", .{ args[0], @errorName(err) }); return error.ClangPreprocessorFailed; }; @@ -395,7 +395,7 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { }, else => { // TODO surface a proper error here - log.err("clang terminated unexpectedly with stderr: {}", .{stderr}); + log.err("clang terminated unexpectedly with stderr: {s}", .{stderr}); return error.ClangPreprocessorFailed; }, } diff --git a/src/musl.zig b/src/musl.zig index f67fe90addaf..a865e786230a 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -155,21 +155,21 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { if (!is_arch_specific) { // Look for an arch specific override. override_path.shrinkRetainingCapacity(0); - try override_path.writer().print("{}" ++ s ++ "{}" ++ s ++ "{}.s", .{ + try override_path.writer().print("{s}" ++ s ++ "{s}" ++ s ++ "{s}.s", .{ dirname, arch_name, noextbasename, }); if (source_table.contains(override_path.items)) continue; override_path.shrinkRetainingCapacity(0); - try override_path.writer().print("{}" ++ s ++ "{}" ++ s ++ "{}.S", .{ + try override_path.writer().print("{s}" ++ s ++ "{s}" ++ s ++ "{s}.S", .{ dirname, arch_name, noextbasename, }); if (source_table.contains(override_path.items)) continue; override_path.shrinkRetainingCapacity(0); - try override_path.writer().print("{}" ++ s ++ "{}" ++ s ++ "{}.c", .{ + try override_path.writer().print("{s}" ++ s ++ "{s}" ++ s ++ "{s}.c", .{ dirname, arch_name, noextbasename, }); if (source_table.contains(override_path.items)) @@ -322,7 +322,7 @@ fn add_cc_args( const target = comp.getTarget(); const arch_name = target_util.archMuslName(target.cpu.arch); const os_name = @tagName(target.os.tag); - const triple = try std.fmt.allocPrint(arena, "{}-{}-musl", .{ arch_name, os_name }); + const triple = try std.fmt.allocPrint(arena, "{s}-{s}-musl", .{ arch_name, os_name }); const o_arg = if (want_O3) "-O3" else "-Os"; try args.appendSlice(&[_][]const u8{ diff --git a/src/print_env.zig b/src/print_env.zig index 8aa692e64432..bcf4a983abca 100644 --- a/src/print_env.zig +++ b/src/print_env.zig @@ -9,7 +9,7 @@ pub fn cmdEnv(gpa: *Allocator, args: []const []const u8, stdout: std.fs.File.Wri defer gpa.free(self_exe_path); var zig_lib_directory = introspect.findZigLibDirFromSelfExe(gpa, self_exe_path) catch |err| { - fatal("unable to find zig installation directory: {}\n", .{@errorName(err)}); + fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)}); }; defer gpa.free(zig_lib_directory.path.?); defer zig_lib_directory.handle.close(); diff --git a/src/print_targets.zig b/src/print_targets.zig index 724cb7a9ac39..cf55eee5168d 100644 --- a/src/print_targets.zig +++ b/src/print_targets.zig @@ -18,7 +18,7 @@ pub fn cmdTargets( native_target: Target, ) !void { var zig_lib_directory = introspect.findZigLibDir(allocator) catch |err| { - fatal("unable to find zig installation directory: {}\n", .{@errorName(err)}); + fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)}); }; defer zig_lib_directory.handle.close(); defer allocator.free(zig_lib_directory.path.?); @@ -61,7 +61,7 @@ pub fn cmdTargets( try jws.objectField("libc"); try jws.beginArray(); for (target.available_libcs) |libc| { - const tmp = try std.fmt.allocPrint(allocator, "{}-{}-{}", .{ + const tmp = try std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ @tagName(libc.arch), @tagName(libc.os), @tagName(libc.abi), }); defer allocator.free(tmp); diff --git a/src/stage1.zig b/src/stage1.zig index 1d50a71ad17c..cf3a252ce8fd 100644 --- a/src/stage1.zig +++ b/src/stage1.zig @@ -37,14 +37,14 @@ pub export fn main(argc: c_int, argv: [*][*:0]u8) c_int { defer arena_instance.deinit(); const arena = &arena_instance.allocator; - const args = arena.alloc([]const u8, @intCast(usize, argc)) catch fatal("{}", .{"OutOfMemory"}); + const args = arena.alloc([]const u8, @intCast(usize, argc)) catch fatal("{s}", .{"OutOfMemory"}); for (args) |*arg, i| { arg.* = mem.spanZ(argv[i]); } if (std.builtin.mode == .Debug) { stage2.mainArgs(gpa, arena, args) catch unreachable; } else { - stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{@errorName(err)}); + stage2.mainArgs(gpa, arena, args) catch |err| fatal("{s}", .{@errorName(err)}); } return 0; } diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index d3ed3cfaabe3..edda765ff901 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -24207,6 +24207,8 @@ static IrInstGen *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstSrcSwi ref_type->data.pointer.allow_zero); return ir_analyze_ptr_cast(ira, &instruction->base.base, target_value_ptr, &instruction->target_value_ptr->base, new_target_value_ptr_type, &instruction->base.base, false, false); + } else if (instruction->prongs_len > 1) { + return target_value_ptr; } else { ir_add_error(ira, &instruction->base.base, buf_sprintf("switch on type '%s' provides no expression parameter", buf_ptr(&target_type->name))); diff --git a/src/stage1/os.hpp b/src/stage1/os.hpp index 4f38403a9eb9..1dd30804ba2c 100644 --- a/src/stage1/os.hpp +++ b/src/stage1/os.hpp @@ -46,7 +46,7 @@ #endif #if defined(ZIG_OS_WINDOWS) -#define ZIG_PRI_usize "I64u" +#define ZIG_PRI_usize "Iu" #define ZIG_PRI_i64 "I64d" #define ZIG_PRI_u64 "I64u" #define ZIG_PRI_llu "I64u" diff --git a/src/test.zig b/src/test.zig index 6d76ae39c1c1..67a30f1f325f 100644 --- a/src/test.zig +++ b/src/test.zig @@ -135,6 +135,7 @@ pub const TestContext = struct { extension: Extension, object_format: ?std.builtin.ObjectFormat = null, emit_h: bool = false, + llvm_backend: bool = false, files: std.ArrayList(File), @@ -266,6 +267,21 @@ pub const TestContext = struct { return &ctx.cases.items[ctx.cases.items.len - 1]; } + /// Adds a test case that uses the LLVM backend to emit an executable. + /// Currently this implies linking libc, because only then we can generate a testable executable. + pub fn exeUsingLlvmBackend(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { + ctx.cases.append(Case{ + .name = name, + .target = target, + .updates = std.ArrayList(Update).init(ctx.cases.allocator), + .output_mode = .Exe, + .extension = .Zig, + .files = std.ArrayList(File).init(ctx.cases.allocator), + .llvm_backend = true, + }) catch unreachable; + return &ctx.cases.items[ctx.cases.items.len - 1]; + } + pub fn addObj( ctx: *TestContext, name: []const u8, @@ -518,10 +534,30 @@ pub const TestContext = struct { try thread_pool.init(std.testing.allocator); defer thread_pool.deinit(); + // Use the same global cache dir for all the tests, such that we for example don't have to + // rebuild musl libc for every case (when LLVM backend is enabled). + var global_tmp = std.testing.tmpDir(.{}); + defer global_tmp.cleanup(); + + var cache_dir = try global_tmp.dir.makeOpenPath("zig-cache", .{}); + defer cache_dir.close(); + const tmp_dir_path = try std.fs.path.join(std.testing.allocator, &[_][]const u8{ ".", "zig-cache", "tmp", &global_tmp.sub_path }); + defer std.testing.allocator.free(tmp_dir_path); + + const global_cache_directory: Compilation.Directory = .{ + .handle = cache_dir, + .path = try std.fs.path.join(std.testing.allocator, &[_][]const u8{ tmp_dir_path, "zig-cache" }), + }; + defer std.testing.allocator.free(global_cache_directory.path.?); + for (self.cases.items) |case| { if (build_options.skip_non_native and case.target.getCpuArch() != std.Target.current.cpu.arch) continue; + // Skip tests that require LLVM backend when it is not available + if (!build_options.have_llvm and case.llvm_backend) + continue; + var prg_node = root_node.start(case.name, case.updates.items.len); prg_node.activate(); defer prg_node.end(); @@ -537,6 +573,7 @@ pub const TestContext = struct { case, zig_lib_directory, &thread_pool, + global_cache_directory, ); } } @@ -548,6 +585,7 @@ pub const TestContext = struct { case: Case, zig_lib_directory: Compilation.Directory, thread_pool: *ThreadPool, + global_cache_directory: Compilation.Directory, ) !void { const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target); const target = target_info.target; @@ -601,7 +639,7 @@ pub const TestContext = struct { null; const comp = try Compilation.create(allocator, .{ .local_cache_directory = zig_cache_directory, - .global_cache_directory = zig_cache_directory, + .global_cache_directory = global_cache_directory, .zig_lib_directory = zig_lib_directory, .thread_pool = thread_pool, .root_name = "test_case", @@ -619,6 +657,9 @@ pub const TestContext = struct { .object_format = case.object_format, .is_native_os = case.target.isNativeOs(), .is_native_abi = case.target.isNativeAbi(), + .link_libc = case.llvm_backend, + .use_llvm = case.llvm_backend, + .self_exe_path = std.testing.zig_exe_path, }); defer comp.destroy(); @@ -660,7 +701,7 @@ pub const TestContext = struct { } } if (comp.bin_file.cast(link.File.C)) |c_file| { - std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{ + std.debug.print("Generated C: \n===============\n{s}\n\n===========\n\n", .{ c_file.main.items, }); } diff --git a/src/translate_c.zig b/src/translate_c.zig index 1b2aa4b2195d..7aebcf069b4e 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -136,7 +136,7 @@ const Scope = struct { var proposed_name = name_copy; while (scope.contains(proposed_name)) { scope.mangle_count += 1; - proposed_name = try std.fmt.allocPrint(c.arena, "{}_{}", .{ name, scope.mangle_count }); + proposed_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ name, scope.mangle_count }); } try scope.variables.append(.{ .name = name_copy, .alias = proposed_name }); return proposed_name; @@ -290,7 +290,7 @@ pub const Context = struct { const line = c.source_manager.getSpellingLineNumber(spelling_loc); const column = c.source_manager.getSpellingColumnNumber(spelling_loc); - return std.fmt.allocPrint(c.arena, "{}:{}:{}", .{ filename, line, column }); + return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, column }); } fn createCall(c: *Context, fn_expr: *ast.Node, params_len: ast.NodeIndex) !*ast.Node.Call { @@ -440,7 +440,7 @@ pub fn translate( mem.copy(*ast.Node, root_node.decls(), context.root_decls.items); if (false) { - std.debug.warn("debug source:\n{}\n==EOF==\ntokens:\n", .{source_buffer.items}); + std.debug.warn("debug source:\n{s}\n==EOF==\ntokens:\n", .{source_buffer.items}); for (context.token_ids.items) |token| { std.debug.warn("{}\n", .{token}); } @@ -530,7 +530,7 @@ fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void { }, else => { const decl_name = try c.str(decl.getDeclKindName()); - try emitWarning(c, decl.getLocation(), "ignoring {} declaration", .{decl_name}); + try emitWarning(c, decl.getLocation(), "ignoring {s} declaration", .{decl_name}); }, } } @@ -625,7 +625,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { const param_name = if (param.name_token) |name_tok| tokenSlice(c, name_tok) else - return failDecl(c, fn_decl_loc, fn_name, "function {} parameter has no name", .{fn_name}); + return failDecl(c, fn_decl_loc, fn_name, "function {s} parameter has no name", .{fn_name}); const c_param = fn_decl.getParamDecl(param_id); const qual_type = c_param.getOriginalType(); @@ -634,7 +634,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { const mangled_param_name = try block_scope.makeMangledName(c, param_name); if (!is_const) { - const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{}", .{mangled_param_name}); + const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{s}", .{mangled_param_name}); const arg_name = try block_scope.makeMangledName(c, bare_arg_name); const mut_tok = try appendToken(c, .Keyword_var, "var"); @@ -727,7 +727,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 - const checked_name = if (isZigPrimitiveType(var_name)) try std.fmt.allocPrint(c.arena, "{}_{}", .{ var_name, c.getMangle() }) else var_name; + const checked_name = if (isZigPrimitiveType(var_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ var_name, c.getMangle() }) else var_name; const var_decl_loc = var_decl.getLocation(); const qual_type = var_decl.getTypeSourceInfo_getType(); @@ -808,7 +808,7 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co _ = try appendToken(rp.c, .LParen, "("); const expr = try transCreateNodeStringLiteral( rp.c, - try std.fmt.allocPrint(rp.c.arena, "\"{}\"", .{str_ptr[0..str_len]}), + try std.fmt.allocPrint(rp.c.arena, "\"{s}\"", .{str_ptr[0..str_len]}), ); _ = try appendToken(rp.c, .RParen, ")"); @@ -887,7 +887,7 @@ fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_lev // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 - const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.arena, "{}_{}", .{ typedef_name, c.getMangle() }) else typedef_name; + const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ typedef_name, c.getMangle() }) else typedef_name; if (checkForBuiltinTypedef(checked_name)) |builtin| { return transTypeDefAsBuiltin(c, typedef_decl, builtin); } @@ -945,7 +945,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as // Record declarations such as `struct {...} x` have no name but they're not // anonymous hence here isAnonymousStructOrUnion is not needed if (bare_name.len == 0) { - bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{}", .{c.getMangle()}); + bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); is_unnamed = true; } @@ -958,11 +958,11 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as container_kind_name = "struct"; container_kind = .Keyword_struct; } else { - try emitWarning(c, record_loc, "record {} is not a struct or union", .{bare_name}); + try emitWarning(c, record_loc, "record {s} is not a struct or union", .{bare_name}); return null; } - const name = try std.fmt.allocPrint(c.arena, "{}_{}", .{ container_kind_name, bare_name }); + const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name }); _ = try c.decl_table.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name); const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null; @@ -1003,7 +1003,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); const opaque_type = try transCreateNodeOpaqueType(c); semicolon = try appendToken(c, .Semicolon, ";"); - try emitWarning(c, field_loc, "{} demoted to opaque type - has bitfield", .{container_kind_name}); + try emitWarning(c, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name}); break :blk opaque_type; } @@ -1011,7 +1011,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); const opaque_type = try transCreateNodeOpaqueType(c); semicolon = try appendToken(c, .Semicolon, ";"); - try emitWarning(c, field_loc, "{} demoted to opaque type - has variable length array", .{container_kind_name}); + try emitWarning(c, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name}); break :blk opaque_type; } @@ -1019,7 +1019,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as var raw_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin()); if (field_decl.isAnonymousStructOrUnion() or raw_name.len == 0) { // Context.getMangle() is not used here because doing so causes unpredictable field names for anonymous fields. - raw_name = try std.fmt.allocPrint(c.arena, "unnamed_{}", .{unnamed_field_count}); + raw_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count}); unnamed_field_count += 1; is_anon = true; } @@ -1030,7 +1030,7 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {}); const opaque_type = try transCreateNodeOpaqueType(c); semicolon = try appendToken(c, .Semicolon, ";"); - try emitWarning(c, record_loc, "{} demoted to opaque type - unable to translate type of field {}", .{ container_kind_name, raw_name }); + try emitWarning(c, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, raw_name }); break :blk opaque_type; }, else => |e| return e, @@ -1110,11 +1110,11 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()); var is_unnamed = false; if (bare_name.len == 0) { - bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{}", .{c.getMangle()}); + bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()}); is_unnamed = true; } - const name = try std.fmt.allocPrint(c.arena, "enum_{}", .{bare_name}); + const name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name}); _ = try c.decl_table.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name); const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null; @@ -1385,7 +1385,7 @@ fn transStmt( rp, error.UnsupportedTranslation, stmt.getBeginLoc(), - "TODO implement translation of stmt class {}", + "TODO implement translation of stmt class {s}", .{@tagName(sc)}, ); }, @@ -1684,7 +1684,7 @@ fn transDeclStmtOne( rp, error.UnsupportedTranslation, decl.getLocation(), - "TODO implement translation of DeclStmt kind {}", + "TODO implement translation of DeclStmt kind {s}", .{@tagName(kind)}, ), } @@ -1782,7 +1782,7 @@ fn transImplicitCastExpr( rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, expr).getBeginLoc(), - "TODO implement translation of CastKind {}", + "TODO implement translation of CastKind {s}", .{@tagName(kind)}, ), } @@ -2043,7 +2043,7 @@ fn transStringLiteral( rp, error.UnsupportedTranslation, @ptrCast(*const clang.Stmt, stmt).getBeginLoc(), - "TODO: support string literal kind {}", + "TODO: support string literal kind {s}", .{kind}, ), } @@ -2168,7 +2168,6 @@ fn transCCast( // @boolToInt returns either a comptime_int or a u1 // TODO: if dst_type is 1 bit & signed (bitfield) we need @bitCast // instead of @as - const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1); builtin_node.params()[0] = expr; builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); @@ -2455,7 +2454,7 @@ fn transInitListExpr( ); } else { const type_name = rp.c.str(qual_type.getTypeClassName()); - return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported initlist type: '{}'", .{type_name}); + return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported initlist type: '{s}'", .{type_name}); } } @@ -3957,7 +3956,7 @@ fn qualTypeToLog2IntRef(rp: RestorePoint, qt: clang.QualType, source_loc: clang. const node = try rp.c.arena.create(ast.Node.OneToken); node.* = .{ .base = .{ .tag = .IntegerLiteral }, - .token = try appendTokenFmt(rp.c, .Identifier, "u{}", .{cast_bit_width}), + .token = try appendTokenFmt(rp.c, .Identifier, "u{d}", .{cast_bit_width}), }; return &node.base; } @@ -4433,7 +4432,8 @@ fn transCreateNodeBoolLiteral(c: *Context, value: bool) !*ast.Node { } fn transCreateNodeInt(c: *Context, int: anytype) !*ast.Node { - const token = try appendTokenFmt(c, .IntegerLiteral, "{}", .{int}); + const fmt_s = if (comptime std.meta.trait.isIntegerNumber(@TypeOf(int))) "{d}" else "{s}"; + const token = try appendTokenFmt(c, .IntegerLiteral, fmt_s, .{int}); const node = try c.arena.create(ast.Node.OneToken); node.* = .{ .base = .{ .tag = .IntegerLiteral }, @@ -4442,8 +4442,8 @@ fn transCreateNodeInt(c: *Context, int: anytype) !*ast.Node { return &node.base; } -fn transCreateNodeFloat(c: *Context, int: anytype) !*ast.Node { - const token = try appendTokenFmt(c, .FloatLiteral, "{}", .{int}); +fn transCreateNodeFloat(c: *Context, str: []const u8) !*ast.Node { + const token = try appendTokenFmt(c, .FloatLiteral, "{s}", .{str}); const node = try c.arena.create(ast.Node.OneToken); node.* = .{ .base = .{ .tag = .FloatLiteral }, @@ -4484,7 +4484,7 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a _ = try appendToken(c, .Comma, ","); } const param_name_tok = param.name_token orelse - try appendTokenFmt(c, .Identifier, "arg_{}", .{c.getMangle()}); + try appendTokenFmt(c, .Identifier, "arg_{d}", .{c.getMangle()}); _ = try appendToken(c, .Colon, ":"); @@ -4916,7 +4916,7 @@ fn transType(rp: RestorePoint, ty: *const clang.Type, source_loc: clang.SourceLo }, else => { const type_name = rp.c.str(ty.getTypeClassName()); - return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{}'", .{type_name}); + return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name}); }, } } @@ -4999,7 +4999,7 @@ fn transCC( rp, error.UnsupportedType, source_loc, - "unsupported calling convention: {}", + "unsupported calling convention: {s}", .{@tagName(clang_cc)}, ), } @@ -5027,7 +5027,7 @@ fn transFnNoProto( is_pub: bool, ) !*ast.Node.FnProto { const cc = try transCC(rp, fn_ty, source_loc); - const is_var_args = if (fn_decl_context) |ctx| !ctx.is_export else true; + const is_var_args = if (fn_decl_context) |ctx| (!ctx.is_export and ctx.storage_class != .Static) else true; return finishTransFnProto(rp, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub); } @@ -5117,7 +5117,7 @@ fn finishTransFnProto( _ = try appendToken(rp.c, .LParen, "("); const expr = try transCreateNodeStringLiteral( rp.c, - try std.fmt.allocPrint(rp.c.arena, "\"{}\"", .{str_ptr[0..str_len]}), + try std.fmt.allocPrint(rp.c.arena, "\"{s}\"", .{str_ptr[0..str_len]}), ); _ = try appendToken(rp.c, .RParen, ")"); @@ -5214,7 +5214,7 @@ fn revertAndWarn( fn emitWarning(c: *Context, loc: clang.SourceLocation, comptime format: []const u8, args: anytype) !void { const args_prefix = .{c.locStr(loc)}; - _ = try appendTokenFmt(c, .LineComment, "// {}: warning: " ++ format, args_prefix ++ args); + _ = try appendTokenFmt(c, .LineComment, "// {s}: warning: " ++ format, args_prefix ++ args); } pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, comptime format: []const u8, args: anytype) !void { @@ -5228,7 +5228,7 @@ pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, compti const msg_tok = try appendTokenFmt(c, .StringLiteral, "\"" ++ format ++ "\"", args); const rparen_tok = try appendToken(c, .RParen, ")"); const semi_tok = try appendToken(c, .Semicolon, ";"); - _ = try appendTokenFmt(c, .LineComment, "// {}", .{c.locStr(loc)}); + _ = try appendTokenFmt(c, .LineComment, "// {s}", .{c.locStr(loc)}); const msg_node = try c.arena.create(ast.Node.OneToken); msg_node.* = .{ @@ -5258,7 +5258,7 @@ pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, compti fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenIndex { std.debug.assert(token_id != .Identifier); // use appendIdentifier - return appendTokenFmt(c, token_id, "{}", .{bytes}); + return appendTokenFmt(c, token_id, "{s}", .{bytes}); } fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: anytype) !ast.TokenIndex { @@ -5329,7 +5329,7 @@ fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node { } fn transCreateNodeIdentifierUnchecked(c: *Context, name: []const u8) !*ast.Node { - const token_index = try appendTokenFmt(c, .Identifier, "{}", .{name}); + const token_index = try appendTokenFmt(c, .Identifier, "{s}", .{name}); const identifier = try c.arena.create(ast.Node.OneToken); identifier.* = .{ .base = .{ .tag = .Identifier }, @@ -5390,7 +5390,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void { const name = try c.str(raw_name); // TODO https://github.com/ziglang/zig/issues/3756 // TODO https://github.com/ziglang/zig/issues/1802 - const mangled_name = if (isZigPrimitiveType(name)) try std.fmt.allocPrint(c.arena, "{}_{}", .{ name, c.getMangle() }) else name; + const mangled_name = if (isZigPrimitiveType(name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ name, c.getMangle() }) else name; if (scope.containsNow(mangled_name)) { continue; } @@ -5468,7 +5468,7 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void { const init_node = try parseCExpr(c, m, scope); const last = m.next().?; if (last != .Eof and last != .Nl) - return m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(last)}); + return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)}); const semicolon_token = try appendToken(c, .Semicolon, ";"); const node = try ast.Node.VarDecl.create(c.arena, .{ @@ -5540,7 +5540,7 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { const expr = try parseCExpr(c, m, scope); const last = m.next().?; if (last != .Eof and last != .Nl) - return m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(last)}); + return m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(last)}); _ = try appendToken(c, .Semicolon, ";"); const type_of_arg = if (!expr.tag.isBlock()) expr else blk: { const stmts = expr.blockStatements(); @@ -5623,11 +5623,11 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node { switch (lit_bytes[1]) { '0'...'7' => { // Octal - lit_bytes = try std.fmt.allocPrint(c.arena, "0o{}", .{lit_bytes}); + lit_bytes = try std.fmt.allocPrint(c.arena, "0o{s}", .{lit_bytes}); }, 'X' => { // Hexadecimal with capital X, valid in C but not in Zig - lit_bytes = try std.fmt.allocPrint(c.arena, "0x{}", .{lit_bytes[2..]}); + lit_bytes = try std.fmt.allocPrint(c.arena, "0x{s}", .{lit_bytes[2..]}); }, else => {}, } @@ -5659,7 +5659,7 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!*ast.Node { }, .FloatLiteral => |suffix| { if (lit_bytes[0] == '.') - lit_bytes = try std.fmt.allocPrint(c.arena, "0{}", .{lit_bytes}); + lit_bytes = try std.fmt.allocPrint(c.arena, "0{s}", .{lit_bytes}); if (suffix == .none) { return transCreateNodeFloat(c, lit_bytes); } @@ -5916,11 +5916,11 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* // struct Foo will be declared as struct_Foo by transRecordDecl const next_id = m.next().?; if (next_id != .Identifier) { - try m.fail(c, "unable to translate C expr: expected Identifier instead got: {}", .{@tagName(next_id)}); + try m.fail(c, "unable to translate C expr: expected Identifier instead got: {s}", .{@tagName(next_id)}); return error.ParseError; } - const ident_token = try appendTokenFmt(c, .Identifier, "{}_{}", .{ slice, m.slice() }); + const ident_token = try appendTokenFmt(c, .Identifier, "{s}_{s}", .{ slice, m.slice() }); const identifier = try c.arena.create(ast.Node.OneToken); identifier.* = .{ .base = .{ .tag = .Identifier }, @@ -5937,7 +5937,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* const next_id = m.next().?; if (next_id != .RParen) { - try m.fail(c, "unable to translate C expr: expected ')' instead got: {}", .{@tagName(next_id)}); + try m.fail(c, "unable to translate C expr: expected ')' instead got: {s}", .{@tagName(next_id)}); return error.ParseError; } var saw_l_paren = false; @@ -5995,7 +5995,7 @@ fn parseCPrimaryExprInner(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!* return &group_node.base; }, else => { - try m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(tok)}); + try m.fail(c, "unable to translate C expr: unexpected token .{s}", .{@tagName(tok)}); return error.ParseError; }, } diff --git a/src/type.zig b/src/type.zig index 7e6294a75a8e..bacc5789793f 100644 --- a/src/type.zig +++ b/src/type.zig @@ -602,21 +602,21 @@ pub const Type = extern union { }, .array_u8 => { const len = ty.castTag(.array_u8).?.data; - return out_stream.print("[{}]u8", .{len}); + return out_stream.print("[{d}]u8", .{len}); }, .array_u8_sentinel_0 => { const len = ty.castTag(.array_u8_sentinel_0).?.data; - return out_stream.print("[{}:0]u8", .{len}); + return out_stream.print("[{d}:0]u8", .{len}); }, .array => { const payload = ty.castTag(.array).?.data; - try out_stream.print("[{}]", .{payload.len}); + try out_stream.print("[{d}]", .{payload.len}); ty = payload.elem_type; continue; }, .array_sentinel => { const payload = ty.castTag(.array_sentinel).?.data; - try out_stream.print("[{}:{}]", .{ payload.len, payload.sentinel }); + try out_stream.print("[{d}:{}]", .{ payload.len, payload.sentinel }); ty = payload.elem_type; continue; }, diff --git a/src/value.zig b/src/value.zig index c6eac57ce272..905ac1da1e69 100644 --- a/src/value.zig +++ b/src/value.zig @@ -330,11 +330,14 @@ pub const Value = extern union { .int_type => return self.copyPayloadShallow(allocator, Payload.IntType), .int_u64 => return self.copyPayloadShallow(allocator, Payload.U64), .int_i64 => return self.copyPayloadShallow(allocator, Payload.I64), - .int_big_positive => { - @panic("TODO implement copying of big ints"); - }, - .int_big_negative => { - @panic("TODO implement copying of big ints"); + .int_big_positive, .int_big_negative => { + const old_payload = self.cast(Payload.BigInt).?; + const new_payload = try allocator.create(Payload.BigInt); + new_payload.* = .{ + .base = .{ .tag = self.ptr_otherwise.tag }, + .data = try allocator.dupe(std.math.big.Limb, old_payload.data), + }; + return Value{ .ptr_otherwise = &new_payload.base }; }, .function => return self.copyPayloadShallow(allocator, Payload.Function), .extern_fn => return self.copyPayloadShallow(allocator, Payload.Decl), @@ -464,7 +467,7 @@ pub const Value = extern union { .ty => return val.castTag(.ty).?.data.format("", options, out_stream), .int_type => { const int_type = val.castTag(.int_type).?.data; - return out_stream.print("{}{}", .{ + return out_stream.print("{s}{d}", .{ if (int_type.signed) "s" else "u", int_type.bits, }); @@ -507,7 +510,7 @@ pub const Value = extern union { } return out_stream.writeAll("}"); }, - .@"error" => return out_stream.print("error.{}", .{val.castTag(.@"error").?.data.name}), + .@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}), .inferred_alloc => return out_stream.writeAll("(inferred allocation value)"), }; } diff --git a/src/zig_clang.h b/src/zig_clang.h index 32879062aee8..169fbcedfb26 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -8,14 +8,32 @@ #ifndef ZIG_ZIG_CLANG_H #define ZIG_ZIG_CLANG_H -#include "stage1/stage2.h" #include #include +#include + +#ifdef __cplusplus +#define ZIG_EXTERN_C extern "C" +#else +#define ZIG_EXTERN_C +#endif // ATTENTION: If you modify this file, be sure to update the corresponding // extern function declarations in the self-hosted compiler file // src/clang.zig. +// ABI warning +struct Stage2ErrorMsg { + const char *filename_ptr; // can be null + size_t filename_len; + const char *msg_ptr; + size_t msg_len; + const char *source; // valid until the ASTUnit is freed. can be null + unsigned line; // 0 based + unsigned column; // 0 based + unsigned offset; // byte offset into source +}; + struct ZigClangSourceLocation { unsigned ID; }; diff --git a/src/zir.zig b/src/zir.zig index ce8498d7e862..8019d0e030fa 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -25,12 +25,13 @@ pub const Decl = struct { /// These are instructions that correspond to the ZIR text format. See `ir.Inst` for /// in-memory, analyzed instructions with types and values. +/// We use a table to map these instruction to their respective semantically analyzed +/// instructions because it is possible to have multiple analyses on the same ZIR +/// happening at the same time. pub const Inst = struct { tag: Tag, /// Byte offset into the source. src: usize, - /// Pre-allocated field for mapping ZIR text instructions to post-analysis instructions. - analyzed_inst: ?*ir.Inst = null, /// These names are used directly as the instruction names in the text format. pub const Tag = enum { @@ -126,6 +127,9 @@ pub const Inst = struct { coerce_to_ptr_elem, /// Emit an error message and fail compilation. compileerror, + /// Changes the maximum number of backwards branches that compile-time + /// code execution can use before giving up and making a compile error. + set_eval_branch_quota, /// Conditional branch. Splits control flow based on a boolean condition value. condbr, /// Special case, has no textual representation. @@ -346,6 +350,7 @@ pub const Inst = struct { .anyframe_type, .bitnot, .import, + .set_eval_branch_quota, => UnOp, .add, @@ -534,6 +539,7 @@ pub const Inst = struct { .switch_range, .typeof_peer, .resolve_inferred_alloc, + .set_eval_branch_quota, => false, .@"break", @@ -793,7 +799,9 @@ pub const Inst = struct { fn_type: *Inst, body: Module.Body, }, - kw_args: struct {}, + kw_args: struct { + is_inline: bool = false, + }, }; pub const FnType = struct { @@ -1150,7 +1158,7 @@ pub const Module = struct { for (self.decls) |decl, i| { write.next_instr_index = 0; - try stream.print("@{} ", .{decl.name}); + try stream.print("@{s} ", .{decl.name}); try write.writeInstToStream(stream, decl.inst); try stream.writeByte('\n'); } @@ -1206,13 +1214,13 @@ const Writer = struct { if (@typeInfo(arg_field.field_type) == .Optional) { if (@field(inst.kw_args, arg_field.name)) |non_optional| { if (need_comma) try stream.writeAll(", "); - try stream.print("{}=", .{arg_field.name}); + try stream.print("{s}=", .{arg_field.name}); try self.writeParamToStream(stream, &non_optional); need_comma = true; } } else { if (need_comma) try stream.writeAll(", "); - try stream.print("{}=", .{arg_field.name}); + try stream.print("{s}=", .{arg_field.name}); try self.writeParamToStream(stream, &@field(inst.kw_args, arg_field.name)); need_comma = true; } @@ -1257,12 +1265,12 @@ const Writer = struct { self.next_instr_index += 1; try self.inst_table.putNoClobber(inst, .{ .inst = inst, .index = my_i, .name = undefined }); try stream.writeByteNTimes(' ', self.indent); - try stream.print("%{} ", .{my_i}); + try stream.print("%{d} ", .{my_i}); if (inst.cast(Inst.Block)) |block| { - const name = try std.fmt.allocPrint(&self.arena.allocator, "label_{}", .{my_i}); + const name = try std.fmt.allocPrint(&self.arena.allocator, "label_{d}", .{my_i}); try self.block_table.put(block, name); } else if (inst.cast(Inst.Loop)) |loop| { - const name = try std.fmt.allocPrint(&self.arena.allocator, "loop_{}", .{my_i}); + const name = try std.fmt.allocPrint(&self.arena.allocator, "loop_{d}", .{my_i}); try self.loop_table.put(loop, name); } self.indent += 2; @@ -1332,18 +1340,18 @@ const Writer = struct { fn writeInstParamToStream(self: *Writer, stream: anytype, inst: *Inst) !void { if (self.inst_table.get(inst)) |info| { if (info.index) |i| { - try stream.print("%{}", .{info.index}); + try stream.print("%{d}", .{info.index}); } else { - try stream.print("@{}", .{info.name}); + try stream.print("@{s}", .{info.name}); } } else if (inst.cast(Inst.DeclVal)) |decl_val| { - try stream.print("@{}", .{decl_val.positionals.name}); + try stream.print("@{s}", .{decl_val.positionals.name}); } else if (inst.cast(Inst.DeclValInModule)) |decl_val| { - try stream.print("@{}", .{decl_val.positionals.decl.name}); + try stream.print("@{s}", .{decl_val.positionals.decl.name}); } else { // This should be unreachable in theory, but since ZIR is used for debugging the compiler // we output some debug text instead. - try stream.print("?{}?", .{@tagName(inst.tag)}); + try stream.print("?{s}?", .{@tagName(inst.tag)}); } } }; @@ -1424,7 +1432,7 @@ const Parser = struct { const decl = try parseInstruction(self, &body_context, ident); const ident_index = body_context.instructions.items.len; if (try body_context.name_map.fetchPut(ident, decl.inst)) |_| { - return self.fail("redefinition of identifier '{}'", .{ident}); + return self.fail("redefinition of identifier '{s}'", .{ident}); } try body_context.instructions.append(decl.inst); continue; @@ -1510,7 +1518,7 @@ const Parser = struct { const decl = try parseInstruction(self, null, ident); const ident_index = self.decls.items.len; if (try self.global_name_map.fetchPut(ident, decl.inst)) |_| { - return self.fail("redefinition of identifier '{}'", .{ident}); + return self.fail("redefinition of identifier '{s}'", .{ident}); } try self.decls.append(self.allocator, decl); }, @@ -1538,7 +1546,7 @@ const Parser = struct { for (bytes) |byte| { if (self.source[self.i] != byte) { self.i = start; - return self.fail("expected '{}'", .{bytes}); + return self.fail("expected '{s}'", .{bytes}); } self.i += 1; } @@ -1585,7 +1593,7 @@ const Parser = struct { return parseInstructionGeneric(self, field.name, tag.Type(), tag, body_ctx, name, contents_start); } } - return self.fail("unknown instruction '{}'", .{fn_name}); + return self.fail("unknown instruction '{s}'", .{fn_name}); } fn parseInstructionGeneric( @@ -1621,7 +1629,7 @@ const Parser = struct { self.i += 1; skipSpace(self); } else if (self.source[self.i] == ')') { - return self.fail("expected positional parameter '{}'", .{arg_field.name}); + return self.fail("expected positional parameter '{s}'", .{arg_field.name}); } @field(inst_specific.positionals, arg_field.name) = try parseParameterGeneric( self, @@ -1648,7 +1656,7 @@ const Parser = struct { break; } } else { - return self.fail("unrecognized keyword parameter: '{}'", .{name}); + return self.fail("unrecognized keyword parameter: '{s}'", .{name}); } skipSpace(self); } @@ -1660,7 +1668,6 @@ const Parser = struct { .contents_hash = std.zig.hashSrc(self.source[contents_start..self.i]), .inst = &inst_specific.base, }; - //std.debug.warn("parsed {} = '{}'\n", .{ inst_specific.base.name, inst_specific.base.contents }); return decl; } @@ -1672,7 +1679,7 @@ const Parser = struct { ' ', '\n', ',', ')' => { const enum_name = self.source[start..self.i]; return std.meta.stringToEnum(T, enum_name) orelse { - return self.fail("tag '{}' not a member of enum '{}'", .{ enum_name, @typeName(T) }); + return self.fail("tag '{s}' not a member of enum '{s}'", .{ enum_name, @typeName(T) }); }; }, 0 => return self.failByte(0), @@ -1710,7 +1717,7 @@ const Parser = struct { BigIntConst => return self.parseIntegerLiteral(), usize => { const big_int = try self.parseIntegerLiteral(); - return big_int.to(usize) catch |err| return self.fail("integer literal: {}", .{@errorName(err)}); + return big_int.to(usize) catch |err| return self.fail("integer literal: {s}", .{@errorName(err)}); }, TypedValue => return self.fail("'const' is a special instruction; not legal in ZIR text", .{}), *IrModule.Decl => return self.fail("'declval_in_module' is a special instruction; not legal in ZIR text", .{}), @@ -1759,7 +1766,7 @@ const Parser = struct { }, else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), } - return self.fail("TODO parse parameter {}", .{@typeName(T)}); + return self.fail("TODO parse parameter {s}", .{@typeName(T)}); } fn parseParameterInst(self: *Parser, body_ctx: ?*Body) !*Inst { @@ -1788,7 +1795,7 @@ const Parser = struct { const src = name_start - 1; if (local_ref) { self.i = src; - return self.fail("unrecognized identifier: {}", .{bad_name}); + return self.fail("unrecognized identifier: {s}", .{bad_name}); } else { const declval = try self.arena.allocator.create(Inst.DeclVal); declval.* = .{ @@ -1805,7 +1812,7 @@ const Parser = struct { } fn generateName(self: *Parser) ![]u8 { - const result = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${}", .{self.unnamed_index}); + const result = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${d}", .{self.unnamed_index}); self.unnamed_index += 1; return result; } @@ -1848,44 +1855,325 @@ pub fn emit(allocator: *Allocator, old_module: *IrModule) !Module { /// For debugging purposes, prints a function representation to stderr. pub fn dumpFn(old_module: IrModule, module_fn: *IrModule.Fn) void { const allocator = old_module.gpa; - var ctx: EmitZIR = .{ + var ctx: DumpTzir = .{ .allocator = allocator, - .decls = .{}, .arena = std.heap.ArenaAllocator.init(allocator), .old_module = &old_module, - .next_auto_name = 0, - .names = std.StringArrayHashMap(void).init(allocator), - .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Decl).init(allocator), - .indent = 0, - .block_table = std.AutoHashMap(*ir.Inst.Block, *Inst.Block).init(allocator), - .loop_table = std.AutoHashMap(*ir.Inst.Loop, *Inst.Loop).init(allocator), - .metadata = std.AutoHashMap(*Inst, Module.MetaData).init(allocator), - .body_metadata = std.AutoHashMap(*Module.Body, Module.BodyMetaData).init(allocator), + .module_fn = module_fn, + .indent = 2, + .inst_table = DumpTzir.InstTable.init(allocator), + .partial_inst_table = DumpTzir.InstTable.init(allocator), + .const_table = DumpTzir.InstTable.init(allocator), }; - defer ctx.metadata.deinit(); - defer ctx.body_metadata.deinit(); - defer ctx.block_table.deinit(); - defer ctx.loop_table.deinit(); - defer ctx.decls.deinit(allocator); - defer ctx.names.deinit(); - defer ctx.primitive_table.deinit(); + defer ctx.inst_table.deinit(); + defer ctx.partial_inst_table.deinit(); + defer ctx.const_table.deinit(); defer ctx.arena.deinit(); - const fn_ty = module_fn.owner_decl.typed_value.most_recent.typed_value.ty; - _ = ctx.emitFn(module_fn, 0, fn_ty) catch |err| { - std.debug.print("unable to dump function: {}\n", .{err}); - return; - }; - var module = Module{ - .decls = ctx.decls.items, - .arena = ctx.arena, - .metadata = ctx.metadata, - .body_metadata = ctx.body_metadata, - }; - - module.dump(); + switch (module_fn.state) { + .queued => std.debug.print("(queued)", .{}), + .inline_only => std.debug.print("(inline_only)", .{}), + .in_progress => std.debug.print("(in_progress)", .{}), + .sema_failure => std.debug.print("(sema_failure)", .{}), + .dependency_failure => std.debug.print("(dependency_failure)", .{}), + .success => { + const writer = std.io.getStdErr().writer(); + ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR"); + }, + } } +const DumpTzir = struct { + allocator: *Allocator, + arena: std.heap.ArenaAllocator, + old_module: *const IrModule, + module_fn: *IrModule.Fn, + indent: usize, + inst_table: InstTable, + partial_inst_table: InstTable, + const_table: InstTable, + next_index: usize = 0, + next_partial_index: usize = 0, + next_const_index: usize = 0, + + const InstTable = std.AutoArrayHashMap(*ir.Inst, usize); + + fn dump(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) !void { + // First pass to pre-populate the table so that we can show even invalid references. + // Must iterate the same order we iterate the second time. + // We also look for constants and put them in the const_table. + for (body.instructions) |inst| { + try dtz.inst_table.put(inst, dtz.next_index); + dtz.next_index += 1; + switch (inst.tag) { + .alloc, + .retvoid, + .unreach, + .breakpoint, + .dbg_stmt, + => {}, + + .ref, + .ret, + .bitcast, + .not, + .isnonnull, + .isnull, + .iserr, + .ptrtoint, + .floatcast, + .intcast, + .load, + .unwrap_optional, + .wrap_optional, + => { + const un_op = inst.cast(ir.Inst.UnOp).?; + try dtz.findConst(un_op.operand); + }, + + .add, + .sub, + .cmp_lt, + .cmp_lte, + .cmp_eq, + .cmp_gte, + .cmp_gt, + .cmp_neq, + .store, + .booland, + .boolor, + .bitand, + .bitor, + .xor, + => { + const bin_op = inst.cast(ir.Inst.BinOp).?; + try dtz.findConst(bin_op.lhs); + try dtz.findConst(bin_op.rhs); + }, + + .arg => {}, + + .br => { + const br = inst.castTag(.br).?; + try dtz.findConst(&br.block.base); + try dtz.findConst(br.operand); + }, + + .brvoid => { + const brvoid = inst.castTag(.brvoid).?; + try dtz.findConst(&brvoid.block.base); + }, + + // TODO fill out this debug printing + .assembly, + .block, + .call, + .condbr, + .constant, + .loop, + .varptr, + .switchbr, + => {}, + } + } + + std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name}); + + for (dtz.const_table.items()) |entry| { + const constant = entry.key.castTag(.constant).?; + try writer.print(" @{d}: {} = {};\n", .{ + entry.value, constant.base.ty, constant.val, + }); + } + + return dtz.dumpBody(body, writer); + } + + fn dumpBody(dtz: *DumpTzir, body: ir.Body, writer: std.fs.File.Writer) !void { + for (body.instructions) |inst| { + const my_index = dtz.next_partial_index; + try dtz.partial_inst_table.put(inst, my_index); + dtz.next_partial_index += 1; + + try writer.writeByteNTimes(' ', dtz.indent); + try writer.print("%{d}: {} = {s}(", .{ + my_index, inst.ty, @tagName(inst.tag), + }); + switch (inst.tag) { + .alloc, + .retvoid, + .unreach, + .breakpoint, + .dbg_stmt, + => try writer.writeAll(")\n"), + + .ref, + .ret, + .bitcast, + .not, + .isnonnull, + .isnull, + .iserr, + .ptrtoint, + .floatcast, + .intcast, + .load, + .unwrap_optional, + .wrap_optional, + => { + const un_op = inst.cast(ir.Inst.UnOp).?; + if (dtz.partial_inst_table.get(un_op.operand)) |operand_index| { + try writer.print("%{d})\n", .{operand_index}); + } else if (dtz.const_table.get(un_op.operand)) |operand_index| { + try writer.print("@{d})\n", .{operand_index}); + } else if (dtz.inst_table.get(un_op.operand)) |operand_index| { + try writer.print("%{d}) // Instruction does not dominate all uses!\n", .{ + operand_index, + }); + } else { + try writer.writeAll("!BADREF!)\n"); + } + }, + + .add, + .sub, + .cmp_lt, + .cmp_lte, + .cmp_eq, + .cmp_gte, + .cmp_gt, + .cmp_neq, + .store, + .booland, + .boolor, + .bitand, + .bitor, + .xor, + => { + var lhs_kinky: ?usize = null; + var rhs_kinky: ?usize = null; + + const bin_op = inst.cast(ir.Inst.BinOp).?; + if (dtz.partial_inst_table.get(bin_op.lhs)) |operand_index| { + try writer.print("%{d}, ", .{operand_index}); + } else if (dtz.const_table.get(bin_op.lhs)) |operand_index| { + try writer.print("@{d}, ", .{operand_index}); + } else if (dtz.inst_table.get(bin_op.lhs)) |operand_index| { + lhs_kinky = operand_index; + try writer.print("%{d}, ", .{operand_index}); + } else { + try writer.writeAll("!BADREF!, "); + } + if (dtz.partial_inst_table.get(bin_op.rhs)) |operand_index| { + try writer.print("%{d}", .{operand_index}); + } else if (dtz.const_table.get(bin_op.rhs)) |operand_index| { + try writer.print("@{d}", .{operand_index}); + } else if (dtz.inst_table.get(bin_op.rhs)) |operand_index| { + rhs_kinky = operand_index; + try writer.print("%{d}", .{operand_index}); + } else { + try writer.writeAll("!BADREF!"); + } + if (lhs_kinky != null or rhs_kinky != null) { + try writer.writeAll(") // Instruction does not dominate all uses!"); + if (lhs_kinky) |lhs| { + try writer.print(" %{d}", .{lhs}); + } + if (rhs_kinky) |rhs| { + try writer.print(" %{d}", .{rhs}); + } + try writer.writeAll("\n"); + } else { + try writer.writeAll(")\n"); + } + }, + + .arg => { + const arg = inst.castTag(.arg).?; + try writer.print("{s})\n", .{arg.name}); + }, + + .br => { + const br = inst.castTag(.br).?; + + var lhs_kinky: ?usize = null; + var rhs_kinky: ?usize = null; + + if (dtz.partial_inst_table.get(&br.block.base)) |operand_index| { + try writer.print("%{d}, ", .{operand_index}); + } else if (dtz.const_table.get(&br.block.base)) |operand_index| { + try writer.print("@{d}, ", .{operand_index}); + } else if (dtz.inst_table.get(&br.block.base)) |operand_index| { + lhs_kinky = operand_index; + try writer.print("%{d}, ", .{operand_index}); + } else { + try writer.writeAll("!BADREF!, "); + } + + if (dtz.partial_inst_table.get(br.operand)) |operand_index| { + try writer.print("%{d}", .{operand_index}); + } else if (dtz.const_table.get(br.operand)) |operand_index| { + try writer.print("@{d}", .{operand_index}); + } else if (dtz.inst_table.get(br.operand)) |operand_index| { + rhs_kinky = operand_index; + try writer.print("%{d}", .{operand_index}); + } else { + try writer.writeAll("!BADREF!"); + } + + if (lhs_kinky != null or rhs_kinky != null) { + try writer.writeAll(") // Instruction does not dominate all uses!"); + if (lhs_kinky) |lhs| { + try writer.print(" %{d}", .{lhs}); + } + if (rhs_kinky) |rhs| { + try writer.print(" %{d}", .{rhs}); + } + try writer.writeAll("\n"); + } else { + try writer.writeAll(")\n"); + } + }, + + .brvoid => { + const brvoid = inst.castTag(.brvoid).?; + if (dtz.partial_inst_table.get(&brvoid.block.base)) |operand_index| { + try writer.print("%{d})\n", .{operand_index}); + } else if (dtz.const_table.get(&brvoid.block.base)) |operand_index| { + try writer.print("@{d})\n", .{operand_index}); + } else if (dtz.inst_table.get(&brvoid.block.base)) |operand_index| { + try writer.print("%{d}) // Instruction does not dominate all uses!\n", .{ + operand_index, + }); + } else { + try writer.writeAll("!BADREF!)\n"); + } + }, + + // TODO fill out this debug printing + .assembly, + .block, + .call, + .condbr, + .constant, + .loop, + .varptr, + .switchbr, + => { + try writer.writeAll("!TODO!)\n"); + }, + } + } + } + + fn findConst(dtz: *DumpTzir, operand: *ir.Inst) !void { + if (operand.tag == .constant) { + try dtz.const_table.put(operand, dtz.next_const_index); + dtz.next_const_index += 1; + } + } +}; + const EmitZIR = struct { allocator: *Allocator, arena: std.heap.ArenaAllocator, @@ -2073,11 +2361,12 @@ const EmitZIR = struct { var instructions = std.ArrayList(*Inst).init(self.allocator); defer instructions.deinit(); - switch (module_fn.analysis) { + switch (module_fn.state) { .queued => unreachable, .in_progress => unreachable, - .success => |body| { - try self.emitBody(body, &inst_table, &instructions); + .inline_only => unreachable, + .success => { + try self.emitBody(module_fn.body, &inst_table, &instructions); }, .sema_failure => { const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?; @@ -2155,7 +2444,9 @@ const EmitZIR = struct { .fn_type = fn_type.inst, .body = .{ .instructions = arena_instrs }, }, - .kw_args = .{}, + .kw_args = .{ + .is_inline = module_fn.state == .inline_only, + }, }; return self.emitUnnamedDecl(&fn_inst.base); } @@ -2203,7 +2494,7 @@ const EmitZIR = struct { }; return self.emitStringLiteral(src, bytes); }, - else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {}", .{@tagName(t)}), + else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {s}", .{@tagName(t)}), } }, .ComptimeInt => return self.emitComptimeIntVal(src, typed_value.val), @@ -2274,7 +2565,7 @@ const EmitZIR = struct { }; return self.emitUnnamedDecl(&inst.base); }, - else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}), + else => |t| std.debug.panic("TODO implement emitTypedValue for {s}", .{@tagName(t)}), } } @@ -2865,7 +3156,7 @@ const EmitZIR = struct { fn autoName(self: *EmitZIR) ![]u8 { while (true) { - const proposed_name = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${}", .{self.next_auto_name}); + const proposed_name = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${d}", .{self.next_auto_name}); self.next_auto_name += 1; const gop = try self.names.getOrPut(proposed_name); if (!gop.found_existing) { @@ -2947,25 +3238,25 @@ pub fn dumpZir(allocator: *Allocator, kind: []const u8, decl_name: [*:0]const u8 try write.inst_table.ensureCapacity(@intCast(u32, instructions.len)); const stderr = std.io.getStdErr().outStream(); - try stderr.print("{} {s} {{ // unanalyzed\n", .{ kind, decl_name }); + try stderr.print("{s} {s} {{ // unanalyzed\n", .{ kind, decl_name }); for (instructions) |inst| { const my_i = write.next_instr_index; write.next_instr_index += 1; if (inst.cast(Inst.Block)) |block| { - const name = try std.fmt.allocPrint(&write.arena.allocator, "label_{}", .{my_i}); + const name = try std.fmt.allocPrint(&write.arena.allocator, "label_{d}", .{my_i}); try write.block_table.put(block, name); } else if (inst.cast(Inst.Loop)) |loop| { - const name = try std.fmt.allocPrint(&write.arena.allocator, "loop_{}", .{my_i}); + const name = try std.fmt.allocPrint(&write.arena.allocator, "loop_{d}", .{my_i}); try write.loop_table.put(loop, name); } try write.inst_table.putNoClobber(inst, .{ .inst = inst, .index = my_i, .name = "inst" }); - try stderr.print(" %{} ", .{my_i}); + try stderr.print(" %{d} ", .{my_i}); try write.writeInstToStream(stderr, inst); try stderr.writeByte('\n'); } - try stderr.print("}} // {} {s}\n\n", .{ kind, decl_name }); + try stderr.print("}} // {s} {s}\n\n", .{ kind, decl_name }); } diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 365b07247a50..b915a74d0a5a 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -81,11 +81,9 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .mut_slice_type => return analyzeInstSimplePtrType(mod, scope, old_inst.castTag(.mut_slice_type).?, true, .Slice), .ptr_type => return analyzeInstPtrType(mod, scope, old_inst.castTag(.ptr_type).?), .store => return analyzeInstStore(mod, scope, old_inst.castTag(.store).?), + .set_eval_branch_quota => return analyzeInstSetEvalBranchQuota(mod, scope, old_inst.castTag(.set_eval_branch_quota).?), .str => return analyzeInstStr(mod, scope, old_inst.castTag(.str).?), - .int => { - const big_int = old_inst.castTag(.int).?.positionals.int; - return mod.constIntBig(scope, old_inst.src, Type.initTag(.comptime_int), big_int); - }, + .int => return analyzeInstInt(mod, scope, old_inst.castTag(.int).?), .inttype => return analyzeInstIntType(mod, scope, old_inst.castTag(.inttype).?), .loop => return analyzeInstLoop(mod, scope, old_inst.castTag(.loop).?), .param_type => return analyzeInstParamType(mod, scope, old_inst.castTag(.param_type).?), @@ -159,16 +157,14 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! } } -pub fn analyzeBody(mod: *Module, scope: *Scope, body: zir.Module.Body) !void { - for (body.instructions) |src_inst, i| { - const analyzed_inst = try analyzeInst(mod, scope, src_inst); - src_inst.analyzed_inst = analyzed_inst; +pub fn analyzeBody(mod: *Module, block: *Scope.Block, body: zir.Module.Body) !void { + const tracy = trace(@src()); + defer tracy.end(); + + for (body.instructions) |src_inst| { + const analyzed_inst = try analyzeInst(mod, &block.base, src_inst); + try block.inst_table.putNoClobber(src_inst, analyzed_inst); if (analyzed_inst.ty.zigTypeTag() == .NoReturn) { - for (body.instructions[i..]) |unreachable_inst| { - if (unreachable_inst.castTag(.dbg_stmt)) |dbg_stmt| { - return mod.fail(scope, dbg_stmt.base.src, "unreachable code", .{}); - } - } break; } } @@ -180,8 +176,8 @@ pub fn analyzeBodyValueAsType( zir_result_inst: *zir.Inst, body: zir.Module.Body, ) !Type { - try analyzeBody(mod, &block_scope.base, body); - const result_inst = zir_result_inst.analyzed_inst.?; + try analyzeBody(mod, block_scope, body); + const result_inst = block_scope.inst_table.get(zir_result_inst).?; const val = try mod.resolveConstValue(&block_scope.base, result_inst); return val.toType(block_scope.base.arena()); } @@ -264,30 +260,9 @@ fn resolveCompleteZirDecl(mod: *Module, scope: *Scope, src_decl: *zir.Decl) Inne return decl; } -/// TODO Look into removing this function. The body is only needed for .zir files, not .zig files. -pub fn resolveInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Inst { - if (old_inst.analyzed_inst) |inst| return inst; - - // If this assert trips, the instruction that was referenced did not get properly - // analyzed before it was referenced. - const zir_module = scope.namespace().cast(Scope.ZIRModule).?; - const entry = if (old_inst.cast(zir.Inst.DeclVal)) |declval| blk: { - const decl_name = declval.positionals.name; - const entry = zir_module.contents.module.findDecl(decl_name) orelse - return mod.fail(scope, old_inst.src, "decl '{}' not found", .{decl_name}); - break :blk entry; - } else blk: { - // If this assert trips, the instruction that was referenced did not get - // properly analyzed by a previous instruction analysis before it was - // referenced by the current one. - break :blk zir_module.contents.module.findInstDecl(old_inst).?; - }; - const decl = try resolveCompleteZirDecl(mod, scope, entry.decl); - const decl_ref = try mod.analyzeDeclRef(scope, old_inst.src, decl); - // Note: it would be tempting here to store the result into old_inst.analyzed_inst field, - // but this would prevent the analyzeDeclRef from happening, which is needed to properly - // detect Decl dependencies and dependency failures on updates. - return mod.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src); +pub fn resolveInst(mod: *Module, scope: *Scope, zir_inst: *zir.Inst) InnerError!*Inst { + const block = scope.cast(Scope.Block).?; + return block.inst_table.get(zir_inst).?; // Instruction does not dominate all uses! } fn resolveConstString(mod: *Module, scope: *Scope, old_inst: *zir.Inst) ![]u8 { @@ -306,6 +281,24 @@ fn resolveType(mod: *Module, scope: *Scope, old_inst: *zir.Inst) !Type { return val.toType(scope.arena()); } +/// Appropriate to call when the coercion has already been done by result +/// location semantics. Asserts the value fits in the provided `Int` type. +/// Only supports `Int` types 64 bits or less. +fn resolveAlreadyCoercedInt( + mod: *Module, + scope: *Scope, + old_inst: *zir.Inst, + comptime Int: type, +) !Int { + comptime assert(@typeInfo(Int).Int.bits <= 64); + const new_inst = try resolveInst(mod, scope, old_inst); + const val = try mod.resolveConstValue(scope, new_inst); + switch (@typeInfo(Int).Int.signedness) { + .signed => return @intCast(Int, val.toSignedInt()), + .unsigned => return @intCast(Int, val.toUnsignedInt()), + } +} + fn resolveInt(mod: *Module, scope: *Scope, old_inst: *zir.Inst, dest_type: Type) !u64 { const new_inst = try resolveInst(mod, scope, old_inst); const coerced = try mod.coerce(scope, dest_type, new_inst); @@ -324,6 +317,8 @@ pub fn resolveInstConst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerE } fn analyzeInstConst(mod: *Module, scope: *Scope, const_inst: *zir.Inst.Const) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions // after analysis. const typed_value_copy = try const_inst.positionals.typed_value.copy(scope.arena()); @@ -343,29 +338,41 @@ fn analyzeInstCoerceResultBlockPtr( scope: *Scope, inst: *zir.Inst.CoerceResultBlockPtr, ) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultBlockPtr", .{}); } fn analyzeInstBitCastRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastRef", .{}); } fn analyzeInstBitCastResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastResultPtr", .{}); } fn analyzeInstCoerceResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{}); } /// Equivalent to `as(ptr_child_type(typeof(ptr)), value)`. fn analyzeInstCoerceToPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const ptr = try resolveInst(mod, scope, inst.positionals.ptr); const operand = try resolveInst(mod, scope, inst.positionals.value); return mod.coerce(scope, ptr.ty.elemType(), operand); } fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const b = try mod.requireFunctionBlock(scope, inst.base.src); const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; const ret_type = fn_ty.fnReturnType(); @@ -374,6 +381,8 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr } fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); const ptr_type = try mod.simplePtrType(scope, inst.base.src, operand.ty, false, .One); @@ -389,6 +398,8 @@ fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError! } fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const b = try mod.requireFunctionBlock(scope, inst.base.src); const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; const ret_type = fn_ty.fnReturnType(); @@ -396,6 +407,8 @@ fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerEr } fn analyzeInstEnsureResultUsed(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); switch (operand.ty.zigTypeTag()) { .Void, .NoReturn => return mod.constVoid(scope, operand.src), @@ -404,6 +417,8 @@ fn analyzeInstEnsureResultUsed(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp } fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); switch (operand.ty.zigTypeTag()) { .ErrorSet, .ErrorUnion => return mod.fail(scope, operand.src, "error is discarded", .{}), @@ -412,6 +427,8 @@ fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst. } fn analyzeInstEnsureIndexable(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); const elem_ty = operand.ty.elemType(); if (elem_ty.isIndexable()) { @@ -425,6 +442,8 @@ fn analyzeInstEnsureIndexable(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) } fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const var_type = try resolveType(mod, scope, inst.positionals.operand); const ptr_type = try mod.simplePtrType(scope, inst.base.src, var_type, true, .One); const b = try mod.requireRuntimeBlock(scope, inst.base.src); @@ -432,6 +451,8 @@ fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerErro } fn analyzeInstAllocMut(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const var_type = try resolveType(mod, scope, inst.positionals.operand); try mod.validateVarType(scope, inst.base.src, var_type); const ptr_type = try mod.simplePtrType(scope, inst.base.src, var_type, true, .One); @@ -445,6 +466,8 @@ fn analyzeInstAllocInferred( inst: *zir.Inst.NoOp, mut_tag: Type.Tag, ) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const val_payload = try scope.arena().create(Value.Payload.InferredAlloc); val_payload.* = .{ .data = .{}, @@ -471,6 +494,8 @@ fn analyzeInstResolveInferredAlloc( scope: *Scope, inst: *zir.Inst.UnOp, ) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const ptr = try resolveInst(mod, scope, inst.positionals.operand); const ptr_val = ptr.castTag(.constant).?.val; const inferred_alloc = ptr_val.castTag(.inferred_alloc).?; @@ -498,6 +523,8 @@ fn analyzeInstStoreToInferredPtr( scope: *Scope, inst: *zir.Inst.BinOp, ) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const ptr = try resolveInst(mod, scope, inst.positionals.lhs); const value = try resolveInst(mod, scope, inst.positionals.rhs); const inferred_alloc = ptr.castTag(.constant).?.val.castTag(.inferred_alloc).?; @@ -512,13 +539,29 @@ fn analyzeInstStoreToInferredPtr( return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value); } +fn analyzeInstSetEvalBranchQuota( + mod: *Module, + scope: *Scope, + inst: *zir.Inst.UnOp, +) InnerError!*Inst { + const b = try mod.requireFunctionBlock(scope, inst.base.src); + const quota = try resolveAlreadyCoercedInt(mod, scope, inst.positionals.operand, u32); + if (b.branch_quota.* < quota) + b.branch_quota.* = quota; + return mod.constVoid(scope, inst.base.src); +} + fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const ptr = try resolveInst(mod, scope, inst.positionals.lhs); const value = try resolveInst(mod, scope, inst.positionals.rhs); return mod.storePtr(scope, inst.base.src, ptr, value); } fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const fn_inst = try resolveInst(mod, scope, inst.positionals.func); const arg_index = inst.positionals.arg_index; @@ -535,7 +578,7 @@ fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) // TODO support C-style var args const param_count = fn_ty.fnParamLen(); if (arg_index >= param_count) { - return mod.fail(scope, inst.base.src, "arg index {} out of bounds; '{}' has {} argument(s)", .{ + return mod.fail(scope, inst.base.src, "arg index {d} out of bounds; '{}' has {d} argument(s)", .{ arg_index, fn_ty, param_count, @@ -548,6 +591,8 @@ fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) } fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); // The bytes references memory inside the ZIR module, which can get deallocated // after semantic analysis is complete. We need the memory to be in the new anonymous Decl's arena. var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); @@ -561,26 +606,44 @@ fn analyzeInstStr(mod: *Module, scope: *Scope, str_inst: *zir.Inst.Str) InnerErr return mod.analyzeDeclRef(scope, str_inst.base.src, new_decl); } +fn analyzeInstInt(mod: *Module, scope: *Scope, inst: *zir.Inst.Int) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); + + return mod.constIntBig(scope, inst.base.src, Type.initTag(.comptime_int), inst.positionals.int); +} + fn analyzeInstExport(mod: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const symbol_name = try resolveConstString(mod, scope, export_inst.positionals.symbol_name); const exported_decl = mod.lookupDeclName(scope, export_inst.positionals.decl_name) orelse - return mod.fail(scope, export_inst.base.src, "decl '{}' not found", .{export_inst.positionals.decl_name}); + return mod.fail(scope, export_inst.base.src, "decl '{s}' not found", .{export_inst.positionals.decl_name}); try mod.analyzeExport(scope, export_inst.base.src, symbol_name, exported_decl); return mod.constVoid(scope, export_inst.base.src); } fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const msg = try resolveConstString(mod, scope, inst.positionals.operand); - return mod.fail(scope, inst.base.src, "{}", .{msg}); + return mod.fail(scope, inst.base.src, "{s}", .{msg}); } fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst { - const b = try mod.requireRuntimeBlock(scope, inst.base.src); + const tracy = trace(@src()); + defer tracy.end(); + const b = try mod.requireFunctionBlock(scope, inst.base.src); + if (b.inlining) |inlining| { + const param_index = inlining.param_index; + inlining.param_index += 1; + return inlining.casted_args[param_index]; + } const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; const param_index = b.instructions.items.len; const param_count = fn_ty.fnParamLen(); if (param_index >= param_count) { - return mod.fail(scope, inst.base.src, "parameter index {} outside list of length {}", .{ + return mod.fail(scope, inst.base.src, "parameter index {d} outside list of length {d}", .{ param_index, param_count, }); @@ -591,6 +654,8 @@ fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!* } fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const parent_block = scope.cast(Scope.Block).?; // Reserve space for a Loop instruction so that generated Break instructions can @@ -608,15 +673,18 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError var child_block: Scope.Block = .{ .parent = parent_block, + .inst_table = parent_block.inst_table, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, .arena = parent_block.arena, + .inlining = parent_block.inlining, .is_comptime = parent_block.is_comptime, + .branch_quota = parent_block.branch_quota, }; defer child_block.instructions.deinit(mod.gpa); - try analyzeBody(mod, &child_block.base, inst.positionals.body); + try analyzeBody(mod, &child_block, inst.positionals.body); // Loop repetition is implied so the last instruction may or may not be a noreturn instruction. @@ -626,20 +694,25 @@ fn analyzeInstLoop(mod: *Module, scope: *Scope, inst: *zir.Inst.Loop) InnerError } fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const parent_block = scope.cast(Scope.Block).?; var child_block: Scope.Block = .{ .parent = parent_block, + .inst_table = parent_block.inst_table, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, .arena = parent_block.arena, .label = null, + .inlining = parent_block.inlining, .is_comptime = parent_block.is_comptime or is_comptime, + .branch_quota = parent_block.branch_quota, }; defer child_block.instructions.deinit(mod.gpa); - try analyzeBody(mod, &child_block.base, inst.positionals.body); + try analyzeBody(mod, &child_block, inst.positionals.body); try parent_block.instructions.appendSlice(mod.gpa, child_block.instructions.items); @@ -651,6 +724,8 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c } fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const parent_block = scope.cast(Scope.Block).?; // Reserve space for a Block instruction so that generated Break instructions can @@ -668,6 +743,7 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt var child_block: Scope.Block = .{ .parent = parent_block, + .inst_table = parent_block.inst_table, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, @@ -675,38 +751,57 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt // TODO @as here is working around a stage1 miscompilation bug :( .label = @as(?Scope.Block.Label, Scope.Block.Label{ .zir_block = inst, - .results = .{}, - .block_inst = block_inst, + .merges = .{ + .results = .{}, + .block_inst = block_inst, + }, }), + .inlining = parent_block.inlining, .is_comptime = is_comptime or parent_block.is_comptime, + .branch_quota = parent_block.branch_quota, }; - const label = &child_block.label.?; + const merges = &child_block.label.?.merges; defer child_block.instructions.deinit(mod.gpa); - defer label.results.deinit(mod.gpa); + defer merges.results.deinit(mod.gpa); + + try analyzeBody(mod, &child_block, inst.positionals.body); + + return analyzeBlockBody(mod, scope, &child_block, merges); +} + +fn analyzeBlockBody( + mod: *Module, + scope: *Scope, + child_block: *Scope.Block, + merges: *Scope.Block.Merges, +) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); - try analyzeBody(mod, &child_block.base, inst.positionals.body); + const parent_block = scope.cast(Scope.Block).?; // Blocks must terminate with noreturn instruction. assert(child_block.instructions.items.len != 0); assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn()); - if (label.results.items.len == 0) { - // No need for a block instruction. We can put the new instructions directly into the parent block. + if (merges.results.items.len == 0) { + // No need for a block instruction. We can put the new instructions + // directly into the parent block. const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items); try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); return copied_instructions[copied_instructions.len - 1]; } - if (label.results.items.len == 1) { + if (merges.results.items.len == 1) { const last_inst_index = child_block.instructions.items.len - 1; const last_inst = child_block.instructions.items[last_inst_index]; if (last_inst.breakBlock()) |br_block| { - if (br_block == block_inst) { + if (br_block == merges.block_inst) { // No need for a block instruction. We can put the new instructions directly into the parent block. // Here we omit the break instruction. const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]); try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); - return label.results.items[0]; + return merges.results.items[0]; } } } @@ -715,30 +810,38 @@ fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_compt // Need to set the type and emit the Block instruction. This allows machine code generation // to emit a jump instruction to after the block when it encounters the break. - try parent_block.instructions.append(mod.gpa, &block_inst.base); - block_inst.base.ty = try mod.resolvePeerTypes(scope, label.results.items); - block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) }; - return &block_inst.base; + try parent_block.instructions.append(mod.gpa, &merges.block_inst.base); + merges.block_inst.base.ty = try mod.resolvePeerTypes(scope, merges.results.items); + merges.block_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) }; + return &merges.block_inst.base; } fn analyzeInstBreakpoint(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const b = try mod.requireRuntimeBlock(scope, inst.base.src); return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .breakpoint); } fn analyzeInstBreak(mod: *Module, scope: *Scope, inst: *zir.Inst.Break) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); const block = inst.positionals.block; return analyzeBreak(mod, scope, inst.base.src, block, operand); } fn analyzeInstBreakVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.BreakVoid) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const block = inst.positionals.block; const void_inst = try mod.constVoid(scope, inst.base.src); return analyzeBreak(mod, scope, inst.base.src, block, void_inst); } fn analyzeInstDbgStmt(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); if (scope.cast(Scope.Block)) |b| { if (!b.is_comptime) { return mod.addNoOp(b, inst.base.src, Type.initTag(.void), .dbg_stmt); @@ -748,26 +851,37 @@ fn analyzeInstDbgStmt(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerEr } fn analyzeInstDeclRefStr(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const decl_name = try resolveConstString(mod, scope, inst.positionals.name); return mod.analyzeDeclRefByName(scope, inst.base.src, decl_name); } fn analyzeInstDeclRef(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.analyzeDeclRefByName(scope, inst.base.src, inst.positionals.name); } fn analyzeInstDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const decl = try analyzeDeclVal(mod, scope, inst); const ptr = try mod.analyzeDeclRef(scope, inst.base.src, decl); return mod.analyzeDeref(scope, inst.base.src, ptr, inst.base.src); } fn analyzeInstDeclValInModule(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclValInModule) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const decl = inst.positionals.decl; return mod.analyzeDeclRef(scope, inst.base.src, decl); } fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); + const func = try resolveInst(mod, scope, inst.positionals.func); if (func.ty.zigTypeTag() != .Fn) return mod.fail(scope, inst.positionals.func.src, "type '{}' not a function", .{func.ty}); @@ -790,7 +904,7 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError return mod.fail( scope, inst.positionals.func.src, - "expected at least {} argument(s), found {}", + "expected at least {d} argument(s), found {d}", .{ fn_params_len, call_params_len }, ); } @@ -800,7 +914,7 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError return mod.fail( scope, inst.positionals.func.src, - "expected {} argument(s), found {}", + "expected {d} argument(s), found {d}", .{ fn_params_len, call_params_len }, ); } @@ -826,28 +940,105 @@ fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError const ret_type = func.ty.fnReturnType(); - const b = try mod.requireRuntimeBlock(scope, inst.base.src); + const b = try mod.requireFunctionBlock(scope, inst.base.src); + const is_comptime_call = b.is_comptime or inst.kw_args.modifier == .compile_time; + const is_inline_call = is_comptime_call or inst.kw_args.modifier == .always_inline or blk: { + // This logic will get simplified by + // https://github.com/ziglang/zig/issues/6429 + if (try mod.resolveDefinedValue(scope, func)) |func_val| { + const module_fn = switch (func_val.tag()) { + .function => func_val.castTag(.function).?.data, + else => break :blk false, + }; + break :blk module_fn.state == .inline_only; + } + break :blk false; + }; + if (is_inline_call) { + const func_val = try mod.resolveConstValue(scope, func); + const module_fn = switch (func_val.tag()) { + .function => func_val.castTag(.function).?.data, + .extern_fn => return mod.fail(scope, inst.base.src, "{s} call of extern function", .{ + @as([]const u8, if (is_comptime_call) "comptime" else "inline"), + }), + else => unreachable, + }; + + // Analyze the ZIR. The same ZIR gets analyzed into a runtime function + // or an inlined call depending on what union tag the `label` field is + // set to in the `Scope.Block`. + // This block instruction will be used to capture the return value from the + // inlined function. + const block_inst = try scope.arena().create(Inst.Block); + block_inst.* = .{ + .base = .{ + .tag = Inst.Block.base_tag, + .ty = ret_type, + .src = inst.base.src, + }, + .body = undefined, + }; + // If this is the top of the inline/comptime call stack, we use this data. + // Otherwise we pass on the shared data from the parent scope. + var shared_inlining = Scope.Block.Inlining.Shared{ + .branch_count = 0, + .caller = b.func, + }; + // This one is shared among sub-blocks within the same callee, but not + // shared among the entire inline/comptime call stack. + var inlining = Scope.Block.Inlining{ + .shared = if (b.inlining) |inlining| inlining.shared else &shared_inlining, + .param_index = 0, + .casted_args = casted_args, + .merges = .{ + .results = .{}, + .block_inst = block_inst, + }, + }; + var inst_table = Scope.Block.InstTable.init(mod.gpa); + defer inst_table.deinit(); + + var child_block: Scope.Block = .{ + .parent = null, + .inst_table = &inst_table, + .func = module_fn, + // Note that we pass the caller's Decl, not the callee. This causes + // compile errors to be attached (correctly) to the caller's Decl. + .decl = scope.decl().?, + .instructions = .{}, + .arena = scope.arena(), + .label = null, + .inlining = &inlining, + .is_comptime = is_comptime_call, + .branch_quota = b.branch_quota, + }; + + const merges = &child_block.inlining.?.merges; + + defer child_block.instructions.deinit(mod.gpa); + defer merges.results.deinit(mod.gpa); + + try mod.emitBackwardBranch(&child_block, inst.base.src); + + // This will have return instructions analyzed as break instructions to + // the block_inst above. + try analyzeBody(mod, &child_block, module_fn.zir); + + return analyzeBlockBody(mod, scope, &child_block, merges); + } + return mod.addCall(b, inst.base.src, ret_type, func, casted_args); } fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const fn_type = try resolveType(mod, scope, fn_inst.positionals.fn_type); - const fn_zir = blk: { - var fn_arena = std.heap.ArenaAllocator.init(mod.gpa); - errdefer fn_arena.deinit(); - - const fn_zir = try scope.arena().create(Module.Fn.ZIR); - fn_zir.* = .{ - .body = .{ - .instructions = fn_inst.positionals.body.instructions, - }, - .arena = fn_arena.state, - }; - break :blk fn_zir; - }; const new_func = try scope.arena().create(Module.Fn); new_func.* = .{ - .analysis = .{ .queued = fn_zir }, + .state = if (fn_inst.kw_args.is_inline) .inline_only else .queued, + .zir = fn_inst.positionals.body, + .body = undefined, .owner_decl = scope.decl().?, }; return mod.constInst(scope, fn_inst.base.src, .{ @@ -857,16 +1048,22 @@ fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError! } fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{}); } fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const child_type = try resolveType(mod, scope, optional.positionals.operand); return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type)); } fn analyzeInstArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); // TODO these should be lazily evaluated const len = try resolveInstConst(mod, scope, array.positionals.lhs); const elem_type = try resolveType(mod, scope, array.positionals.rhs); @@ -875,6 +1072,8 @@ fn analyzeInstArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) Inn } fn analyzeInstArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.ArrayTypeSentinel) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); // TODO these should be lazily evaluated const len = try resolveInstConst(mod, scope, array.positionals.len); const sentinel = try resolveInstConst(mod, scope, array.positionals.sentinel); @@ -884,6 +1083,8 @@ fn analyzeInstArrayTypeSentinel(mod: *Module, scope: *Scope, array: *zir.Inst.Ar } fn analyzeInstErrorUnionType(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const error_union = try resolveType(mod, scope, inst.positionals.lhs); const payload = try resolveType(mod, scope, inst.positionals.rhs); @@ -895,12 +1096,16 @@ fn analyzeInstErrorUnionType(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) } fn analyzeInstAnyframeType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const return_type = try resolveType(mod, scope, inst.positionals.operand); return mod.constType(scope, inst.base.src, try mod.anyframeType(scope, return_type)); } fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); // The declarations arena will store the hashmap. var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); errdefer new_decl_arena.deinit(); @@ -918,7 +1123,7 @@ fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) In for (inst.positionals.fields) |field_name| { const entry = try mod.getErrorValue(field_name); if (payload.data.fields.fetchPutAssumeCapacity(entry.key, entry.value)) |prev| { - return mod.fail(scope, inst.base.src, "duplicate error: '{}'", .{field_name}); + return mod.fail(scope, inst.base.src, "duplicate error: '{s}'", .{field_name}); } } // TODO create name in format "error:line:column" @@ -931,6 +1136,9 @@ fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) In } fn analyzeInstMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); + const rhs_fields = (try resolveType(mod, scope, inst.positionals.rhs)).getErrs(); const lhs_fields = (try resolveType(mod, scope, inst.positionals.lhs)).getErrs(); if (lhs_fields == .anyerror or rhs_fields == .anyerror) @@ -1001,6 +1209,8 @@ fn analyzeInstMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) } fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiteral) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const duped_name = try scope.arena().dupe(u8, inst.positionals.name); return mod.constInst(scope, inst.base.src, .{ .ty = Type.initTag(.enum_literal), @@ -1009,6 +1219,8 @@ fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiter } fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, unwrap.positionals.operand); assert(operand.ty.zigTypeTag() == .Pointer); @@ -1039,18 +1251,26 @@ fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp } fn analyzeInstUnwrapErr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErr", .{}); } fn analyzeInstUnwrapErrCode(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErrCode", .{}); } fn analyzeInstEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstEnsureErrPayloadVoid", .{}); } fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const return_type = try resolveType(mod, scope, fntype.positionals.return_type); // Hot path for some common function types. @@ -1092,16 +1312,22 @@ fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) Inne } fn analyzeInstPrimitive(mod: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue()); } fn analyzeInstAs(mod: *Module, scope: *Scope, as: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const dest_type = try resolveType(mod, scope, as.positionals.lhs); const new_inst = try resolveInst(mod, scope, as.positionals.rhs); return mod.coerce(scope, dest_type, new_inst); } fn analyzeInstPtrToInt(mod: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const ptr = try resolveInst(mod, scope, ptrtoint.positionals.operand); if (ptr.ty.zigTypeTag() != .Pointer) { return mod.fail(scope, ptrtoint.positionals.operand.src, "expected pointer, found '{}'", .{ptr.ty}); @@ -1113,6 +1339,8 @@ fn analyzeInstPtrToInt(mod: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) In } fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const object_ptr = try resolveInst(mod, scope, fieldptr.positionals.object_ptr); const field_name = try resolveConstString(mod, scope, fieldptr.positionals.field_name); @@ -1134,7 +1362,7 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr return mod.fail( scope, fieldptr.positionals.field_name.src, - "no member named '{}' in '{}'", + "no member named '{s}' in '{}'", .{ field_name, elem_ty }, ); } @@ -1155,7 +1383,7 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr return mod.fail( scope, fieldptr.positionals.field_name.src, - "no member named '{}' in '{}'", + "no member named '{s}' in '{}'", .{ field_name, elem_ty }, ); } @@ -1173,7 +1401,7 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr // TODO resolve inferred error sets const entry = if (val.castTag(.error_set)) |payload| (payload.data.fields.getEntry(field_name) orelse - return mod.fail(scope, fieldptr.base.src, "no error named '{}' in '{}'", .{ field_name, child_type })).* + return mod.fail(scope, fieldptr.base.src, "no error named '{s}' in '{}'", .{ field_name, child_type })).* else try mod.getErrorValue(field_name); @@ -1201,9 +1429,9 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr } if (&container_scope.file_scope.base == mod.root_scope) { - return mod.fail(scope, fieldptr.base.src, "root source file has no member called '{}'", .{field_name}); + return mod.fail(scope, fieldptr.base.src, "root source file has no member called '{s}'", .{field_name}); } else { - return mod.fail(scope, fieldptr.base.src, "container '{}' has no member called '{}'", .{ child_type, field_name }); + return mod.fail(scope, fieldptr.base.src, "container '{}' has no member called '{s}'", .{ child_type, field_name }); } }, else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{child_type}), @@ -1215,6 +1443,8 @@ fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr } fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const dest_type = try resolveType(mod, scope, inst.positionals.lhs); const operand = try resolveInst(mod, scope, inst.positionals.rhs); @@ -1251,12 +1481,16 @@ fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerE } fn analyzeInstBitCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const dest_type = try resolveType(mod, scope, inst.positionals.lhs); const operand = try resolveInst(mod, scope, inst.positionals.rhs); return mod.bitcast(scope, dest_type, operand); } fn analyzeInstFloatCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const dest_type = try resolveType(mod, scope, inst.positionals.lhs); const operand = try resolveInst(mod, scope, inst.positionals.rhs); @@ -1293,6 +1527,8 @@ fn analyzeInstFloatCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inne } fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr); const uncasted_index = try resolveInst(mod, scope, inst.positionals.index); const elem_index = try mod.coerce(scope, Type.initTag(.usize), uncasted_index); @@ -1329,6 +1565,8 @@ fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inne } fn analyzeInstSlice(mod: *Module, scope: *Scope, inst: *zir.Inst.Slice) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr); const start = try resolveInst(mod, scope, inst.positionals.start); const end = if (inst.kw_args.end) |end| try resolveInst(mod, scope, end) else null; @@ -1338,6 +1576,8 @@ fn analyzeInstSlice(mod: *Module, scope: *Scope, inst: *zir.Inst.Slice) InnerErr } fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const array_ptr = try resolveInst(mod, scope, inst.positionals.lhs); const start = try resolveInst(mod, scope, inst.positionals.rhs); @@ -1345,6 +1585,8 @@ fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn } fn analyzeInstSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const start = try resolveInst(mod, scope, inst.positionals.lhs); const end = try resolveInst(mod, scope, inst.positionals.rhs); @@ -1367,6 +1609,8 @@ fn analyzeInstSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) In } fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const target_ptr = try resolveInst(mod, scope, inst.positionals.target_ptr); const target = try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target_ptr.src); try validateSwitch(mod, scope, target, inst); @@ -1378,17 +1622,17 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In const item = try mod.resolveConstValue(scope, casted); if (target_val.eql(item)) { - try analyzeBody(mod, scope, case.body); + try analyzeBody(mod, scope.cast(Scope.Block).?, case.body); return mod.constNoReturn(scope, inst.base.src); } } - try analyzeBody(mod, scope, inst.positionals.else_body); + try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body); return mod.constNoReturn(scope, inst.base.src); } if (inst.positionals.cases.len == 0) { // no cases just analyze else_branch - try analyzeBody(mod, scope, inst.positionals.else_body); + try analyzeBody(mod, scope.cast(Scope.Block).?, inst.positionals.else_body); return mod.constNoReturn(scope, inst.base.src); } @@ -1397,11 +1641,14 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In var case_block: Scope.Block = .{ .parent = parent_block, + .inst_table = parent_block.inst_table, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, .arena = parent_block.arena, + .inlining = parent_block.inlining, .is_comptime = parent_block.is_comptime, + .branch_quota = parent_block.branch_quota, }; defer case_block.instructions.deinit(mod.gpa); @@ -1413,7 +1660,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In const casted = try mod.coerce(scope, target.ty, resolved); const item = try mod.resolveConstValue(scope, casted); - try analyzeBody(mod, &case_block.base, case.body); + try analyzeBody(mod, &case_block, case.body); cases[i] = .{ .item = item, @@ -1422,7 +1669,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In } case_block.instructions.items.len = 0; - try analyzeBody(mod, &case_block.base, inst.positionals.else_body); + try analyzeBody(mod, &case_block, inst.positionals.else_body); const else_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items), @@ -1595,28 +1842,34 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw } fn analyzeInstImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveConstString(mod, scope, inst.positionals.operand); const file_scope = mod.analyzeImport(scope, inst.base.src, operand) catch |err| switch (err) { error.ImportOutsidePkgPath => { - return mod.fail(scope, inst.base.src, "import of file outside package path: '{}'", .{operand}); + return mod.fail(scope, inst.base.src, "import of file outside package path: '{s}'", .{operand}); }, error.FileNotFound => { - return mod.fail(scope, inst.base.src, "unable to find '{}'", .{operand}); + return mod.fail(scope, inst.base.src, "unable to find '{s}'", .{operand}); }, else => { - // TODO user friendly error to string - return mod.fail(scope, inst.base.src, "unable to open '{}': {}", .{ operand, @errorName(err) }); + // TODO: make sure this gets retried and not cached + return mod.fail(scope, inst.base.src, "unable to open '{s}': {s}", .{ operand, @errorName(err) }); }, }; return mod.constType(scope, inst.base.src, file_scope.root_container.ty); } fn analyzeInstShl(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShl", .{}); } fn analyzeInstShr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstShr", .{}); } @@ -1641,7 +1894,7 @@ fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerE if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) { if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { - return mod.fail(scope, inst.base.src, "vector length mismatch: {} and {}", .{ + return mod.fail(scope, inst.base.src, "vector length mismatch: {d} and {d}", .{ lhs.ty.arrayLen(), rhs.ty.arrayLen(), }); @@ -1684,14 +1937,20 @@ fn analyzeInstBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerE } fn analyzeInstBitNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitNot", .{}); } fn analyzeInstArrayCat(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayCat", .{}); } fn analyzeInstArrayMul(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); return mod.fail(scope, inst.base.src, "TODO implement analyzeInstArrayMul", .{}); } @@ -1716,7 +1975,7 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) { if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { - return mod.fail(scope, inst.base.src, "vector length mismatch: {} and {}", .{ + return mod.fail(scope, inst.base.src, "vector length mismatch: {d} and {d}", .{ lhs.ty.arrayLen(), rhs.ty.arrayLen(), }); @@ -1733,7 +1992,7 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) { - return mod.fail(scope, inst.base.src, "invalid operands to binary expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); + return mod.fail(scope, inst.base.src, "invalid operands to binary expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); } if (casted_lhs.value()) |lhs_val| { @@ -1752,7 +2011,7 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn const ir_tag = switch (inst.base.tag) { .add => Inst.Tag.add, .sub => Inst.Tag.sub, - else => return mod.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{}''", .{@tagName(inst.base.tag)}), + else => return mod.fail(scope, inst.base.src, "TODO implement arithmetic for operand '{s}''", .{@tagName(inst.base.tag)}), }; return mod.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs); @@ -1770,24 +2029,26 @@ fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir } const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt; - const value = try switch (inst.base.tag) { + const value = switch (inst.base.tag) { .add => blk: { const val = if (is_int) - Module.intAdd(scope.arena(), lhs_val, rhs_val) + try Module.intAdd(scope.arena(), lhs_val, rhs_val) else - mod.floatAdd(scope, res_type, inst.base.src, lhs_val, rhs_val); + try mod.floatAdd(scope, res_type, inst.base.src, lhs_val, rhs_val); break :blk val; }, .sub => blk: { const val = if (is_int) - Module.intSub(scope.arena(), lhs_val, rhs_val) + try Module.intSub(scope.arena(), lhs_val, rhs_val) else - mod.floatSub(scope, res_type, inst.base.src, lhs_val, rhs_val); + try mod.floatSub(scope, res_type, inst.base.src, lhs_val, rhs_val); break :blk val; }, - else => return mod.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{}'", .{@tagName(inst.base.tag)}), + else => return mod.fail(scope, inst.base.src, "TODO Implement arithmetic operand '{s}'", .{@tagName(inst.base.tag)}), }; + log.debug("{s}({}, {}) result: {}", .{ @tagName(inst.base.tag), lhs_val, rhs_val, value }); + return mod.constInst(scope, inst.base.src, .{ .ty = res_type, .val = value, @@ -1795,11 +2056,15 @@ fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir } fn analyzeInstDeref(mod: *Module, scope: *Scope, deref: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const ptr = try resolveInst(mod, scope, deref.positionals.operand); return mod.analyzeDeref(scope, deref.base.src, ptr, deref.positionals.operand.src); } fn analyzeInstAsm(mod: *Module, scope: *Scope, assembly: *zir.Inst.Asm) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const return_type = try resolveType(mod, scope, assembly.positionals.return_type); const asm_source = try resolveConstString(mod, scope, assembly.positionals.asm_source); const output = if (assembly.kw_args.output) |o| try resolveConstString(mod, scope, o) else null; @@ -1844,6 +2109,8 @@ fn analyzeInstCmp( inst: *zir.Inst.BinOp, op: std.math.CompareOperator, ) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const lhs = try resolveInst(mod, scope, inst.positionals.lhs); const rhs = try resolveInst(mod, scope, inst.positionals.rhs); @@ -1877,7 +2144,7 @@ fn analyzeInstCmp( return mod.fail(scope, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { if (!is_equality_cmp) { - return mod.fail(scope, inst.base.src, "{} operator not allowed for errors", .{@tagName(op)}); + return mod.fail(scope, inst.base.src, "{s} operator not allowed for errors", .{@tagName(op)}); } const lhs_val = lhs.value(); const rhs_val = rhs.value(); @@ -1892,7 +2159,7 @@ fn analyzeInstCmp( return mod.cmpNumeric(scope, inst.base.src, lhs, rhs, op); } else if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) { if (!is_equality_cmp) { - return mod.fail(scope, inst.base.src, "{} operator not allowed for types", .{@tagName(op)}); + return mod.fail(scope, inst.base.src, "{s} operator not allowed for types", .{@tagName(op)}); } return mod.constBool(scope, inst.base.src, lhs.value().?.eql(rhs.value().?) == (op == .eq)); } @@ -1900,11 +2167,15 @@ fn analyzeInstCmp( } fn analyzeInstTypeOf(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); return mod.constType(scope, inst.base.src, operand.ty); } fn analyzeInstTypeOfPeer(mod: *Module, scope: *Scope, inst: *zir.Inst.TypeOfPeer) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); var insts_to_res = try mod.gpa.alloc(*ir.Inst, inst.positionals.items.len); defer mod.gpa.free(insts_to_res); for (inst.positionals.items) |item, i| { @@ -1915,6 +2186,8 @@ fn analyzeInstTypeOfPeer(mod: *Module, scope: *Scope, inst: *zir.Inst.TypeOfPeer } fn analyzeInstBoolNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const uncasted_operand = try resolveInst(mod, scope, inst.positionals.operand); const bool_type = Type.initTag(.bool); const operand = try mod.coerce(scope, bool_type, uncasted_operand); @@ -1926,6 +2199,8 @@ fn analyzeInstBoolNot(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerEr } fn analyzeInstBoolOp(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const bool_type = Type.initTag(.bool); const uncasted_lhs = try resolveInst(mod, scope, inst.positionals.lhs); const lhs = try mod.coerce(scope, bool_type, uncasted_lhs); @@ -1948,48 +2223,60 @@ fn analyzeInstBoolOp(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerEr } fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic); } fn analyzeInstIsErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); return mod.analyzeIsErr(scope, inst.base.src, operand); } fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const uncasted_cond = try resolveInst(mod, scope, inst.positionals.condition); const cond = try mod.coerce(scope, Type.initTag(.bool), uncasted_cond); + const parent_block = scope.cast(Scope.Block).?; + if (try mod.resolveDefinedValue(scope, cond)) |cond_val| { const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body; - try analyzeBody(mod, scope, body.*); + try analyzeBody(mod, parent_block, body.*); return mod.constNoReturn(scope, inst.base.src); } - const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src); - var true_block: Scope.Block = .{ .parent = parent_block, + .inst_table = parent_block.inst_table, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, .arena = parent_block.arena, + .inlining = parent_block.inlining, .is_comptime = parent_block.is_comptime, + .branch_quota = parent_block.branch_quota, }; defer true_block.instructions.deinit(mod.gpa); - try analyzeBody(mod, &true_block.base, inst.positionals.then_body); + try analyzeBody(mod, &true_block, inst.positionals.then_body); var false_block: Scope.Block = .{ .parent = parent_block, + .inst_table = parent_block.inst_table, .func = parent_block.func, .decl = parent_block.decl, .instructions = .{}, .arena = parent_block.arena, + .inlining = parent_block.inlining, .is_comptime = parent_block.is_comptime, + .branch_quota = parent_block.branch_quota, }; defer false_block.instructions.deinit(mod.gpa); - try analyzeBody(mod, &false_block.base, inst.positionals.else_body); + try analyzeBody(mod, &false_block, inst.positionals.else_body); const then_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, true_block.instructions.items) }; const else_body: ir.Body = .{ .instructions = try scope.arena().dupe(*Inst, false_block.instructions.items) }; @@ -2002,6 +2289,8 @@ fn analyzeInstUnreachable( unreach: *zir.Inst.NoOp, safety_check: bool, ) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const b = try mod.requireRuntimeBlock(scope, unreach.base.src); // TODO Add compile error for @optimizeFor occurring too late in a scope. if (safety_check and mod.wantSafety(scope)) { @@ -2012,13 +2301,31 @@ fn analyzeInstUnreachable( } fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const operand = try resolveInst(mod, scope, inst.positionals.operand); - const b = try mod.requireRuntimeBlock(scope, inst.base.src); + const b = try mod.requireFunctionBlock(scope, inst.base.src); + + if (b.inlining) |inlining| { + // We are inlining a function call; rewrite the `ret` as a `break`. + try inlining.merges.results.append(mod.gpa, operand); + return mod.addBr(b, inst.base.src, inlining.merges.block_inst, operand); + } + return mod.addUnOp(b, inst.base.src, Type.initTag(.noreturn), .ret, operand); } fn analyzeInstRetVoid(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { - const b = try mod.requireRuntimeBlock(scope, inst.base.src); + const tracy = trace(@src()); + defer tracy.end(); + const b = try mod.requireFunctionBlock(scope, inst.base.src); + if (b.inlining) |inlining| { + // We are inlining a function call; rewrite the `retvoid` as a `breakvoid`. + const void_inst = try mod.constVoid(scope, inst.base.src); + try inlining.merges.results.append(mod.gpa, void_inst); + return mod.addBr(b, inst.base.src, inlining.merges.block_inst, void_inst); + } + if (b.func) |func| { // Need to emit a compile error if returning void is not allowed. const void_inst = try mod.constVoid(scope, inst.base.src); @@ -2050,9 +2357,9 @@ fn analyzeBreak( while (opt_block) |block| { if (block.label) |*label| { if (label.zir_block == zir_block) { - try label.results.append(mod.gpa, operand); - const b = try mod.requireRuntimeBlock(scope, src); - return mod.addBr(b, src, label.block_inst, operand); + try label.merges.results.append(mod.gpa, operand); + const b = try mod.requireFunctionBlock(scope, src); + return mod.addBr(b, src, label.merges.block_inst, operand); } } opt_block = block.parent; @@ -2063,7 +2370,7 @@ fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerErr const decl_name = inst.positionals.name; const zir_module = scope.namespace().cast(Scope.ZIRModule).?; const src_decl = zir_module.contents.module.findDecl(decl_name) orelse - return mod.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name}); + return mod.fail(scope, inst.base.src, "use of undeclared identifier '{s}'", .{decl_name}); const decl = try resolveCompleteZirDecl(mod, scope, src_decl.decl); @@ -2071,12 +2378,16 @@ fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerErr } fn analyzeInstSimplePtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, mutable: bool, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); const elem_type = try resolveType(mod, scope, inst.positionals.operand); const ty = try mod.simplePtrType(scope, inst.base.src, elem_type, mutable, size); return mod.constType(scope, inst.base.src, ty); } fn analyzeInstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.PtrType) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); // TODO lazy values const @"align" = if (inst.kw_args.@"align") |some| @truncate(u32, try resolveInt(mod, scope, some, Type.initTag(.u32))) diff --git a/test/compare_output.zig b/test/compare_output.zig index 46c475e04609..9dc80f202d5f 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -453,7 +453,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ _ = args_it.skip(); \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) { \\ const arg = try arg_or_err; - \\ try stdout.print("{}: {}\n", .{index, arg}); + \\ try stdout.print("{}: {s}\n", .{index, arg}); \\ } \\} , @@ -492,7 +492,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ _ = args_it.skip(); \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) { \\ const arg = try arg_or_err; - \\ try stdout.print("{}: {}\n", .{index, arg}); + \\ try stdout.print("{}: {s}\n", .{index, arg}); \\ } \\} , diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 632601b70cc8..2097ea184272 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -657,4 +657,50 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("static K&R-style no prototype function declaration (empty parameter list)", + \\#include + \\static int foo() { + \\ return 42; + \\} + \\int main() { + \\ if (foo() != 42) abort(); + \\ return 0; + \\} + , ""); + + cases.add("K&R-style static function prototype for unused function", + \\static int foo(); + \\int main() { + \\ return 0; + \\} + , ""); + + cases.add("K&R-style static function prototype + separate definition", + \\#include + \\static int foo(); + \\static int foo(int a, int b) { + \\ return a + b; + \\} + \\int main() { + \\ if (foo(40, 2) != 42) abort(); + \\ return 0; + \\} + , ""); + + cases.add("dollar sign in identifiers", + \\#include + \\#define $FOO 2 + \\#define $foo bar$ + \\#define $baz($x) ($x + $FOO) + \\int $$$(int $x$) { return $x$ + $FOO; } + \\int main() { + \\ int bar$ = 42; + \\ if ($foo != 42) abort(); + \\ if (bar$ != 42) abort(); + \\ if ($baz(bar$) != 44) abort(); + \\ if ($$$(bar$) != 44) abort(); + \\ return 0; + \\} + , ""); } diff --git a/test/src/compare_output.zig b/test/src/compare_output.zig index f78d6744e1d3..1cde470887be 100644 --- a/test/src/compare_output.zig +++ b/test/src/compare_output.zig @@ -97,7 +97,7 @@ pub const CompareOutputContext = struct { switch (case.special) { Special.Asm => { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "assemble-and-link {}", .{ + const annotated_case_name = fmt.allocPrint(self.b.allocator, "assemble-and-link {s}", .{ case.name, }) catch unreachable; if (self.test_filter) |filter| { @@ -116,7 +116,7 @@ pub const CompareOutputContext = struct { }, Special.None => { for (self.modes) |mode| { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", .{ + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ "compare-output", case.name, @tagName(mode), @@ -141,7 +141,7 @@ pub const CompareOutputContext = struct { } }, Special.RuntimeSafety => { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {}", .{case.name}) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {s}", .{case.name}) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } diff --git a/test/src/run_translated_c.zig b/test/src/run_translated_c.zig index 06990241cc84..1de329112c14 100644 --- a/test/src/run_translated_c.zig +++ b/test/src/run_translated_c.zig @@ -77,7 +77,7 @@ pub const RunTranslatedCContext = struct { pub fn addCase(self: *RunTranslatedCContext, case: *const TestCase) void { const b = self.b; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "run-translated-c {}", .{case.name}) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "run-translated-c {s}", .{case.name}) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } @@ -92,13 +92,13 @@ pub const RunTranslatedCContext = struct { .basename = case.sources.items[0].filename, }, }); - translate_c.step.name = b.fmt("{} translate-c", .{annotated_case_name}); + translate_c.step.name = b.fmt("{s} translate-c", .{annotated_case_name}); const exe = translate_c.addExecutable(); exe.setTarget(self.target); - exe.step.name = b.fmt("{} build-exe", .{annotated_case_name}); + exe.step.name = b.fmt("{s} build-exe", .{annotated_case_name}); exe.linkLibC(); const run = exe.run(); - run.step.name = b.fmt("{} run", .{annotated_case_name}); + run.step.name = b.fmt("{s} run", .{annotated_case_name}); if (!case.allow_warnings) { run.expectStdErrEqual(""); } diff --git a/test/src/translate_c.zig b/test/src/translate_c.zig index 405c5954ba5a..c43f3269269d 100644 --- a/test/src/translate_c.zig +++ b/test/src/translate_c.zig @@ -99,7 +99,7 @@ pub const TranslateCContext = struct { const b = self.b; const translate_c_cmd = "translate-c"; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {}", .{ translate_c_cmd, case.name }) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s}", .{ translate_c_cmd, case.name }) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index db6e5962912d..f82631cd9e19 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -141,5 +141,5 @@ comptime { _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); _ = @import("behavior/src.zig"); - _ = @import("behavior/translate_c_macros.zig"); + // _ = @import("behavior/translate_c_macros.zig"); } diff --git a/test/stage1/behavior/async_fn.zig b/test/stage1/behavior/async_fn.zig index eb9c3f5d07cc..16c7b14944a1 100644 --- a/test/stage1/behavior/async_fn.zig +++ b/test/stage1/behavior/async_fn.zig @@ -2,6 +2,7 @@ const std = @import("std"); const builtin = @import("builtin"); const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; +const expectEqualStrings = std.testing.expectEqualStrings; const expectError = std.testing.expectError; var global_x: i32 = 1; @@ -541,7 +542,7 @@ test "pass string literal to async function" { fn hello(msg: []const u8) void { frame = @frame(); suspend; - expectEqual(@as([]const u8, "hello"), msg); + expectEqualStrings("hello", msg); ok = true; } }; diff --git a/test/stage1/behavior/switch.zig b/test/stage1/behavior/switch.zig index 28979b8ae8f1..20ca0d314666 100644 --- a/test/stage1/behavior/switch.zig +++ b/test/stage1/behavior/switch.zig @@ -436,6 +436,26 @@ test "switch with disjoint range" { } } +test "switch variable for range and multiple prongs" { + const S = struct { + fn doTheTest() void { + var u: u8 = 16; + doTheSwitch(u); + comptime doTheSwitch(u); + var v: u8 = 42; + doTheSwitch(v); + comptime doTheSwitch(v); + } + fn doTheSwitch(q: u8) void { + switch (q) { + 0...40 => |x| expect(x == 16), + 41, 42, 43 => |x| expect(x == 42), + else => expect(false), + } + } + }; +} + var state: u32 = 0; fn poll() void { switch (state) { diff --git a/test/stage1/behavior/union.zig b/test/stage1/behavior/union.zig index d350ae369a9b..544adaf979e8 100644 --- a/test/stage1/behavior/union.zig +++ b/test/stage1/behavior/union.zig @@ -742,7 +742,7 @@ test "@unionInit on union w/ tag but no fields" { const Data = union(Type) { no_op: void, - pub fn decode(buf: []const u8) !Data { + pub fn decode(buf: []const u8) Data { return @unionInit(Data, "no_op", {}); } }; @@ -753,7 +753,7 @@ test "@unionInit on union w/ tag but no fields" { fn doTheTest() void { var data: Data = .{ .no_op = .{} }; - var o = try Data.decode(&[_]u8{}); + var o = Data.decode(&[_]u8{}); expectEqual(Type.no_op, o); } }; diff --git a/test/stage2/aarch64.zig b/test/stage2/aarch64.zig index b71362cd3d0e..7d05c60bb87b 100644 --- a/test/stage2/aarch64.zig +++ b/test/stage2/aarch64.zig @@ -155,4 +155,48 @@ pub fn addCases(ctx: *TestContext) !void { "Hello, World!\n", ); } + + { + var case = ctx.exe("exit fn taking argument", macos_aarch64); + + case.addCompareOutput( + \\export fn _start() noreturn { + \\ exit(0); + \\} + \\ + \\fn exit(ret: usize) noreturn { + \\ asm volatile ("svc #0x80" + \\ : + \\ : [number] "{x16}" (1), + \\ [arg1] "{x0}" (ret) + \\ : "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); + } + + { + var case = ctx.exe("exit fn taking argument", linux_aarch64); + + case.addCompareOutput( + \\export fn _start() noreturn { + \\ exit(0); + \\} + \\ + \\fn exit(ret: usize) noreturn { + \\ asm volatile ("svc #0" + \\ : + \\ : [number] "{x8}" (93), + \\ [arg1] "{x0}" (ret) + \\ : "memory", "cc" + \\ ); + \\ unreachable; + \\} + , + "", + ); + } } diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index a0a4587983fd..b227d6a7839b 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -67,7 +67,22 @@ pub fn addCases(ctx: *TestContext) !void { \\} , ""); } + { + var case = ctx.exeFromCompiledC("@setEvalBranchQuota", .{}); + case.addCompareOutput( + \\export fn main() i32 { + \\ @setEvalBranchQuota(1001); + \\ const y = rec(1001); + \\ return y - 1; + \\} + \\ + \\inline fn rec(n: usize) usize { + \\ if (n <= 1) return n; + \\ return rec(n - 1); + \\} + , ""); + } ctx.c("empty start function", linux_x64, \\export fn _start() noreturn { \\ unreachable; diff --git a/test/stage2/llvm_backend.zig b/test/stage2/llvm_backend.zig new file mode 100644 index 000000000000..1b753621ea4e --- /dev/null +++ b/test/stage2/llvm_backend.zig @@ -0,0 +1,30 @@ +const std = @import("std"); +const TestContext = @import("../../src/test.zig").TestContext; +const build_options = @import("build_options"); + +// These tests should work with all platforms, but we're using linux_x64 for +// now for consistency. Will be expanded eventually. +const linux_x64 = std.zig.CrossTarget{ + .cpu_arch = .x86_64, + .os_tag = .linux, +}; + +pub fn addCases(ctx: *TestContext) !void { + { + var case = ctx.exeUsingLlvmBackend("simple addition and subtraction", linux_x64); + + case.addCompareOutput( + \\fn add(a: i32, b: i32) i32 { + \\ return a + b; + \\} + \\ + \\export fn main() c_int { + \\ var a: i32 = -5; + \\ const x = add(a, 7); + \\ var y = add(2, 0); + \\ y -= x; + \\ return y; + \\} + , ""); + } +} diff --git a/test/stage2/test.zig b/test/stage2/test.zig index a8a8edd59873..61e4eb629107 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -27,11 +27,11 @@ const wasi = std.zig.CrossTarget{ }; pub fn addCases(ctx: *TestContext) !void { - try @import("zir.zig").addCases(ctx); try @import("cbe.zig").addCases(ctx); try @import("spu-ii.zig").addCases(ctx); try @import("arm.zig").addCases(ctx); try @import("aarch64.zig").addCases(ctx); + try @import("llvm_backend.zig").addCases(ctx); { var case = ctx.exe("hello world with updates", linux_x64); @@ -534,7 +534,8 @@ pub fn addCases(ctx: *TestContext) !void { ); } { - var case = ctx.exe("adding numbers at runtime", linux_x64); + var case = ctx.exe("adding numbers at runtime and comptime", linux_x64); + case.addCompareOutput( \\export fn _start() noreturn { \\ add(3, 4); @@ -558,6 +559,54 @@ pub fn addCases(ctx: *TestContext) !void { , "", ); + // comptime function call + case.addCompareOutput( + \\export fn _start() noreturn { + \\ exit(); + \\} + \\ + \\fn add(a: u32, b: u32) u32 { + \\ return a + b; + \\} + \\ + \\const x = add(3, 4); + \\ + \\fn exit() noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (x - 7) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); + // Inline function call + case.addCompareOutput( + \\export fn _start() noreturn { + \\ var x: usize = 3; + \\ const y = add(1, 2, x); + \\ exit(y - 6); + \\} + \\ + \\inline fn add(a: usize, b: usize, c: usize) usize { + \\ return a + b + c; + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); } { @@ -1547,4 +1596,102 @@ pub fn addCases(ctx: *TestContext) !void { \\} , &[_][]const u8{":2:9: error: variable of type '@Type(.Null)' must be const or comptime"}); } + + { + var case = ctx.exe("compile error in inline fn call fixed", linux_x64); + case.addError( + \\export fn _start() noreturn { + \\ var x: usize = 3; + \\ const y = add(10, 2, x); + \\ exit(y - 6); + \\} + \\ + \\inline fn add(a: usize, b: usize, c: usize) usize { + \\ if (a == 10) @compileError("bad"); + \\ return a + b + c; + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , &[_][]const u8{":8:18: error: bad"}); + + case.addCompareOutput( + \\export fn _start() noreturn { + \\ var x: usize = 3; + \\ const y = add(1, 2, x); + \\ exit(y - 6); + \\} + \\ + \\inline fn add(a: usize, b: usize, c: usize) usize { + \\ if (a == 10) @compileError("bad"); + \\ return a + b + c; + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); + } + { + var case = ctx.exe("recursive inline function", linux_x64); + case.addCompareOutput( + \\export fn _start() noreturn { + \\ const y = fibonacci(7); + \\ exit(y - 21); + \\} + \\ + \\inline fn fibonacci(n: usize) usize { + \\ if (n <= 2) return n; + \\ return fibonacci(n - 2) + fibonacci(n - 1); + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); + case.addError( + \\export fn _start() noreturn { + \\ const y = fibonacci(999); + \\ exit(y - 21); + \\} + \\ + \\inline fn fibonacci(n: usize) usize { + \\ if (n <= 2) return n; + \\ return fibonacci(n - 2) + fibonacci(n - 1); + \\} + \\ + \\fn exit(code: usize) noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (code) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , &[_][]const u8{":8:10: error: evaluation exceeded 1000 backwards branches"}); + } } diff --git a/test/stage2/zir.zig b/test/stage2/zir.zig deleted file mode 100644 index da4038e79219..000000000000 --- a/test/stage2/zir.zig +++ /dev/null @@ -1,316 +0,0 @@ -const std = @import("std"); -const TestContext = @import("../../src/test.zig").TestContext; -// self-hosted does not yet support PE executable files / COFF object files -// or mach-o files. So we do the ZIR transform test cases cross compiling for -// x86_64-linux. -const linux_x64 = std.zig.CrossTarget{ - .cpu_arch = .x86_64, - .os_tag = .linux, -}; - -pub fn addCases(ctx: *TestContext) !void { - ctx.transformZIR("referencing decls which appear later in the file", linux_x64, - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\ - \\@9 = str("entry") - \\@11 = export(@9, "entry") - \\ - \\@entry = fn(@fnty, { - \\ %11 = returnvoid() - \\}) - , - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\@9 = declref("9__anon_0") - \\@9__anon_0 = str("entry") - \\@unnamed$4 = str("entry") - \\@unnamed$5 = export(@unnamed$4, "entry") - \\@11 = primitive(void_value) - \\@unnamed$7 = fntype([], @void, cc=C) - \\@entry = fn(@unnamed$7, { - \\ %0 = returnvoid() ; deaths=0b1000000000000000 - \\}) - \\ - ); - ctx.transformZIR("elemptr, add, cmp, condbr, return, breakpoint", linux_x64, - \\@void = primitive(void) - \\@usize = primitive(usize) - \\@fnty = fntype([], @void, cc=C) - \\@0 = int(0) - \\@1 = int(1) - \\@2 = int(2) - \\@3 = int(3) - \\ - \\@entry = fn(@fnty, { - \\ %a = str("\x32\x08\x01\x0a") - \\ %a_ref = ref(%a) - \\ %eptr0 = elemptr(%a_ref, @0) - \\ %eptr1 = elemptr(%a_ref, @1) - \\ %eptr2 = elemptr(%a_ref, @2) - \\ %eptr3 = elemptr(%a_ref, @3) - \\ %v0 = deref(%eptr0) - \\ %v1 = deref(%eptr1) - \\ %v2 = deref(%eptr2) - \\ %v3 = deref(%eptr3) - \\ %x0 = add(%v0, %v1) - \\ %x1 = add(%v2, %v3) - \\ %result = add(%x0, %x1) - \\ - \\ %expected = int(69) - \\ %ok = cmp_eq(%result, %expected) - \\ %10 = condbr(%ok, { - \\ %11 = returnvoid() - \\ }, { - \\ %12 = breakpoint() - \\ }) - \\}) - \\ - \\@9 = str("entry") - \\@11 = export(@9, "entry") - , - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\@0 = int(0) - \\@1 = int(1) - \\@2 = int(2) - \\@3 = int(3) - \\@unnamed$6 = fntype([], @void, cc=C) - \\@entry = fn(@unnamed$6, { - \\ %0 = returnvoid() ; deaths=0b1000000000000000 - \\}) - \\@entry__anon_1 = str("2\x08\x01\n") - \\@9 = declref("9__anon_0") - \\@9__anon_0 = str("entry") - \\@unnamed$11 = str("entry") - \\@unnamed$12 = export(@unnamed$11, "entry") - \\@11 = primitive(void_value) - \\ - ); - - { - var case = ctx.objZIR("reference cycle with compile error in the cycle", linux_x64); - case.addTransform( - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\ - \\@9 = str("entry") - \\@11 = export(@9, "entry") - \\ - \\@entry = fn(@fnty, { - \\ %0 = call(@a, []) - \\ %1 = returnvoid() - \\}) - \\ - \\@a = fn(@fnty, { - \\ %0 = call(@b, []) - \\ %1 = returnvoid() - \\}) - \\ - \\@b = fn(@fnty, { - \\ %0 = call(@a, []) - \\ %1 = returnvoid() - \\}) - , - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\@9 = declref("9__anon_0") - \\@9__anon_0 = str("entry") - \\@unnamed$4 = str("entry") - \\@unnamed$5 = export(@unnamed$4, "entry") - \\@11 = primitive(void_value) - \\@unnamed$7 = fntype([], @void, cc=C) - \\@entry = fn(@unnamed$7, { - \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001 - \\ %1 = returnvoid() ; deaths=0b1000000000000000 - \\}) - \\@unnamed$9 = fntype([], @void, cc=C) - \\@a = fn(@unnamed$9, { - \\ %0 = call(@b, [], modifier=auto) ; deaths=0b1000000000000001 - \\ %1 = returnvoid() ; deaths=0b1000000000000000 - \\}) - \\@unnamed$11 = fntype([], @void, cc=C) - \\@b = fn(@unnamed$11, { - \\ %0 = call(@a, [], modifier=auto) ; deaths=0b1000000000000001 - \\ %1 = returnvoid() ; deaths=0b1000000000000000 - \\}) - \\ - ); - // Now we introduce a compile error - case.addError( - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\ - \\@9 = str("entry") - \\@11 = export(@9, "entry") - \\ - \\@entry = fn(@fnty, { - \\ %0 = call(@a, []) - \\ %1 = returnvoid() - \\}) - \\ - \\@a = fn(@fnty, { - \\ %0 = call(@c, []) - \\ %1 = returnvoid() - \\}) - \\ - \\@b = str("message") - \\ - \\@c = fn(@fnty, { - \\ %9 = compileerror(@b) - \\ %0 = call(@a, []) - \\ %1 = returnvoid() - \\}) - , - &[_][]const u8{ - ":20:21: error: message", - }, - ); - // Now we remove the call to `a`. `a` and `b` form a cycle, but no entry points are - // referencing either of them. This tests that the cycle is detected, and the error - // goes away. - case.addTransform( - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\ - \\@9 = str("entry") - \\@11 = export(@9, "entry") - \\ - \\@entry = fn(@fnty, { - \\ %0 = returnvoid() - \\}) - \\ - \\@a = fn(@fnty, { - \\ %0 = call(@c, []) - \\ %1 = returnvoid() - \\}) - \\ - \\@b = str("message") - \\ - \\@c = fn(@fnty, { - \\ %9 = compileerror(@b) - \\ %0 = call(@a, []) - \\ %1 = returnvoid() - \\}) - , - \\@void = primitive(void) - \\@fnty = fntype([], @void, cc=C) - \\@9 = declref("9__anon_3") - \\@9__anon_3 = str("entry") - \\@unnamed$4 = str("entry") - \\@unnamed$5 = export(@unnamed$4, "entry") - \\@11 = primitive(void_value) - \\@unnamed$7 = fntype([], @void, cc=C) - \\@entry = fn(@unnamed$7, { - \\ %0 = returnvoid() ; deaths=0b1000000000000000 - \\}) - \\ - ); - } - - if (std.Target.current.os.tag != .linux or - std.Target.current.cpu.arch != .x86_64) - { - // TODO implement self-hosted PE (.exe file) linking - // TODO implement more ZIR so we don't depend on x86_64-linux - return; - } - - ctx.compareOutputZIR("hello world ZIR", - \\@noreturn = primitive(noreturn) - \\@void = primitive(void) - \\@usize = primitive(usize) - \\@0 = int(0) - \\@1 = int(1) - \\@2 = int(2) - \\@3 = int(3) - \\ - \\@msg = str("Hello, world!\n") - \\ - \\@start_fnty = fntype([], @noreturn, cc=Naked) - \\@start = fn(@start_fnty, { - \\ %SYS_exit_group = int(231) - \\ %exit_code = as(@usize, @0) - \\ - \\ %syscall = str("syscall") - \\ %sysoutreg = str("={rax}") - \\ %rax = str("{rax}") - \\ %rdi = str("{rdi}") - \\ %rcx = str("rcx") - \\ %rdx = str("{rdx}") - \\ %rsi = str("{rsi}") - \\ %r11 = str("r11") - \\ %memory = str("memory") - \\ - \\ %SYS_write = as(@usize, @1) - \\ %STDOUT_FILENO = as(@usize, @1) - \\ - \\ %msg_addr = ptrtoint(@msg) - \\ - \\ %len_name = str("len") - \\ %msg_len_ptr = fieldptr(@msg, %len_name) - \\ %msg_len = deref(%msg_len_ptr) - \\ %rc_write = asm(%syscall, @usize, - \\ volatile=1, - \\ output=%sysoutreg, - \\ inputs=[%rax, %rdi, %rsi, %rdx], - \\ clobbers=[%rcx, %r11, %memory], - \\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len]) - \\ - \\ %rc_exit = asm(%syscall, @usize, - \\ volatile=1, - \\ output=%sysoutreg, - \\ inputs=[%rax, %rdi], - \\ clobbers=[%rcx, %r11, %memory], - \\ args=[%SYS_exit_group, %exit_code]) - \\ - \\ %99 = unreachable() - \\}); - \\ - \\@9 = str("_start") - \\@11 = export(@9, "start") - , - \\Hello, world! - \\ - ); - - ctx.compareOutputZIR("function call with no args no return value", - \\@noreturn = primitive(noreturn) - \\@void = primitive(void) - \\@usize = primitive(usize) - \\@0 = int(0) - \\@1 = int(1) - \\@2 = int(2) - \\@3 = int(3) - \\ - \\@exit0_fnty = fntype([], @noreturn) - \\@exit0 = fn(@exit0_fnty, { - \\ %SYS_exit_group = int(231) - \\ %exit_code = as(@usize, @0) - \\ - \\ %syscall = str("syscall") - \\ %sysoutreg = str("={rax}") - \\ %rax = str("{rax}") - \\ %rdi = str("{rdi}") - \\ %rcx = str("rcx") - \\ %r11 = str("r11") - \\ %memory = str("memory") - \\ - \\ %rc = asm(%syscall, @usize, - \\ volatile=1, - \\ output=%sysoutreg, - \\ inputs=[%rax, %rdi], - \\ clobbers=[%rcx, %r11, %memory], - \\ args=[%SYS_exit_group, %exit_code]) - \\ - \\ %99 = unreachable() - \\}); - \\ - \\@start_fnty = fntype([], @noreturn, cc=Naked) - \\@start = fn(@start_fnty, { - \\ %0 = call(@exit0, []) - \\}) - \\@9 = str("_start") - \\@11 = export(@9, "start") - , ""); -} diff --git a/test/tests.zig b/test/tests.zig index 250c1cc4eacd..5ee381e5c23e 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -482,7 +482,7 @@ pub fn addPkgTests( is_wasmtime_enabled: bool, glibc_dir: ?[]const u8, ) *build.Step { - const step = b.step(b.fmt("test-{}", .{name}), desc); + const step = b.step(b.fmt("test-{s}", .{name}), desc); for (test_targets) |test_target| { if (skip_non_native and !test_target.target.isNative()) @@ -523,7 +523,7 @@ pub fn addPkgTests( const these_tests = b.addTest(root_src); const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; - these_tests.setNamePrefix(b.fmt("{}-{}-{}-{}-{} ", .{ + these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s} ", .{ name, triple_prefix, @tagName(test_target.mode), @@ -570,7 +570,7 @@ pub const StackTracesContext = struct { const expect_for_mode = expect[@enumToInt(mode)]; if (expect_for_mode.len == 0) continue; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", .{ + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ "stack-trace", name, @tagName(mode), @@ -637,7 +637,7 @@ pub const StackTracesContext = struct { defer args.deinit(); args.append(full_exe_path) catch unreachable; - warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); + warn("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name }); const child = std.ChildProcess.init(args.items, b.allocator) catch unreachable; defer child.deinit(); @@ -650,7 +650,7 @@ pub const StackTracesContext = struct { if (b.verbose) { printInvocation(args.items); } - child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) }); + child.spawn() catch |err| debug.panic("Unable to spawn {s}: {s}\n", .{ full_exe_path, @errorName(err) }); const stdout = child.stdout.?.inStream().readAllAlloc(b.allocator, max_stdout_size) catch unreachable; defer b.allocator.free(stdout); @@ -659,14 +659,14 @@ pub const StackTracesContext = struct { var stderr = stderrFull; const term = child.wait() catch |err| { - debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) }); + debug.panic("Unable to spawn {s}: {s}\n", .{ full_exe_path, @errorName(err) }); }; switch (term) { .Exited => |code| { const expect_code: u32 = 1; if (code != expect_code) { - warn("Process {} exited with error code {} but expected code {}\n", .{ + warn("Process {s} exited with error code {d} but expected code {d}\n", .{ full_exe_path, code, expect_code, @@ -676,17 +676,17 @@ pub const StackTracesContext = struct { } }, .Signal => |signum| { - warn("Process {} terminated on signal {}\n", .{ full_exe_path, signum }); + warn("Process {s} terminated on signal {d}\n", .{ full_exe_path, signum }); printInvocation(args.items); return error.TestFailed; }, .Stopped => |signum| { - warn("Process {} stopped on signal {}\n", .{ full_exe_path, signum }); + warn("Process {s} stopped on signal {d}\n", .{ full_exe_path, signum }); printInvocation(args.items); return error.TestFailed; }, .Unknown => |code| { - warn("Process {} terminated unexpectedly with error code {}\n", .{ full_exe_path, code }); + warn("Process {s} terminated unexpectedly with error code {d}\n", .{ full_exe_path, code }); printInvocation(args.items); return error.TestFailed; }, @@ -732,9 +732,9 @@ pub const StackTracesContext = struct { warn( \\ \\========= Expected this output: ========= - \\{} + \\{s} \\================================================ - \\{} + \\{s} \\ , .{ self.expect_output, got }); return error.TestFailed; @@ -856,7 +856,7 @@ pub const CompileErrorContext = struct { zig_args.append("-O") catch unreachable; zig_args.append(@tagName(self.build_mode)) catch unreachable; - warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); + warn("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name }); if (b.verbose) { printInvocation(zig_args.items); @@ -870,7 +870,7 @@ pub const CompileErrorContext = struct { child.stdout_behavior = .Pipe; child.stderr_behavior = .Pipe; - child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ zig_args.items[0], @errorName(err) }); + child.spawn() catch |err| debug.panic("Unable to spawn {s}: {s}\n", .{ zig_args.items[0], @errorName(err) }); var stdout_buf = ArrayList(u8).init(b.allocator); var stderr_buf = ArrayList(u8).init(b.allocator); @@ -879,7 +879,7 @@ pub const CompileErrorContext = struct { child.stderr.?.inStream().readAllArrayList(&stderr_buf, max_stdout_size) catch unreachable; const term = child.wait() catch |err| { - debug.panic("Unable to spawn {}: {}\n", .{ zig_args.items[0], @errorName(err) }); + debug.panic("Unable to spawn {s}: {s}\n", .{ zig_args.items[0], @errorName(err) }); }; switch (term) { .Exited => |code| { @@ -889,7 +889,7 @@ pub const CompileErrorContext = struct { } }, else => { - warn("Process {} terminated unexpectedly\n", .{b.zig_exe}); + warn("Process {s} terminated unexpectedly\n", .{b.zig_exe}); printInvocation(zig_args.items); return error.TestFailed; }, @@ -903,7 +903,7 @@ pub const CompileErrorContext = struct { \\ \\Expected empty stdout, instead found: \\================================================ - \\{} + \\{s} \\================================================ \\ , .{stdout}); @@ -926,7 +926,7 @@ pub const CompileErrorContext = struct { if (!ok) { warn("\n======== Expected these compile errors: ========\n", .{}); for (self.case.expected_errors.items) |expected| { - warn("{}\n", .{expected}); + warn("{s}\n", .{expected}); } } } else { @@ -935,7 +935,7 @@ pub const CompileErrorContext = struct { warn( \\ \\=========== Expected compile error: ============ - \\{} + \\{s} \\ , .{expected}); ok = false; @@ -947,7 +947,7 @@ pub const CompileErrorContext = struct { if (!ok) { warn( \\================= Full output: ================= - \\{} + \\{s} \\ , .{stderr}); return error.TestFailed; @@ -1023,7 +1023,7 @@ pub const CompileErrorContext = struct { pub fn addCase(self: *CompileErrorContext, case: *const TestCase) void { const b = self.b; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "compile-error {}", .{ + const annotated_case_name = fmt.allocPrint(self.b.allocator, "compile-error {s}", .{ case.name, }) catch unreachable; if (self.test_filter) |filter| { @@ -1058,7 +1058,7 @@ pub const StandaloneContext = struct { pub fn addBuildFile(self: *StandaloneContext, build_file: []const u8) void { const b = self.b; - const annotated_case_name = b.fmt("build {} (Debug)", .{build_file}); + const annotated_case_name = b.fmt("build {s} (Debug)", .{build_file}); if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } @@ -1079,7 +1079,7 @@ pub const StandaloneContext = struct { const run_cmd = b.addSystemCommand(zig_args.items); - const log_step = b.addLog("PASS {}\n", .{annotated_case_name}); + const log_step = b.addLog("PASS {s}\n", .{annotated_case_name}); log_step.step.dependOn(&run_cmd.step); self.step.dependOn(&log_step.step); @@ -1089,7 +1089,7 @@ pub const StandaloneContext = struct { const b = self.b; for (self.modes) |mode| { - const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})", .{ + const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{ root_src, @tagName(mode), }) catch unreachable; @@ -1103,7 +1103,7 @@ pub const StandaloneContext = struct { exe.linkSystemLibrary("c"); } - const log_step = b.addLog("PASS {}\n", .{annotated_case_name}); + const log_step = b.addLog("PASS {s}\n", .{annotated_case_name}); log_step.step.dependOn(&exe.step); self.step.dependOn(&log_step.step); @@ -1172,7 +1172,7 @@ pub const GenHContext = struct { const self = @fieldParentPtr(GenHCmpOutputStep, "step", step); const b = self.context.b; - warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); + warn("Test {d}/{d} {s}...", .{ self.test_index + 1, self.context.test_index, self.name }); const full_h_path = self.obj.getOutputHPath(); const actual_h = try io.readFileAlloc(b.allocator, full_h_path); @@ -1182,9 +1182,9 @@ pub const GenHContext = struct { warn( \\ \\========= Expected this output: ================ - \\{} + \\{s} \\========= But found: =========================== - \\{} + \\{s} \\ , .{ expected_line, actual_h }); return error.TestFailed; @@ -1196,7 +1196,7 @@ pub const GenHContext = struct { fn printInvocation(args: []const []const u8) void { for (args) |arg| { - warn("{} ", .{arg}); + warn("{s} ", .{arg}); } warn("\n", .{}); } @@ -1232,7 +1232,7 @@ pub const GenHContext = struct { const b = self.b; const mode = builtin.Mode.Debug; - const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {} ({})", .{ case.name, @tagName(mode) }) catch unreachable; + const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(mode) }) catch unreachable; if (self.test_filter) |filter| { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } @@ -1253,7 +1253,7 @@ pub const GenHContext = struct { fn printInvocation(args: []const []const u8) void { for (args) |arg| { - warn("{} ", .{arg}); + warn("{s} ", .{arg}); } warn("\n", .{}); } diff --git a/test/translate_c.zig b/test/translate_c.zig index 60f3d0e2c5da..6ebe27c0a795 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1375,11 +1375,19 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\void b(void) {} \\void c(); \\void d(void); + \\static void e() {} + \\static void f(void) {} + \\static void g(); + \\static void h(void); , &[_][]const u8{ \\pub export fn a() void {} \\pub export fn b() void {} \\pub extern fn c(...) void; \\pub extern fn d() void; + \\pub fn e() callconv(.C) void {} + \\pub fn f() callconv(.C) void {} + \\pub extern fn g() void; + \\pub extern fn h() void; }); cases.add("variable declarations", @@ -2938,7 +2946,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub fn a() callconv(.C) void {} \\pub fn b() callconv(.C) void {} \\pub export fn c() void {} - \\pub fn foo(...) callconv(.C) void {} + \\pub fn foo() callconv(.C) void {} }); cases.add("casting away const and volatile",